<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Grant Winney</title><link>https://grantwinney.com/</link><description>Recent content on Grant Winney</description><generator>Hugo -- gohugo.io</generator><language>en</language><copyright>© 2026 Grant Winney</copyright><lastBuildDate>Sat, 11 Jul 2026 18:12:00 +0000</lastBuildDate><atom:link href="https://grantwinney.com/index.xml" rel="self" type="application/rss+xml"/><item><title>Yay, I don't have to trash my PC until next year</title><link>https://grantwinney.com/windows-10-extended-esu/</link><pubDate>Sat, 11 Jul 2026 18:12:00 +0000</pubDate><guid>https://grantwinney.com/windows-10-extended-esu/</guid><description>Microsoft extended their extension to offer security updates for Windows 10 for another year. Unexpected, but nice.</description><content:encoded><![CDATA[<p>Microsoft sent an email the other day, letting all of us Windows 10 holdouts know that they&rsquo;ll be providing security updates for an extra year. Cool, so I don&rsquo;t have to toss my PC in the trash until <em>next</em> year. Sigh.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/windows-10-extended-esu/win-10-esu-protection-extended.webp"
    width="618"
      height="321"></figure>

<h2 class="relative group">One-Two Punch
    <div id="one-two-punch" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#one-two-punch" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>For anyone needing a recap, Microsoft called it quits on Windows 10 last Oct. They announced they&rsquo;re done with features, bug fixes, and even security updates&hellip; sort of. They gave us the option to pay $30 (or buy a OneDrive subscription or redeem 1,000 magic MS points) for <a href="https://www.microsoft.com/en-us/windows/extended-security-updates"  target="_blank" rel="noreferrer">one additional year of updates</a>. Companies could get updates for a few years, but the cost increases each year. They <em>really</em> want us off it.</p>
<p>Why would anyone want to pay though, instead of just upgrading to Windows 11? Especially when it was a free upgrade for quite awhile? Because we had no choice.</p>
<p>Microsoft drew some lines in the sand when they released Windows 11, requiring certain minimum hardware. I started with Windows 95, and upgrading has always meant a little more free space on your drive, a little more RAM, etc. That&rsquo;s normal for any software upgrade. But for the first time, they&rsquo;ve set some unusually stringent hardware requirements.</p>
<p>To install Win 11, a device needs a 64-bit system, a minimum level of processor, and a <a href="https://support.microsoft.com/en-us/windows/security/device-security/what-s-a-trusted-platform-module-tpm"  target="_blank" rel="noreferrer">TPM chip</a>. In my case, the PC has a space for the TPM on the motherboard, so I <em>could</em> get a chip, but it wouldn&rsquo;t matter anyway because the CPU isn&rsquo;t on their <a href="https://www.eatyourbytes.com/list-of-windows-11-supported-processors/"  target="_blank" rel="noreferrer">list of supported processors</a>.</p>
<p>And so a lot of people are stuck, unable to continue with Windows 10 but unable to upgrade to Windows 11. That&rsquo;s what the email above is about.. giving us more time before the inevitable. Because unless they change something about the min reqs, it&rsquo;ll come down to a choice of:</p>
<ul>
<li>upgrading devices (expensive, if they even can be),</li>
<li>trashing devices, and replacing with something newer (more expensive, esp during the RAMpocolypse),</li>
<li>installing and learning a different OS (not a trivial thing, and not always practical)</li>
</ul>

<h2 class="relative group">Extended Extension?
    <div id="extended-extension" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#extended-extension" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>So why are they extending their own extension another year, just a few months before they could wash their hands of Windows 10, at least for individual consumers? Good question. Thanks for asking it. I have no idea.. maybe it&rsquo;s just out of the kindness of their collective hearts?</p>
<p>Looking at the data from StatCounter though, over a quarter of all Windows users are <em>still</em> on Windows 10! That&rsquo;s a lot, considering it&rsquo;s been 9 months since they flagged it EOL.</p>
<div id="desktop-windows_version-ww-monthly-202506-202606" height="350" width="600"></div><p style="font-decoration:italic">Source: <a href="https://gs.statcounter.com/windows-version-market-share/desktop/worldwide/" style="font-weight:normal">StatCounter Global Stats - Windows Version Market Share</a></p><script type="text/javascript" src="https://www.statcounter.com/js/fusioncharts.js"></script><script type="text/javascript" src="https://gs.statcounter.com/chart.php?desktop-windows_version-ww-monthly-202506-202606&chartWidth=600"></script>
<p>I wonder if they&rsquo;re worried about just how many of those users (millions? tens of millions?) will <em>actually</em> upgrade their computer (at a time when the AI craze has driven memory and disks prices wayyy up) and <em>then</em> buy a Windows 11 license on top of it? I bet more than a few will just stick with (or move to) a Chromebook, iPad or other tablet, etc.</p>
<p>Combine that with smaller businesses that can&rsquo;t afford to upgrade/replace all their old computers just to upgrade to Win 11, and MS may stand to lose a lot of revenue (and good will), now and in the future.</p>
<p>Or maybe they&rsquo;re concerned that if they pull the plug in Oct, a lot of people will just.. do nothing. Many of them will be fine for quite awhile, while some others will get hit with malware and everything else that comes from no more security updates. And when those users recover, they may just say screw it to Windows and go with something else.</p>
<p>Or maybe they&rsquo;re worried about litigation, or major consumer pushback, or politicians getting involved come the October deadline? And keeping the pipeline open that pushes security updates is easier than facing something they see coming? Who knows.</p>
<p>As for me, I still don&rsquo;t know what I&rsquo;ll do. I need the PC because our family shares it for schoolwork, gaming, taxes, etc. It works for us, and it&rsquo;s cheaper and more maintainable than having multiple machines. I can&rsquo;t abandon it because the software we use runs on Windows and requires the power of a desktop. I&rsquo;m hoping I can upgrade what needs upgrading, and keep the rest (the box, cooling system, graphics card, etc).</p>
<p>We&rsquo;ll see.</p>
<p>What about you? If you&rsquo;re in the same boat, what will you do?</p>
]]></content:encoded><media:content url="https://grantwinney.com/windows-10-extended-esu/feature.webp" medium="image" type="image/webp"/></item><item><title>Bringing old JS games back to life, part 2</title><link>https://grantwinney.com/old-js-games-part2/</link><pubDate>Thu, 04 Jun 2026 14:14:00 +0000</pubDate><guid>https://grantwinney.com/old-js-games-part2/</guid><description>After resurrecting a 25 year old JS maze game, I decided to run a few more through Copilot and see what could be learned in the process.</description><content:encoded><![CDATA[<p>A couple weeks ago, I stumbled on an <a href="/old-js-maze-game" >old JS game</a> that lets you navigate a 3D maze created in ASCII characters, but being that it was over 25 years old and hosted on a long gone site, it didn&rsquo;t work very well, lol. With the help of Copilot, I brought it back to life.</p>
<p>I had some fun doing that, so I decided to check out <a href="https://web.archive.org/web/20000815211242/http://javascript.internet.com/games/"  target="_blank" rel="noreferrer">a few more archived games</a> to see what Copilot could make of them again. It did surprisingly well&hellip;</p>

<h2 class="relative group">Battleship
    <div id="battleship" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#battleship" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>First up, a <a href="https://web.archive.org/web/20000815214940/http://javascript.internet.com/games/battleship.html"  target="_blank" rel="noreferrer">battleship game</a> courtesy of Jason Hotchkiss, who shared it on Dec 15, 1999. It works as-is in chromium-based browsers, which is kind of amazing. It wasn&rsquo;t using anything that modern browsers prohibit, but there&rsquo;s plenty that&rsquo;s been deprecated.</p>
<div class="game">
  <div class="boards">
    <div class="board">
      <div class="heading">COMPUTER'S FLEET</div>
      <div id="computer-grid" class="ship_grid"></div>
    </div>
    <div class="board">
      <div class="heading">PLAYER'S FLEET</div>
      <div id="player-grid" class="ship_grid"></div>
    </div>
  </div>
  <div id="status"></div>
  <div id="message"></div>
  <div><button id="newgame">New Game</button></div>
</div>

<h3 class="relative group">Document.write
    <div id="documentwrite" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#documentwrite" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The biggest issue was the use of <code>document.write</code> to update the DOM, which almost all of these games did, and which <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/write"  target="_blank" rel="noreferrer">MDN strongly discourages</a> nowadays. Like using <code>eval</code> or unparameterized SQL queries, it&rsquo;s a potential security hole. Imagine a commenting system that uses <code>document.write</code>, and then someone posts a comment containing javascript code that&rsquo;s naively stored as-is and then loaded (unseen) for every future visitor to the page. 😬</p>
<p>The way the game used it was pretty harmless since it didn&rsquo;t involve user input. It created an <code>img</code> for each cell in a 16x16 grid, made them clickable by nesting each in an <code>a</code> element, and then wrote them to the DOM. Copilot replaced it with calls to <code>document.createElement</code> and <code>appendChild</code>, and used <code>addEventListener</code> instead of hyperlinks.</p>

<h3 class="relative group">Classes and modules
    <div id="classes-and-modules" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#classes-and-modules" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Copilot did some nice cleanup too, moving arrays of data representing ships, ship types, etc into constants. It also moved most of the code into a &ldquo;Battleship&rdquo; class, moving variable initialization into the constructor.</p>
<p>It&rsquo;s nice to see things can be encapsulated this way, so every variable isn&rsquo;t globally defined. Plus the layout looks a lot more familiar to this C# dev.</p>

<h3 class="relative group">BR tag vs CSS grids
    <div id="br-tag-vs-css-grids" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#br-tag-vs-css-grids" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The legacy code created each &ldquo;board&rdquo; (for the player and computer) by document.writing 16 <code>img</code> elements, then writing a <code>&lt;BR&gt;</code> tag, rinse and repeat for 16 rows. Copilot refactored it to create and add 256 <code>img</code> elements to each grid, using CSS to wrap them into the correct shape. Nice.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="nt">grid-template-columns</span><span class="o">:</span> <span class="nt">repeat</span><span class="o">(</span><span class="nt">16</span><span class="o">,</span> <span class="nt">16px</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="nt">grid-auto-rows</span><span class="o">:</span> <span class="nt">16px</span><span class="o">;</span></span></span></code></pre></div></div>
<p>Check out the code here: <a href="https://github.com/grantwinney/BlogCodeSamples/tree/master/Languages/JavaScript/LegacyGames/LegacyJSBattleship"  target="_blank" rel="noreferrer">LegacyJSBattleship · grantwinney/BlogCodeSamples</a></p>
<hr>

<h2 class="relative group">Breakout
    <div id="breakout" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#breakout" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Next on our tour of ancient JS games, we have <a href="https://web.archive.org/web/20000815214951/http://javascript.internet.com/games/break-out.html"  target="_blank" rel="noreferrer">Breakout</a>, uploaded by Nick Young in April 2000. Breakout is a much simpler predecessor of Arkanoid that <a href="https://www.youtube.com/watch?v=AMUv8KvVt08"  target="_blank" rel="noreferrer">came out 50 years ago</a>. No powerups or anything special here.. it&rsquo;s just you, a ball, and 40 blocks. Ah, those were simpler times.</p>
<p><canvas id="breakoutgame" width="440" height="320" style="border:1px solid"></canvas></p>
<p>Unlike the Battleship one that worked well without any changes, this one didn&rsquo;t work at all. After a little playing around, it&rsquo;s because it used proprietary IE elements. Replacing all the <code>.posTop</code> and <code>.posLeft</code> properties with <code>.top</code> and <code>.left</code> gets things moving. Oddly, in some places he used the non-proprietary names as well. Ah, those were complicated times.</p>
<p>So let&rsquo;s see what Copilot did with this one.</p>

<h3 class="relative group">Canvas
    <div id="canvas" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#canvas" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The original script had 50 lines using <code>document.write</code> to position <em>40 separate tables</em> all using <code>position:absolute</code>, along with some messages and buttons and various text fields. Yikes.</p>
<p>Copilot replaced it with the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API"  target="_blank" rel="noreferrer">Canvas API</a>, so it could loop through the same code 40 times to draw the rectangles, along with the ball, paddle, timer, and whatever else it needed. The canvas has a <em>lot</em> of flexibility, so Copilot moved everything inside it, eliminating the need for buttons and text fields that lived outside the playing area.</p>

<h3 class="relative group">Elapsed Time
    <div id="elapsed-time" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#elapsed-time" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The original code used <code>setTimeout</code> to make the main loop of the program call itself every 100 ms, or 10x times a second. On each call, it incremented a timer variable, so that it also went up in value 10 per second. Then some other logic divided the timer value by 10 and displayed it. The end result was that the onscreen gameplay time looked like it was just a clock, increasing once per second, but it was a little more complicated and less reliable.</p>
<p>Copilot&rsquo;s new code uses Canvas and calls <a href="https://developer.mozilla.org/en-US/docs/Web/API/DedicatedWorkerGlobalScope/requestAnimationFrame"  target="_blank" rel="noreferrer">requestAnimationFrame</a>, which means the main loop is now executing 60x a second, the variable increments 60 a second, and the gameplay timer increases 6 every second. In other words, way too fast. To be fair, the original code had a &ldquo;fast&rdquo; mode that reduced the 100ms call to 10ms, and in that case the timer would increase 10 every second, so it wasn&rsquo;t great either.</p>
<p>After some nudging, Copilot fixed it by using the timestamp that <code>requestAnimationFrame</code> sends to whatever function you pass it. It saved the first timestamp as the &ldquo;game start&rdquo; time, computing a delta for each subsequent call to the main loop, adding that delta to the original time and doing some quick math to get the number of seconds. Neat. And completely overengineered.</p>
<p>My solution was to store <code>Date.now()</code> when a new game starts, and then just compare it to the current time each time the canvas updates, replacing its 30 line suggestion with just 2 lines:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="k">this</span><span class="p">.</span><span class="nx">startTime</span> <span class="o">=</span> <span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="sb">`Time: </span><span class="si">${</span><span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">((</span><span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">()</span> <span class="o">-</span> <span class="k">this</span><span class="p">.</span><span class="nx">startTime</span><span class="p">)</span> <span class="o">/</span> <span class="mi">1000</span><span class="p">)</span><span class="si">}</span><span class="sb">`</span></span></span></code></pre></div></div>
<p>It&rsquo;s a good reminder that although these LLM tools are good at finding <em>a</em> solution, it&rsquo;s not always the <em>best</em> solution!</p>
<p>Check out the code here: <a href="https://github.com/grantwinney/BlogCodeSamples/tree/master/Languages/JavaScript/LegacyGames/LegacyJSBreakout"  target="_blank" rel="noreferrer">LegacyJSBreakout · grantwinney/BlogCodeSamples</a></p>
<hr>

<h2 class="relative group">Concentration
    <div id="concentration" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#concentration" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Let&rsquo;s check out one more, a simple matching game called <a href="https://web.archive.org/web/20000711003541/http://javascript.internet.com/games/concentration.html"  target="_blank" rel="noreferrer">Concentration</a>, uploaded by Brian Gosselin in June 2000.</p>
<div id="concroot"><div id="board"></div><button type="button" value="" id="conctimer" >START</button><div id="boardmsg"></div></div>
<p>Nothing all that new from Copilot. It replaced all the <code>document.write</code> calls again, and replaced some of the same stuff as the other games, so I&rsquo;ll focus on something else interesting I noticed.</p>

<h3 class="relative group">Accessing elements directly by name??
    <div id="accessing-elements-directly-by-name" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#accessing-elements-directly-by-name" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The original code assigned a <code>name</code> to a few elements, like the <code>form</code> and button, <a href="https://www.w3docs.com/snippets/html/what-is-the-difference-between-the-id-and-name-attributes.html#the-name-attribute"  target="_blank" rel="noreferrer">which is still valid</a>, but also to <code>img</code> elements it created, which is <em>not</em> valid. That&rsquo;s not surprising, it&rsquo;s old code right? I&rsquo;m not great with today&rsquo;s JS, let alone what passed for valid 25 years ago.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nx">form</span> <span class="nx">name</span><span class="o">=</span><span class="s2">&#34;f&#34;</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">document</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="s1">&#39;&lt;img src=&#34;image0.gif&#34; name=&#34;img&#39;</span><span class="o">+</span><span class="p">((</span><span class="mi">6</span><span class="o">*</span><span class="nx">r</span><span class="p">)</span><span class="o">+</span><span class="nx">c</span><span class="p">)</span><span class="o">+</span><span class="s1">&#39;&#34; border=&#34;0&#34;&gt;&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nx">input</span> <span class="nx">type</span><span class="o">=</span><span class="s2">&#34;button&#34;</span> <span class="nx">value</span><span class="o">=</span><span class="s2">&#34;         &#34;</span> <span class="nx">name</span><span class="o">=</span><span class="s2">&#34;b&#34;</span> <span class="nx">onClick</span><span class="o">=</span><span class="s2">&#34;init()&#34;</span><span class="o">&gt;</span></span></span></code></pre></div></div>
<p>But there&rsquo;s also code that accesses the elements directly by their name, which I had no idea was ever possible. Interesting. Copilot replaced them with <code>document.getElementById</code>.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nb">document</span><span class="p">.</span><span class="nx">f</span><span class="p">.</span><span class="nx">b</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span><span class="p">;</span> <span class="c1">// clearing the timer text from the button
</span></span></span><span class="line"><span class="cl"><span class="nb">document</span><span class="p">.</span><span class="nx">f</span><span class="p">.</span><span class="nx">b</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">min</span> <span class="o">+</span> <span class="s2">&#34;:&#34;</span> <span class="o">+</span> <span class="nx">sec</span><span class="p">;</span> <span class="c1">// setting the timer text on the button
</span></span></span><span class="line"><span class="cl"><span class="nb">document</span><span class="p">.</span><span class="nx">f</span><span class="p">[(</span><span class="s1">&#39;img&#39;</span> <span class="o">+</span> <span class="nx">i</span><span class="p">)].</span><span class="nx">src</span> <span class="o">=</span> <span class="s2">&#34;image0.gif&#34;</span><span class="p">;</span> <span class="c1">// accessing the images in the form
</span></span></span></code></pre></div></div>
<p>Trying to find info on the above code sent me down a rabbit hole, where I learned that you can actually <em>still</em> access elements directly by their ID. Um, what? All I&rsquo;ve ever heard about is using <code>document.getElementByID</code>, but doing something like this totally works:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nx">div</span> <span class="nx">id</span> <span class="o">=</span><span class="s2">&#34;myDiv&#34;</span><span class="o">&gt;</span><span class="nx">initial</span> <span class="nx">value</span><span class="o">&lt;</span><span class="err">/div&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">someButton</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s2">&#34;click&#34;</span><span class="p">,</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span> <span class="nx">myDiv</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="s1">&#39;new value&#39;</span><span class="p">;</span> <span class="p">});</span></span></span></code></pre></div></div>
<p>According to <a href="https://stackoverflow.com/questions/25325221/why-dont-we-just-use-element-ids-as-identifiers-in-javascript"  target="_blank" rel="noreferrer">this thread</a>, it&rsquo;s not only allowed but actually standards compliant. Yuck. It seems it&rsquo;s only in there for backwards compatibility though, and generally discouraged.</p>
<p>Enough of that.. here&rsquo;s code for the last one: <a href="https://github.com/grantwinney/BlogCodeSamples/tree/master/Languages/JavaScript/LegacyGames/LegacyJSConcentration"  target="_blank" rel="noreferrer">LegacyJSConcentration · grantwinney/BlogCodeSamples</a></p>
]]></content:encoded><media:content url="https://grantwinney.com/old-js-games-part2/feature.webp" medium="image" type="image/webp"/></item><item><title>Bringing a 25 year old JS maze back to life</title><link>https://grantwinney.com/old-js-maze-game/</link><pubDate>Fri, 22 May 2026 18:36:00 +0000</pubDate><guid>https://grantwinney.com/old-js-maze-game/</guid><description>I stumbled on a website 30 years in the making, and a little JS maze game that needed a bit of TLC.</description><content:encoded><![CDATA[<p>I was trying to get something to work in IrfanView the other day, and for once, instead of being taken to reddit or stackexchange or a yt short, I was surprised to find myself on a homey website called <a href="https://etherwork.net/"  target="_blank" rel="noreferrer">etherwork.net</a>. I got my answer, but instead of jumping ship like I&rsquo;d do with the other sites, I spent a couple hours just looking around.</p>
<p>It looks straight out of the 90s (which it is), and it reminds me of what was fun about the web back then. Not singularly focused, not SEO optimized, not riddled with ads showing gross toenails.. just someone sharing their hobbies and interests with whoever happens by. Most sites like these were abandoned a long time ago, or were the victim of shuttered services, or were reworked and the old content deleted.</p>
<p>What&rsquo;s so unique about this site is that it&rsquo;s still actively being updated. Things from 30 years ago are still there, and then there&rsquo;s new recipe posts from just a month ago. It looks like a labor of love if I ever saw one.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/old-js-maze-game/etherworknet.gif"
    width="580"
      height="673"></figure>
<p>In an era when most roads now lead to the same few large sites (social media, forums, youtube), and nearly everyone&rsquo;s feeding the google monster, this feels like a unicorn. It&rsquo;s pleasant to find someone simply writing and sharing for the sake of writing and sharing. If you lived through the dawn of home internet right around the turn of the century (ugh I feel old), you know what I&rsquo;m talking about. Don&rsquo;t get me wrong, there was a lot of garbage that no one will miss, but there was a lot of good too.</p>
<p>The author has a <a href="https://etherwork.net/llinks.shtml"  target="_blank" rel="noreferrer">page full of bookmarks</a>, including one for an archived copy of a maze someone uploaded to a site called <a href="https://web.archive.org/web/20000815211242/http://javascript.internet.com/games/"  target="_blank" rel="noreferrer">The JavaScript Source</a>. The original author of <a href="https://web.archive.org/web/20000815215258/http://javascript.internet.com/games/maze.html"  target="_blank" rel="noreferrer">this maze script</a> was Jason Hotchkiss, who uploaded it back in 1999. Just a few weeks before Y2K hit and the world <em>didn&rsquo;t</em> end. 😏</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/old-js-maze-game/mazejs-credit.webp"
    width="677"
      height="141"></figure>
<p>Although the script was guaranteed to work in IE5 <em>and</em> Netscape (never a given back then!), it doesn&rsquo;t work in Chromium-based browsers. Not a shock. What <em>is</em> shocking is that it works as-is in Firefox.. gotta love Mozilla for their backward-compatibility! I thought it&rsquo;d be fun to bring it back to life, although I wasn&rsquo;t sure how bad it&rsquo;d be. The fact that it&rsquo;s working Firefox is promising.</p>
<p>As it turns out, it didn&rsquo;t take much of anything. I plugged it into MS Copilot, which quickly identified why it was failing (mainly the use of Netscape&rsquo;s <code>document.layers</code> object) and then rewrote it for me. Amazingly, I had to fix nothing, although I couldn&rsquo;t resist adding in a couple things of my own.</p>
<p>Here it is, in all its ASCII glory. You can press the buttons (duh) or click on the maze to give it focus and then use WASD or the arrows to move around.</p>
<div id="root">
  <pre id="viewport" tabindex="0"></pre>
  <div id="readout"></div><br>
  <form onsubmit="return false">
    <button onclick="turn(-1);">Left</button>
    <button onclick="turn(1);">Right</button>
    <button onclick="moveForward();">Forward</button>
    <button onclick="moveBack();">Back</button><br>
    <button onclick="cheat();"><u>C</u>heat</button>
    <button onclick="start();"><u>R</u>eset</button>
  </form>
</div>
<p>If you got this far, there&rsquo;s not really any &ldquo;point&rdquo; to this post, but maybe that <em>was</em> the point. <a href="https://github.com/grantwinney/BlogCodeSamples/tree/master/Languages/JavaScript/LegacyGames/LegacyJSMaze"  target="_blank" rel="noreferrer">Here&rsquo;s the code</a> if you want to mess around with it.</p>
<p>It&rsquo;s interesting to me, stumbling onto code that someone put time into making, only for it to be nearly lost to the shifting sands of the internet. And it happens all the time, every day.</p>
]]></content:encoded><media:content url="https://grantwinney.com/old-js-maze-game/feature.webp" medium="image" type="image/webp"/></item><item><title>eBay, EdgeSuite, and error #97.etc.etc</title><link>https://grantwinney.com/ebay-edgesuite-akamai-error/</link><pubDate>Thu, 30 Apr 2026 16:49:00 +0000</pubDate><guid>https://grantwinney.com/ebay-edgesuite-akamai-error/</guid><description>When eBay went down the other day, everyone started getting edgesuite errors. I was kinda curious what those were.. here&amp;rsquo;s what I found.</description><content:encoded><![CDATA[<p>Just a warning, if you&rsquo;re reading this (and you must be, unless you&rsquo;re tasting or smelling it somehow) I can&rsquo;t promise you&rsquo;ll learn anything. I just stumbled on something and felt like looking into it a bit.</p>
<p>When eBay went down the other day (it&rsquo;s gone down a few times recently), the page periodically threw out errors like this one when I tried to visit it:</p>
<blockquote><p>An error occurred while processing your request.
Reference #97.ec4f4317.1777321399.42065ab
<a href="https://errors.edgesuite.net/97.ec4f4317.1777321399.42065ab"  target="_blank" rel="noreferrer">https://errors.edgesuite.net/97.ec4f4317.1777321399.42065ab</a></p>
</blockquote><p>Nothing else in the DOM, nothing in the console except that the server got a 504 gateway error. Strange to not just have a page saying &ldquo;we&rsquo;ll be back shortly&rdquo; or &ldquo;please stop DDOS&rsquo;ing us thx&rdquo; instead of whatever this cryptic error message is.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/ebay-edgesuite-akamai-error/ebay-error.png"
    width="509"
      height="294"></figure>
<p>The only thing to go on was the link, which begged clicking, so one click later&hellip;</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/ebay-edgesuite-akamai-error/edgesuite-error.png"
    width="624"
      height="294"></figure>
<p>Hm, a little more to go on. Clearly a page only useful to whoever the customer is, not the tens of thousands of random people who saw it while trying to buy an item.</p>
<p>One quick visit to a <a href="https://www.godaddy.com/whois/results.aspx?domain=edgesuite.net"  target="_blank" rel="noreferrer">whois site</a> tells us exactly who owns this service, and I&rsquo;m kinda not shocked this is the experience when something goes down. Some years back I discovered <a href="https://grantwinney.com/websites-requesting-access-to-motion-sensors/"  target="_blank" rel="noreferrer">Akamai is the reason many sites request access to motion sensors</a>, which is kind of a weird thing to see in your address bar on the desktop. I can&rsquo;t speak to their effectiveness since I&rsquo;ve never used them personally, but they don&rsquo;t seem to bother with hiding their presence.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/ebay-edgesuite-akamai-error/edgesuite-whois.png"
    width="803"
      height="625"></figure>
<p>And if there&rsquo;s any doubt, the <a href="https://techdocs.akamai.com/edge-diagnostics/docs/translate-error-string"  target="_blank" rel="noreferrer">Translate Error String</a> mentioned in the error is documented on Akamai&rsquo;s site. It just takes the cryptic id and returns the error message and logs. At least they don&rsquo;t display the actual logs right on the page for everyone to see, but it&rsquo;s still odd.</p>
<p>This seems to be part of their <a href="https://www.akamai.com/glossary/what-is-a-waf"  target="_blank" rel="noreferrer">Web Application Firewall</a> and fwiw they&rsquo;re not the only provider that behaves (to me) unexpectedly. I&rsquo;ve had a number of sites toss up weird Cloudflare &ldquo;are you a human&rdquo; pages that honestly make it look like the site got hacked. Rather ugly pages that don&rsquo;t fit the site&rsquo;s theme at all. I wonder why they don&rsquo;t polish the experience a bit? Do the companies that use them not realize, or just not care?</p>
]]></content:encoded><media:content url="https://grantwinney.com/ebay-edgesuite-akamai-error/feature.webp" medium="image" type="image/webp"/></item><item><title>A weekend spent cleaning house</title><link>https://grantwinney.com/cleaning-up-hide-comments-everywhere/</link><pubDate>Tue, 28 Apr 2026 18:51:00 +0000</pubDate><guid>https://grantwinney.com/cleaning-up-hide-comments-everywhere/</guid><description>I spent a weekend cleaning up a personal project, and learned more about JavaScript in the process.</description><content:encoded><![CDATA[<p>It&rsquo;s probably a good sign that you&rsquo;ve found the right career for yourself, when you spend time over the weekend on a small project that uses the same skills you spent all week using professionally! Or maybe it&rsquo;s unhealthy, who&rsquo;s to say? 🙄</p>
<p>I spent a few hours this weekend cleaning up, commenting, and bug-fixing a browser addon I created quite a few years ago. I rarely touch it more than once a year or so, which is good since uploading new versions is a multi-step process involving an &ldquo;approval&rdquo; <a href="https://techgyo.com/researcher-uncovers-sketchy-chrome-extensions-with-4-million-installs"  target="_blank" rel="noreferrer">that</a> <a href="https://www.kaspersky.com/blog/suspicious-chrome-extensions-with-6-million-installs/53529/"  target="_blank" rel="noreferrer">has</a> <a href="https://www.malwarebytes.com/blog/news/2025/07/millions-of-people-spied-on-by-malicious-browser-extensions-in-chrome-and-edge"  target="_blank" rel="noreferrer">questionable</a> <a href="https://www.techradar.com/pro/security/fake-chrome-ai-extensions-targeted-over-300-000-users-to-steal-emails-personal-data-and-more"  target="_blank" rel="noreferrer">value</a>. But I digress&hellip;</p>

<h2 class="relative group">Better code reuse with ES modules
    <div id="better-code-reuse-with-es-modules" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#better-code-reuse-with-es-modules" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Keeping a codebase <a href="https://www.geeksforgeeks.org/software-engineering/dont-repeat-yourselfdry-in-software-development/"  target="_blank" rel="noreferrer">DRY</a> is something every dev should strive for, as much as possible. If the same code is copied several times, then they all need to be updated every time there&rsquo;s a change. At some point, they&rsquo;ll start to diverge as one gets updated and the others don&rsquo;t, until nothing&rsquo;s quite the same anymore. No es bueno.</p>
<p>I had some duplicate code though, because I couldn&rsquo;t figure out how to reference it from all the various parts of the addon, but I fixed that and learned a few things in the process.</p>

<h3 class="relative group">Calling ES modules from a service worker
    <div id="calling-es-modules-from-a-service-worker" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#calling-es-modules-from-a-service-worker" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/background"  target="_blank" rel="noreferrer">Background service workers</a> can be specified as ES modules in the <code>manifest.json</code> file, which allows them to import code from other files, simply by adding a &ldquo;type&rdquo;:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">&#34;background&#34;</span><span class="err">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;service_worker&#34;</span><span class="p">:</span> <span class="s2">&#34;js/bg-service-worker.js&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;module&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="err">,</span></span></span></code></pre></div></div>
<p>After that, add <code>export</code> to the front of any &ldquo;shared&rdquo; functions:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">export</span> <span class="kd">function</span> <span class="nx">log</span><span class="p">(</span><span class="nx">message</span><span class="p">,</span> <span class="nx">isError</span> <span class="o">=</span> <span class="kc">false</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">isError</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="sb">`[</span><span class="si">${</span><span class="nx">chrome</span><span class="p">.</span><span class="nx">runtime</span><span class="p">.</span><span class="nx">getManifest</span><span class="p">().</span><span class="nx">name</span><span class="si">}</span><span class="sb">]: </span><span class="si">${</span><span class="nx">message</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">info</span><span class="p">(</span><span class="sb">`[</span><span class="si">${</span><span class="nx">chrome</span><span class="p">.</span><span class="nx">runtime</span><span class="p">.</span><span class="nx">getManifest</span><span class="p">().</span><span class="nx">name</span><span class="si">}</span><span class="sb">]: </span><span class="si">${</span><span class="nx">message</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>And import them into the service worker:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="o">*</span> <span class="nx">as</span> <span class="nx">utils</span> <span class="nx">from</span> <span class="s1">&#39;./shared-utils.js&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">chrome</span><span class="p">.</span><span class="nx">runtime</span><span class="p">.</span><span class="nx">onMessage</span><span class="p">.</span><span class="nx">addListener</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">message</span><span class="p">,</span> <span class="nx">sender</span><span class="p">,</span> <span class="nx">sendResponse</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">message</span><span class="p">.</span><span class="nx">event</span> <span class="o">!==</span> <span class="s1">&#39;comments_hidden&#39;</span> <span class="o">&amp;&amp;</span> <span class="nx">message</span><span class="p">.</span><span class="nx">event</span> <span class="o">!==</span> <span class="s1">&#39;comments_shown&#39;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">utils</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`background script not configured to run for message event: &#39;</span><span class="si">${</span><span class="nx">message</span><span class="p">.</span><span class="nx">event</span><span class="si">}</span><span class="sb">&#39;`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>I didn&rsquo;t <em>need</em> to do this, but it&rsquo;s a nice thing to learn about, that JS code can be organized and encapsulated in a way that seems similar to other languages. Reminds me of Python.</p>

<h3 class="relative group">Calling ES modules from a content script
    <div id="calling-es-modules-from-a-content-script" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#calling-es-modules-from-a-content-script" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The context for a background service worker is outside of any individual tab, and the code above works for those. Annoyingly, content scripts, which run inside of individual tabs, don&rsquo;t play nicely with ES modules. That was a big reason I had duplicate code.</p>
<p>But then I came across a <a href="https://stackoverflow.com/a/53033388"  target="_blank" rel="noreferrer">post from 2018</a> where user <a href="https://otiai10.com/"  target="_blank" rel="noreferrer">otiai10</a> suggested using a dynamic <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import"  target="_blank" rel="noreferrer">import()</a>. From the MDN page, this sounds like exactly what I needed to load an ES module from vanilla JS.</p>
<blockquote><p>The <strong><code>import()</code></strong> syntax, commonly called <em>dynamic import</em>, is a function-like expression that allows loading an ECMAScript module asynchronously and dynamically into a potentially non-module environment.</p>
</blockquote><p>To get it to work, I just had to take the entry point code in the content script that&rsquo;s called whenever the content script loads:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">chrome</span><span class="p">.</span><span class="nx">runtime</span><span class="p">.</span><span class="nx">onMessage</span><span class="p">.</span><span class="nx">addListener</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">message</span><span class="p">,</span> <span class="nx">_sender</span><span class="p">,</span> <span class="nx">_sendResponse</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">switch</span> <span class="p">(</span><span class="nx">message</span><span class="p">.</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="s1">&#39;update_tab&#39;</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">            <span class="nx">adjustCommentsVisibility</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">            <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="s1">&#39;toggle_tab&#39;</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">            <span class="nx">toggleCommentVisibility</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">default</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">            <span class="nx">log</span><span class="p">(</span><span class="sb">`content script received unexpected event: </span><span class="si">${</span><span class="nx">message</span><span class="p">.</span><span class="nx">event</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">insertStylesIntoPage</span><span class="p">();</span></span></span></code></pre></div></div>
<p>And put it in an exported function (which I named &ldquo;main&rdquo;):</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">export</span> <span class="kd">function</span> <span class="nx">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">chrome</span><span class="p">.</span><span class="nx">runtime</span><span class="p">.</span><span class="nx">onMessage</span><span class="p">.</span><span class="nx">addListener</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">message</span><span class="p">,</span> <span class="nx">_sender</span><span class="p">,</span> <span class="nx">_sendResponse</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">switch</span> <span class="p">(</span><span class="nx">message</span><span class="p">.</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">case</span> <span class="s1">&#39;update_tab&#39;</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">                <span class="nx">adjustCommentsVisibility</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">                <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">case</span> <span class="s1">&#39;toggle_tab&#39;</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">                <span class="nx">toggleCommentVisibility</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">default</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">                <span class="nx">utils</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`content script received unexpected event: </span><span class="si">${</span><span class="nx">message</span><span class="p">.</span><span class="nx">event</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">insertStylesIntoPage</span><span class="p">();</span></span></span></code></pre></div></div>
<p>Then create a <em>new</em> content script, whose sole purpose is to call the exported <code>main</code> function in the file that was <em>previously</em> the content script:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="p">(</span><span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">src</span> <span class="o">=</span> <span class="nx">chrome</span><span class="p">.</span><span class="nx">runtime</span><span class="p">.</span><span class="nx">getURL</span><span class="p">(</span><span class="s1">&#39;js/content-main.js&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">contentScript</span> <span class="o">=</span> <span class="kr">await</span> <span class="kr">import</span><span class="p">(</span><span class="nx">src</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">contentScript</span><span class="p">.</span><span class="nx">main</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">})();</span></span></span></code></pre></div></div>
<p>And finally, declare the imported script in the &ldquo;manifest.json&rdquo; file as a web-accessible resource. I&rsquo;m not sure I had to add &ldquo;js/shared-utils.js&rdquo; actually, but it doesn&rsquo;t seem to hurt anything. Might not be necessary though.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">&#34;content_scripts&#34;</span><span class="err">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;matches&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;&lt;all_urls&gt;&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;js&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;js/content-script.js&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;run_at&#34;</span><span class="p">:</span> <span class="s2">&#34;document_start&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">]</span><span class="err">,</span>
</span></span><span class="line"><span class="cl"><span class="s2">&#34;web_accessible_resources&#34;</span><span class="err">:</span> <span class="p">[{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;matches&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;&lt;all_urls&gt;&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;resources&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;js/shared-utils.js&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;js/content-main.js&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}]</span><span class="err">,</span></span></span></code></pre></div></div>

<h2 class="relative group">Better file change detection with etag headers
    <div id="better-file-change-detection-with-etag-headers" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#better-file-change-detection-with-etag-headers" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>When I started this addon, I created two separate JSON files that it checks on a regular basis. One has a list of all the CSS elements to block for each website, but the other had only a single value - a version number. Both are cached in <code>storage.local</code> and checked daily. Every time I modified the list of CSS elements, I also bumped the version number in the other file.</p>
<p>My thinking was that updates are fairly rare, and downloading a small JSON file with a version number in it, and then comparing that before downloading the larger file, was better. A tiny optimization, but why not. The caveat was that if I forgot to bump the version, then whatever changes I made to the other file would go unnoticed. The addon wouldn&rsquo;t grab them if the version number didn&rsquo;t change.</p>
<p>And then I read about the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/If-None-Match"  target="_blank" rel="noreferrer">If-None-Match</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/If-Modified-Since"  target="_blank" rel="noreferrer">If-Modified-Since</a> headers, which is much better. My thinking was correct, but my approach reinvented the wheel. You can <a href="https://web.dev/articles/http-cache"  target="_blank" rel="noreferrer">read more here</a>, but if a server supports the etag header, then:</p>
<ul>
<li>Requesting data can also return a unique hash code representing that data, which can be cached.</li>
<li>Subsequent requests using that cached value cause a 304 Not Modified response if the data hasn&rsquo;t changed.</li>
<li>If the data <em>has</em> changed, the request returns a 200 with the data, and a new hash code to cache.</li>
<li>GitHub <a href="https://docs.github.com/en/rest/using-the-rest-api/best-practices-for-using-the-rest-api?apiVersion=2026-03-10&amp;utm_source=chatgpt.com#use-conditional-requests-if-appropriate"  target="_blank" rel="noreferrer">supports the etag header</a> from api.github.com, and after a little testing, it supports it when requesting files from raw.githubusercontent.com too.</li>
</ul>
<p>So a block of code like the one I use in the addon makes a request and passes the etag value. If the data hasn&rsquo;t changed, it&rsquo;ll get only a 304; otherwise, it receives a 200 and caches the updated CSS elements.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">eTagData</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">chrome</span><span class="p">.</span><span class="nx">storage</span><span class="p">.</span><span class="nx">local</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s1">&#39;etag&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">eTag</span> <span class="o">=</span> <span class="nx">eTagData</span><span class="p">.</span><span class="nx">etag</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">SITES_JSON</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">headers</span><span class="o">:</span> <span class="nx">eTag</span>  <span class="o">?</span> <span class="p">{</span> <span class="s1">&#39;If-None-Match&#39;</span><span class="o">:</span> <span class="nx">eTag</span>  <span class="p">}</span> <span class="o">:</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nx">status</span> <span class="o">===</span> <span class="mi">304</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* Not modified since last check, so no need to update local definitions */</span>
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">chrome</span><span class="p">.</span><span class="nx">storage</span><span class="p">.</span><span class="nx">local</span><span class="p">.</span><span class="nx">set</span><span class="p">({</span> <span class="s1">&#39;definition_version_last_check&#39;</span><span class="o">:</span> <span class="nx">getCurrentSeconds</span><span class="p">()</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">notUpdatedAction</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">notUpdatedAction</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nx">status</span> <span class="o">===</span> <span class="mi">200</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* Updated definitions retrieved successfully */</span>
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">chrome</span><span class="p">.</span><span class="nx">storage</span><span class="p">.</span><span class="nx">local</span><span class="p">.</span><span class="nx">set</span><span class="p">({</span> <span class="s1">&#39;definition_version_last_check&#39;</span><span class="o">:</span> <span class="nx">getCurrentSeconds</span><span class="p">()</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nx">newEtag</span> <span class="o">=</span> <span class="nx">response</span><span class="p">.</span><span class="nx">headers</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s2">&#34;ETag&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">newEtag</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kr">await</span> <span class="nx">chrome</span><span class="p">.</span><span class="nx">storage</span><span class="p">.</span><span class="nx">local</span><span class="p">.</span><span class="nx">set</span><span class="p">({</span> <span class="s1">&#39;etag&#39;</span><span class="o">:</span> <span class="nx">newEtag</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">        <span class="kd">let</span> <span class="nx">data</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="kr">await</span> <span class="nx">chrome</span><span class="p">.</span><span class="nx">storage</span><span class="p">.</span><span class="nx">local</span><span class="p">.</span><span class="nx">set</span><span class="p">({</span> <span class="s1">&#39;global_definitions&#39;</span><span class="o">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">updatedAction</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">updatedAction</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">log</span><span class="p">(</span><span class="sb">`Unable to retrieve the latest definitions: (</span><span class="si">${</span><span class="nx">response</span><span class="p">.</span><span class="nx">status</span><span class="si">}</span><span class="sb">) </span><span class="si">${</span><span class="nx">response</span><span class="p">.</span><span class="nx">statusText</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>That&rsquo;s better than what I had before, since it only requires a single request no matter what. The server determines whether it sends the data back with a 200, or just sends a 304 response.</p>

<h2 class="relative group">Better comments with JSDoc
    <div id="better-comments-with-jsdoc" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#better-comments-with-jsdoc" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I have a lot of comments in the addon, all using the <code>//</code> symbol. After reading up on <a href="https://jsdoc.app/"  target="_blank" rel="noreferrer">JSDoc</a>, I updated quite a few of them (but not nearly all yet) to use the JSDoc syntax. I&rsquo;m not running any tool over the codebase to create documentation or anything.. what I really wanted was to get tooltips in VSCode to make life a little easier when I&rsquo;m updating the code, and VSCode can use the JSDoc comments to do that.</p>
<p>As a bonus, it forces me to think about what each parameter does, and what the type is that the function expects.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * Check if the addon can run for a given URL based on its protocol.
</span></span></span><span class="line"><span class="cl"><span class="cm"> * 
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @param {URL} tabUrl - The URL for which to check the protocol.
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @returns {boolean} - True if the current URL is supported; otherwise false.
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kd">function</span> <span class="nx">isCurrentUrlSupported</span><span class="p">(</span><span class="nx">tabUrl</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="o">!</span><span class="nx">INVALID_PROTOCOLS</span><span class="p">.</span><span class="nx">some</span><span class="p">(</span><span class="nx">p</span> <span class="p">=&gt;</span> <span class="nx">tabUrl</span><span class="p">.</span><span class="nx">protocol</span><span class="p">.</span><span class="nx">startsWith</span><span class="p">(</span><span class="nx">p</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="nx">INVALID_SITES</span><span class="p">.</span><span class="nx">some</span><span class="p">(</span><span class="nx">p</span> <span class="p">=&gt;</span> <span class="nx">tabUrl</span><span class="p">.</span><span class="nx">hostname</span><span class="p">.</span><span class="nx">startsWith</span><span class="p">(</span><span class="nx">p</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/cleaning-up-hide-comments-everywhere/jsdoc-sample.png"
    width="575"
      height="225"></figure>
<p>I&rsquo;ll probably get around to updating the other comments too, at some point, and switching out all my <code>.then</code> promise types of calls to be <code>async/await</code> (there&rsquo;s some heavily nested chunks of code that are kinda ugly), but right now the addon works well and after spending a weekend updating it I think I&rsquo;m good to shelve it again for awhile!</p>
]]></content:encoded><media:content url="https://grantwinney.com/cleaning-up-hide-comments-everywhere/feature.webp" medium="image" type="image/webp"/></item><item><title>Missing year on the VS 2026 shortcut hints at big change</title><link>https://grantwinney.com/visual-studio-mystery-version/</link><pubDate>Mon, 19 Jan 2026 11:52:00 +0000</pubDate><guid>https://grantwinney.com/visual-studio-mystery-version/</guid><description>On installing VS 2026, I noticed the year&amp;rsquo;s missing on the shortcut. It&amp;rsquo;s no bug, and the way VS is being developed, released and supported is changing in a big way.</description><content:encoded><![CDATA[<p>After installing Visual Studio 2026 recently, which rolled out this past November, the first thing I noticed when I went to start it was the shortcut. Specifically, the version.. or lack of one.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Yes, I have all of these installed.."
    src="/visual-studio-mystery-version/vs-icons.png"
    width="392"
      height="312"></figure>
<p>My first thought was it&rsquo;s a bug, but a Microsoft program manager confirmed <a href="https://developercommunity.visualstudio.com/t/VisualStudio2026StartMenuIssues:MissingShortcutandIncorrectToolLocation/10998855#TPIN-N11001509"  target="_blank" rel="noreferrer">it&rsquo;s intentional</a>:</p>
<blockquote><p>The change in naming and shortcut launch behavior is intentional. Opening the “Visual Studio” shortcut will launch Visual Studio 2026, and that will be the primary way to start the IDE moving forward.</p>
</blockquote><p>It&rsquo;s part of a bigger redesign for Visual Studio, which will release a new version every year, each one <em>replacing</em> the previous one. That&rsquo;s right.. replacing. When asked if new versions would be side-by-side (as they&rsquo;ve always been) or an upgrade, another program manager confirmed <a href="https://devblogs.microsoft.com/visualstudio/spend-less-time-upgrading-more-time-coding-in-visual-studio-2026/?commentid=30512#comment-30512"  target="_blank" rel="noreferrer">it&rsquo;s the latter</a>:</p>
<blockquote><p>[T]he next major version of VS (2027) will be an inline upgrade and not a side-by-side install.</p>
</blockquote><p>The rest of this post is derived from official blog posts as well as release, support and other documentation for VS 2026. Check out these original posts for all the details:</p>
<ul>
<li><a href="https://devblogs.microsoft.com/visualstudio/visual-studio-2026-is-here-faster-smarter-and-a-hit-with-early-adopters/#:~:text=Here%E2%80%99s%20the%20best%20part"  target="_blank" rel="noreferrer">Visual Studio 2026 is here: faster, smarter, and a hit with early adopters</a></li>
<li><a href="https://devblogs.microsoft.com/visualstudio/spend-less-time-upgrading-more-time-coding-in-visual-studio-2026/"  target="_blank" rel="noreferrer">Spend Less Time Upgrading, More Time Coding in Visual Studio 2026</a></li>
<li><a href="https://devblogs.microsoft.com/visualstudio/visual-studio-built-for-the-speed-of-modern-development/"  target="_blank" rel="noreferrer">Visual Studio – Built for the Speed of Modern Development</a></li>
<li><a href="https://learn.microsoft.com/en-us/visualstudio/releases/2026/release-rhythm"  target="_blank" rel="noreferrer">Visual Studio Channels and Release Rhythm | Microsoft Learn</a></li>
<li><a href="https://learn.microsoft.com/en-us/visualstudio/releases/2026/servicing-vs"  target="_blank" rel="noreferrer">Visual Studio Product Lifecycle and Servicing | Microsoft Learn</a></li>
<li><a href="https://github.com/dotnet/sdk/blob/main/documentation/general/decouple-vs-and-net-sdk.md"  target="_blank" rel="noreferrer">Decoupling the .NET SDK and Visual Studio · dotnet/sdk</a></li>
</ul>

<h2 class="relative group">What are they changing?
    <div id="what-are-they-changing" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-are-they-changing" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Starting with VS 2026:</p>
<ul>
<li>Every November means a new version of Visual Studio (2026, 2027, etc), which will overwrite the previous one. No side-by-side installs.
<ul>
<li>Community users must upgrade to continue getting updates.</li>
<li>Pro and Enterprise users can hold off for a bit, but eventually need to update too.</li>
</ul>
</li>
<li>Updates are monthly, instead of quarterly, as long as the latest release is installed.</li>
<li>The shortcut won&rsquo;t have a version/year number in it anymore.</li>
</ul>

<h2 class="relative group">Why are they changing it?
    <div id="why-are-they-changing-it" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#why-are-they-changing-it" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>They want to be able to update Visual Studio faster, but the IDE (for writing code) has always been tightly coupled with other related tools, as evidenced by the <a href="https://learn.microsoft.com/en-us/dotnet/core/porting/versioning-sdk-msbuild-vs"  target="_blank" rel="noreferrer">.NET SDK, MSBuild, and Visual Studio versioning</a> page. I can <em>easily</em> imagine how separate but related parts being bundled into a single app would slow down overall development. Any dev who&rsquo;s worked with legacy WinForms apps knows what monolithic looks like.</p>
<p>If they update the build tools, but need the old tools to still work and be available, then they have to release a new Visual Studio version too, since everything&rsquo;s bundled. If they also have major updates for Visual Studio, it&rsquo;d make sense to wait until that new version is being released to include them, but that means delaying new features that we could&rsquo;ve benefitted from sooner. Everything has to line up and play nice, and a delay in one area or from one team prevents <em>everything</em> from shipping.</p>
<p>Beyond that, it sounds like they want out of the business of <a href="https://learn.microsoft.com/en-us/visualstudio/releases/2026/servicing-vs#support-for-older-versions"  target="_blank" rel="noreferrer">supporting old versions</a> for a decade, too. VS 2015 <em>just</em> went out of support a few months ago, 2017 is supported until next year, and 2019 and 2022 will be supported for years to come. That&rsquo;s a lot to juggle.</p>

<h2 class="relative group">How are they changing it?
    <div id="how-are-they-changing-it" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#how-are-they-changing-it" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>They decoupled the Visual Studio IDE from the build tools, so they can be developed and released separately. I imagine that the IDE, being guaranteed to have access to certain tools that were bundled with the install, was probably a bit of a nightmare to disentangle from everything else.</p>
<p>Since the IDE won&rsquo;t be bundled with a particular set of build tools anymore, it uses a new <a href="https://learn.microsoft.com/en-us/visualstudio/install/setup-assistant"  target="_blank" rel="noreferrer">Setup Assistant</a> tool that determines what an app needs and can retrieve missing dependencies. I haven&rsquo;t used it yet, but I assume it looks at the sln/slnx and csproj files.</p>

<h2 class="relative group">What&rsquo;s not changing?
    <div id="whats-not-changing" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whats-not-changing" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>VS 2022 (and earlier) will continue to run side-by-side with 2026 (and later), although they&rsquo;re claiming all projects and extensions that worked in 2022 will work as-is with nothing else needed in 2026.</p>
<p>They&rsquo;re not changing the lifecycle for old versions, but each version going forward will only receive full support for the year it&rsquo;s released, then it <em>has</em> to be upgraded.</p>

<h2 class="relative group">Some concerns&hellip;
    <div id="some-concerns" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#some-concerns" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I hope it goes as great as they say it will, but I can imagine how it might <em>not</em>&hellip;</p>

<h3 class="relative group">Discoverability
    <div id="discoverability" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#discoverability" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Right now, VS 2026 can&rsquo;t be quickly found in the start menu. Typing &ldquo;Visual Studio 2026&rdquo; shows nothing, and typing &ldquo;Visual Studio&rdquo; shows <em>every</em> version of VS that&rsquo;s installed (and VS Code). Maybe eventually as the older versions become unnecessary this won&rsquo;t be an issue, but for now it&rsquo;s annoying.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/visual-studio-mystery-version/search-for-visual-studio-2026.jpg"
    width="773"
      height="408"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/visual-studio-mystery-version/search-for-visual-studio.jpg"
    width="744"
      height="462"></figure>
<p>As Dave noted in the comments below, the context menu item in File Explorer has the same issue.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/visual-studio-mystery-version/context-menu-sln.png"
    width="628"
      height="354"></figure>
<p>It shows up as 2026 in the Visual Studio Installer app and on the splash screen. Why not just show the shortcut and menu item like that too, and then replace them with &ldquo;Visual Studio 2027&rdquo; in November? Why make these parts generic and nothing else?</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/visual-studio-mystery-version/visual-studio-installer.png"
    width="1200"
      height="600"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/visual-studio-mystery-version/vs2026-splash.jpg"
    width="680"
      height="420"></figure>
<p>Ironically, this seems to fly in the face of what was said <a href="https://devblogs.microsoft.com/visualstudio/weve-upgraded-the-ui-in-visual-studio-2022"  target="_blank" rel="noreferrer">when VS 2022 was released</a>:</p>
<blockquote><p>From a basic wayfinding perspective, distinguishing updates to the look and feel and packaging of a new product make it easier for you to know when you’re using the new product versus other versions you may have running at the same time.</p>
</blockquote>
<h3 class="relative group">Legacy Apps
    <div id="legacy-apps" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#legacy-apps" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>VS 2022 is a 64-bit app with a 64-bit WinForms designer. Because of that, <a href="https://grantwinney.com/why-doesnt-vs2022-show-my-winforms-ui/"  target="_blank" rel="noreferrer">older 32-bit WinForms apps can&rsquo;t be designed in 2022</a>. Microsoft&rsquo;s suggestion is the obvious one - upgrade apps to 64-bit. But I&rsquo;ve worked on decades-old apps with a hundred projects - upgrading is a large undertaking that&rsquo;s tough for the devs <em>and</em> a tough sell to management. Not great, but that&rsquo;s the way it is. They created an <a href="https://devblogs.microsoft.com/visualstudio/winforms-designer-selection-for-32-bit-net-framework-projects/"  target="_blank" rel="noreferrer">out of process designer</a> that helped but had caveats, and I&rsquo;m not sure how much further they can/will take it. The solution for most teams, I&rsquo;m guessing, is to keep using VS 2019.</p>
<p>I don&rsquo;t know what else might happen to Visual Studio that would have an impact like this, but what if it does? What&rsquo;s the recourse for companies who paid for Visual Studio, developed an app, and can&rsquo;t afford to overhaul it to appease a new Visual Studio version? I&rsquo;d like to think there&rsquo;d be a reasonable workaround, but Microsoft has shown us with Windows 11 that&rsquo;s <a href="https://www.windowscentral.com/software-apps/windows-11/microsoft-openly-promotes-tossing-your-laptop-into-a-big-pile-of-e-waste"  target="_blank" rel="noreferrer">it&rsquo;s not afraid to just leave us in the dust</a>. Then again, <a href="https://learn.microsoft.com/en-us/lifecycle/products/microsoft-net-framework"  target="_blank" rel="noreferrer">they&rsquo;re still supporting .NET Framework</a> 4.6 for another year and haven&rsquo;t even placed EOL dates on 4.7 and 4.8. Still, I wouldn&rsquo;t be shocked if they told everyone to update their legacy apps or find a different way to keep developing them.</p>

<h3 class="relative group">Usability
    <div id="usability" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#usability" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Microsoft has made poor design changes in the past, bad enough to make devs want to skip a version. Anyone here remember VS 2012? For reference, here&rsquo;s VS 2005, 2010, and 2015, with a normal color palette, UI, and menus:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/visual-studio-mystery-version/vs-ui-not2012.png"
    width="1040"
      height="506"></figure>
<p>And here&rsquo;s 2012, which tossed out those distracting colors, space-wasting borders, and sheepish menus. I WANT MENUS THAT SCREAM AT ME.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/visual-studio-mystery-version/vs-ui-2012.png"
    width="655"
      height="301"></figure>
<p>There was so much pushback, they added a regex key to revert the menus, and then reverted everything in 2015. I kept using 2010 for most things, but going forward that wouldn&rsquo;t be an option. If some design guru decides VS 2027 (or 2028, or 2029) needs a UI overhaul, then we&rsquo;re stuck with it.</p>
<p>Reading about the changes, and thinking about it all, it sounds like a really good thing. I&rsquo;ll be cautiously optimistic, and hope that the team not having to support 4-5 separate versions of Visual Studio means we get more innovations faster.</p>
]]></content:encoded><media:content url="https://grantwinney.com/visual-studio-mystery-version/feature.webp" medium="image" type="image/webp"/></item><item><title>Using null conditional assignment in C# 14</title><link>https://grantwinney.com/csharp-null-conditional-assignment/</link><pubDate>Wed, 03 Dec 2025 12:09:00 +0000</pubDate><guid>https://grantwinney.com/csharp-null-conditional-assignment/</guid><description>The null conditional operator just got an upgrade.. we can do assignments with it now! Let&amp;rsquo;s see it in action.</description><content:encoded><![CDATA[<p>We&rsquo;ve had the <a href="https://grantwinney.com/null-conditional-and-null-coalescing-operators/"  target="_blank" rel="noreferrer">null conditional operator</a> for years, since C# 6. You might want to skim that post before reading this one, if you&rsquo;re unfamiliar with it. Otherwise, I&rsquo;ll borrow a bit for a quick review, and then we&rsquo;ll look at what C# 14 added. And if you&rsquo;d like to mess around with the code below, it&rsquo;s available in my <a href="https://github.com/grantwinney/CSharpDotNetFeatures/tree/master/C%23%2014/NullConditionalAssignment"  target="_blank" rel="noreferrer">CSharpDotNetFeatures</a> repo on GitHub.</p>

<h2 class="relative group">Null Conditional (a review)
    <div id="null-conditional-a-review" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#null-conditional-a-review" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>When we&rsquo;re dealing with nested classes, like the one below for example, we usually have to be really defensive to avoid the dreaded <code>NullReferenceException</code>.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Company</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">IList</span><span class="p">&lt;</span><span class="n">Department</span><span class="p">&gt;</span> <span class="n">Departments</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Department</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">IList</span><span class="p">&lt;</span><span class="n">Employee</span><span class="p">&gt;</span> <span class="n">Employees</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Employee</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Employee</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>That means checking for <code>null</code> repeatedly when referencing, say, an employee&rsquo;s name. And since collections are involved, we need to check to make sure they have valid values too. If we just wanted to get the first employee from the first department, we might do something like this:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="c1">// Getting employee name traditionally</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">company</span> <span class="p">!=</span> <span class="kc">null</span> <span class="p">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">    <span class="n">company</span><span class="p">.</span><span class="n">Departments</span> <span class="p">!=</span> <span class="kc">null</span> <span class="p">&amp;&amp;</span> <span class="n">company</span><span class="p">.</span><span class="n">Departments</span><span class="p">.</span><span class="n">Count</span> <span class="p">&gt;</span> <span class="m">0</span> <span class="p">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">    <span class="n">company</span><span class="p">.</span><span class="n">Departments</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="n">Employees</span> <span class="p">!=</span> <span class="kc">null</span> <span class="p">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">    <span class="n">company</span><span class="p">.</span><span class="n">Departments</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="n">Employees</span><span class="p">.</span><span class="n">Count</span> <span class="p">&gt;</span> <span class="m">0</span> <span class="p">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">    <span class="n">company</span><span class="p">.</span><span class="n">Departments</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="n">Employees</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="n">Name</span> <span class="p">!=</span> <span class="kc">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">employeeName</span> <span class="p">=</span> <span class="n">company</span><span class="p">.</span><span class="n">Departments</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="n">Employees</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="n">Name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Employee Name: {employeeName}&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;Employee Name: N/A&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>What the null conditional operators let us do is shorten the above to just this:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="c1">// Getting employee name using null-conditional operators</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">employeeName</span> <span class="p">=</span> <span class="n">company</span><span class="p">?.</span><span class="n">Departments</span><span class="p">?[</span><span class="m">0</span><span class="p">]?.</span><span class="n">Employees</span><span class="p">?[</span><span class="m">0</span><span class="p">]?.</span><span class="n">Name</span> <span class="p">??</span> <span class="s">&#34;N/A&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Employee Name: {employeeName}&#34;</span><span class="p">);</span></span></span></code></pre></div></div>
<p>At every step along the way, it checks whether it can skip the rest (short-circuit), and just set <code>employeeName == &quot;N/A&quot;</code>:</p>
<ul>
<li>If <code>org == null</code>, then it skips everything else and sets employee name to &ldquo;N/A&rdquo;.</li>
<li>If <code>org.Departments == null</code>, it skips the rest and moves on.</li>
<li>If <code>org?.Departments[0] == null</code> for some weird reason, it skips the rest.</li>
<li>And on and on, with <code>.Employees</code>, then <code>.Employees[0]</code>, etc.</li>
</ul>
<p>Getting a nested value involves a <em>lot</em> less code, which is great.</p>

<h2 class="relative group">Null Conditional Assignment
    <div id="null-conditional-assignment" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#null-conditional-assignment" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>What the null conditional operator hasn&rsquo;t helped us shorten in the past, though, is <em>setting</em> a value. If any part of the nested objects is <code>null</code> we&rsquo;ll get an exception, so we have to be careful:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="c1">// Setting employee name traditionally</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">company</span> <span class="p">!=</span> <span class="kc">null</span> <span class="p">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">    <span class="n">company</span><span class="p">.</span><span class="n">Departments</span> <span class="p">!=</span> <span class="kc">null</span> <span class="p">&amp;&amp;</span> <span class="n">company</span><span class="p">.</span><span class="n">Departments</span><span class="p">.</span><span class="n">Count</span> <span class="p">&gt;</span> <span class="m">0</span> <span class="p">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">    <span class="n">company</span><span class="p">.</span><span class="n">Departments</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="n">Employees</span> <span class="p">!=</span> <span class="kc">null</span> <span class="p">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">    <span class="n">company</span><span class="p">.</span><span class="n">Departments</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="n">Employees</span><span class="p">.</span><span class="n">Count</span> <span class="p">&gt;</span> <span class="m">0</span> <span class="p">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">    <span class="n">company</span><span class="p">.</span><span class="n">Departments</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="n">Employees</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="n">Name</span> <span class="p">!=</span> <span class="kc">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">company</span><span class="p">.</span><span class="n">Departments</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="n">Employees</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;John Doe&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>However, with the update in C# 14 that allows using the null conditional operator when <em>assigning</em> a value, we can use the same short-circuiting code here too:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="c1">// Setting employee name using null-conditional operators</span>
</span></span><span class="line"><span class="cl"><span class="n">company</span><span class="p">?.</span><span class="n">Departments</span><span class="p">?[</span><span class="m">0</span><span class="p">]?.</span><span class="n">Employees</span><span class="p">?[</span><span class="m">0</span><span class="p">]?.</span><span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;John Doe&#34;</span><span class="p">;</span></span></span></code></pre></div></div>
<p>Combined with a feature we got in C# 8, to use the null-coalescing operator <code>??=</code> during assignment, we can also quickly make sure that existing values aren&rsquo;t accidentally overwritten:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="c1">// Setting employee name using null-conditional and null-coalescing operators</span>
</span></span><span class="line"><span class="cl"><span class="n">company</span><span class="p">?.</span><span class="n">Departments</span><span class="p">?[</span><span class="m">0</span><span class="p">]?.</span><span class="n">Employees</span><span class="p">?[</span><span class="m">0</span><span class="p">]?.</span><span class="n">Name</span> <span class="p">??=</span> <span class="s">&#34;John Doe&#34;</span><span class="p">;</span></span></span></code></pre></div></div>
<p>It could even be used with LINQ when searching through nested collections, to handle the case where whatever we&rsquo;re searching for isn&rsquo;t found:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">ActivateEmployee</span><span class="p">(</span><span class="n">Company</span> <span class="n">company</span><span class="p">,</span> <span class="kt">string</span> <span class="n">dept</span><span class="p">,</span> <span class="kt">string</span> <span class="n">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Activate employee, using null-conditional assignment operator</span>
</span></span><span class="line"><span class="cl">    <span class="n">company</span>
</span></span><span class="line"><span class="cl">        <span class="p">?.</span><span class="n">Departments</span><span class="p">.</span><span class="n">SingleOrDefault</span><span class="p">(</span><span class="n">d</span> <span class="p">=&gt;</span> <span class="n">d</span><span class="p">.</span><span class="n">Name</span> <span class="p">==</span> <span class="s">&#34;Sales&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">?.</span><span class="n">Employees</span><span class="p">.</span><span class="n">SingleOrDefault</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Name</span> <span class="p">==</span> <span class="s">&#34;Greg Smith&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">?.</span><span class="n">Active</span> <span class="p">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h2 class="relative group">Thoughts
    <div id="thoughts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#thoughts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I like that the feature works on <em>both</em> sides of the assignment operator now, which is more consistent. I haven&rsquo;t used it in a prod environment yet (since it&rsquo;s brand new), but it definitely seems like it&rsquo;ll make things more readable &hellip; once I&rsquo;m used to reading it!</p>
<p>In some cases though, the (older) more verbose way could be more readable, and I think readability is really important, especially when multiple devs are working in an app. Heck, when I set an app down and pick it up a year later, <em>I</em> might as well be a different dev for all I remember about it, lol. All-in-all though, it&rsquo;s a nice addition to the language!</p>

<h2 class="relative group">Learn More
    <div id="learn-more" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#learn-more" aria-label="Anchor">#</a>
    </span>
    
</h2>
<ul>
<li><a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators#null-conditional-operators--and-"  target="_blank" rel="noreferrer">Null-conditional operators - <code>?.</code> and <code>?[]</code></a></li>
<li><a href="https://learn.microsoft.com/en-my/dotnet/csharp/language-reference/operators/null-coalescing-operator"  target="_blank" rel="noreferrer">Null-coalescing operators - <code>??</code> and <code>??=</code></a></li>
<li><a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-14#null-conditional-assignment"  target="_blank" rel="noreferrer">Null-conditional assignment - What&rsquo;s new in C# 14</a></li>
<li><a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-14.0/null-conditional-assignment"  target="_blank" rel="noreferrer">Null conditional assignment - C# feature specifications | Microsoft Learn</a></li>
</ul>
]]></content:encoded><media:content url="https://grantwinney.com/csharp-null-conditional-assignment/feature.webp" medium="image" type="image/webp"/></item><item><title>Using extension members in C# 14</title><link>https://grantwinney.com/csharp-extension-members/</link><pubDate>Tue, 02 Dec 2025 12:58:00 +0000</pubDate><guid>https://grantwinney.com/csharp-extension-members/</guid><description>Extension members take extension methods to the next level. Let&amp;rsquo;s see how to use this new C# 14 feature.</description><content:encoded><![CDATA[<p>Visual Studio 2026 was <a href="https://learn.microsoft.com/en-us/visualstudio/releases/2026/release-notes"  target="_blank" rel="noreferrer">released a week ago</a>, so it&rsquo;s time to checkout <a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-14"  target="_blank" rel="noreferrer">C# 14</a> and see what new and interesting goodies we got, starting with <a href="https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods"  target="_blank" rel="noreferrer">extension members</a>! If you&rsquo;d like to mess around with the code on here, it&rsquo;s available in my <a href="https://github.com/grantwinney/CSharpDotNetFeatures/tree/master/C%23%2014/ExtensionMembers"  target="_blank" rel="noreferrer">CSharpDotNetFeatures</a> repo on GitHub.</p>

<h2 class="relative group">Extension Methods (a review)
    <div id="extension-methods-a-review" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#extension-methods-a-review" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>For a long time, we&rsquo;ve had the ability to extend a class with extension methods, making it seem like the original class has methods on it that it doesn&rsquo;t. For example, here&rsquo;s a class with three methods that act on strings:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="k">class</span> <span class="nc">BlogStringHelpers</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Truncate post for summary, as for use in social media.</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">string</span> <span class="n">ToExcerpt</span><span class="p">(</span><span class="k">this</span> <span class="kt">string</span> <span class="n">str</span><span class="p">,</span> <span class="kt">int</span> <span class="n">maxLength</span> <span class="p">=</span> <span class="m">128</span><span class="p">)</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="n">str</span><span class="p">.</span><span class="n">Length</span> <span class="p">&lt;=</span> <span class="n">maxLength</span> <span class="p">?</span> <span class="n">str</span> <span class="p">:</span> <span class="s">$&#34;{str[..maxLength]}...&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Convert string to a slug to use as identifier for post.</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">string</span> <span class="n">ToSlug</span><span class="p">(</span><span class="k">this</span> <span class="kt">string</span> <span class="n">str</span><span class="p">)</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Regex</span><span class="p">.</span><span class="n">Replace</span><span class="p">(</span><span class="n">str</span><span class="p">.</span><span class="n">Trim</span><span class="p">().</span><span class="n">ToLower</span><span class="p">(),</span> <span class="s">@&#34;\s+&#34;</span><span class="p">,</span> <span class="s">&#34;-&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Convert string to Title Case to use as title for post.</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">string</span> <span class="n">ToTitleCase</span><span class="p">(</span><span class="k">this</span> <span class="kt">string</span> <span class="n">str</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">words</span> <span class="p">=</span> <span class="n">str</span><span class="p">.</span><span class="n">Split</span><span class="p">(</span><span class="sc">&#39; &#39;</span><span class="p">,</span> <span class="n">StringSplitOptions</span><span class="p">.</span><span class="n">RemoveEmptyEntries</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="p">&lt;</span> <span class="n">words</span><span class="p">.</span><span class="n">Length</span><span class="p">;</span> <span class="n">i</span><span class="p">++)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">w</span> <span class="p">=</span> <span class="n">words</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">            <span class="n">words</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="p">=</span> <span class="kt">char</span><span class="p">.</span><span class="n">ToUpper</span><span class="p">(</span><span class="n">w</span><span class="p">[</span><span class="m">0</span><span class="p">])</span> <span class="p">+</span> <span class="n">w</span><span class="p">.</span><span class="n">Substring</span><span class="p">(</span><span class="m">1</span><span class="p">,</span> <span class="n">w</span><span class="p">.</span><span class="n">Length</span> <span class="p">-</span> <span class="m">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kt">string</span><span class="p">.</span><span class="n">Join</span><span class="p">(</span><span class="sc">&#39; &#39;</span><span class="p">,</span> <span class="n">words</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>And here&rsquo;s a few examples using the above methods:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">post</span> <span class="p">=</span> <span class="s">&#34;Here we go again, with another .NET release.&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">excerpt</span> <span class="p">=</span> <span class="n">post</span><span class="p">.</span><span class="n">ToExcerpt</span><span class="p">(</span><span class="m">10</span><span class="p">);</span>  <span class="c1">// &#34;Here we go...&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">str</span> <span class="p">=</span> <span class="s">&#34;using new extension members&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">slug</span> <span class="p">=</span> <span class="n">str</span><span class="p">.</span><span class="n">ToSlug</span><span class="p">();</span>           <span class="c1">// &#34;using-new-extension-members&#34;</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">title</span> <span class="p">=</span> <span class="n">str</span><span class="p">.</span><span class="n">ToTitleCase</span><span class="p">();</span>     <span class="c1">// &#34;Using New Extension Members&#34;</span></span></span></code></pre></div></div>
<p>The first parameter to any extension method is an instance of whatever class is being &ldquo;extended&rdquo; (i.e. <code>this string str</code> above), so there&rsquo;s no such thing as methods that extend a class <em>without</em> needing an instance. Until now&hellip;</p>

<h2 class="relative group">Extension Members
    <div id="extension-members" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#extension-members" aria-label="Anchor">#</a>
    </span>
    
</h2>

<h3 class="relative group">Instance Methods
    <div id="instance-methods" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#instance-methods" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>To convert the above to use the new <a href="https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods"  target="_blank" rel="noreferrer">extension members</a> feature:</p>
<ol>
<li>Remove <code>this string str</code> from each method,</li>
<li>Nest everything inside an extension block that defines <code>str</code> as the extension (aka receiver) parameter, and</li>
<li>Remove <code>static</code> from everything except the class definition.</li>
</ol>
<p>The <code>string str</code> variable is then available to all of the (instance) methods defined inside the curly braces of the extension block:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="k">class</span> <span class="nc">BlogStringHelpers</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">extension</span><span class="p">(</span><span class="kt">string</span> <span class="n">str</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Truncate post for summary, as for use in social media.</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kt">string</span> <span class="n">ToExcerpt</span><span class="p">(</span><span class="kt">int</span> <span class="n">maxLength</span> <span class="p">=</span> <span class="m">128</span><span class="p">)</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="n">str</span><span class="p">.</span><span class="n">Length</span> <span class="p">&lt;=</span> <span class="n">maxLength</span> <span class="p">?</span> <span class="n">str</span> <span class="p">:</span> <span class="s">$&#34;{str[..maxLength]}...&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Convert string to a slug to use as identifier for post.</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kt">string</span> <span class="n">ToSlug</span><span class="p">()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="n">Regex</span><span class="p">.</span><span class="n">Replace</span><span class="p">(</span><span class="n">str</span><span class="p">.</span><span class="n">Trim</span><span class="p">().</span><span class="n">ToLower</span><span class="p">(),</span> <span class="s">@&#34;\s+&#34;</span><span class="p">,</span> <span class="s">&#34;-&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Convert string to Title Case to use as title for post.</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kt">string</span> <span class="n">ToTitleCase</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">words</span> <span class="p">=</span> <span class="n">str</span><span class="p">.</span><span class="n">Split</span><span class="p">(</span><span class="sc">&#39; &#39;</span><span class="p">,</span> <span class="n">StringSplitOptions</span><span class="p">.</span><span class="n">RemoveEmptyEntries</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="p">&lt;</span> <span class="n">words</span><span class="p">.</span><span class="n">Length</span><span class="p">;</span> <span class="n">i</span><span class="p">++)</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="kt">var</span> <span class="n">w</span> <span class="p">=</span> <span class="n">words</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">                <span class="n">words</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="p">=</span> <span class="kt">char</span><span class="p">.</span><span class="n">ToUpper</span><span class="p">(</span><span class="n">w</span><span class="p">[</span><span class="m">0</span><span class="p">])</span> <span class="p">+</span> <span class="n">w</span><span class="p">.</span><span class="n">Substring</span><span class="p">(</span><span class="m">1</span><span class="p">,</span> <span class="n">w</span><span class="p">.</span><span class="n">Length</span> <span class="p">-</span> <span class="m">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kt">string</span><span class="p">.</span><span class="n">Join</span><span class="p">(</span><span class="sc">&#39; &#39;</span><span class="p">,</span> <span class="n">words</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The output of the above members is the same as the extension methods, but let&rsquo;s take a look at what else we can do that we <em>couldn&rsquo;t</em> do before.</p>

<h3 class="relative group">Instance Properties
    <div id="instance-properties" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#instance-properties" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Aside from instance <em>methods</em> (like above), we can also define instance <em>properties</em>:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="k">class</span> <span class="nc">BlogStringHelpers</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">extension</span><span class="p">(</span><span class="kt">string</span> <span class="n">str</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kt">bool</span> <span class="n">IsTitleTooLong</span> <span class="p">=&gt;</span> <span class="n">str</span><span class="p">.</span><span class="n">Length</span> <span class="p">&gt;</span> <span class="m">100</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kt">bool</span> <span class="n">IsTitleAlphanumeric</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="n">str</span><span class="p">.</span><span class="n">All</span><span class="p">(</span><span class="n">c</span> <span class="p">=&gt;</span> <span class="kt">char</span><span class="p">.</span><span class="n">IsLetterOrDigit</span><span class="p">(</span><span class="n">c</span><span class="p">)</span> <span class="p">||</span> <span class="kt">char</span><span class="p">.</span><span class="n">IsWhiteSpace</span><span class="p">(</span><span class="n">c</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span></span></span></code></pre></div></div>
<p>Calling them is as straightforward as calling the methods:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">str</span> <span class="p">=</span> <span class="s">&#34;Introducing C# 14&#34;</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">isTooLong</span> <span class="p">=</span> <span class="n">str</span><span class="p">.</span><span class="n">IsTitleTooLong</span><span class="p">;</span>             <span class="c1">// false</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">isTitleAlphaNum</span> <span class="p">=</span> <span class="n">str</span><span class="p">.</span><span class="n">IsTitleAlphanumeric</span><span class="p">;</span>  <span class="c1">// false</span></span></span></code></pre></div></div>
<p>This works because the instance of whatever type we&rsquo;re extending is defined in the extension container and available to the properties. Let&rsquo;s see what else we can do.</p>

<h3 class="relative group">Static Methods and Properties
    <div id="static-methods-and-properties" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#static-methods-and-properties" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>We can also define static methods and properties, to extend a type without requiring a particular instance of it. This is something we couldn&rsquo;t do with extension methods, as the first parameter was <em>always</em> the instance itself.</p>
<p>All that&rsquo;s needed in the extension block container is the type we&rsquo;re extending, in this case the <code>DateTime</code> struct. It already has <code>Today</code>, of course, but what if I wrote an app that needed to use yesterday&rsquo;s date in a bunch of places? Might be convenient to effectively add that to <code>DateTime</code>:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="k">class</span> <span class="nc">DateTimeHelpers</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">extension</span><span class="p">(</span><span class="n">DateTime</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kd">static</span> <span class="n">DateTime</span> <span class="n">Yesterday</span> <span class="p">=&gt;</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">.</span><span class="n">AddDays</span><span class="p">(-</span><span class="m">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kd">static</span> <span class="n">DateTime</span> <span class="n">Tomorrow</span> <span class="p">=&gt;</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">.</span><span class="n">AddDays</span><span class="p">(</span><span class="m">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kd">static</span> <span class="kt">string</span> <span class="n">Weekday</span> <span class="p">=&gt;</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">.</span><span class="n">DayOfWeek</span><span class="p">.</span><span class="n">ToString</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kd">static</span> <span class="kt">bool</span> <span class="n">IsTodayTomorrow</span> <span class="p">=&gt;</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kd">static</span> <span class="kt">string</span> <span class="n">GetDailyHoroscope</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="p">=&gt;</span> <span class="s">&#34;Carp diem.. seize the fish! Or is it fish the day? 🐟&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>All of these static properties and methods can be called as if they&rsquo;re part of <code>DateTime</code> and, unlike the older extension methods, we don&rsquo;t need to have an instance of a date first.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">DateTime</span><span class="p">.</span><span class="n">Yesterday</span><span class="p">);</span>            <span class="c1">// 12/1/2025 4:22:06 PM</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">DateTime</span><span class="p">.</span><span class="n">Tomorrow</span><span class="p">);</span>             <span class="c1">// 12/3/2025 4:22:06 PM</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">DateTime</span><span class="p">.</span><span class="n">Weekday</span><span class="p">);</span>              <span class="c1">// Tuesday</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">DateTime</span><span class="p">.</span><span class="n">IsTodayTomorrow</span><span class="p">);</span>      <span class="c1">// False</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">DateTime</span><span class="p">.</span><span class="n">GetDailyHoroscope</span><span class="p">());</span>  <span class="c1">// Carp diem.. seize the fish! Or is it fish the day? 🐟</span></span></span></code></pre></div></div>

<h3 class="relative group">Operators
    <div id="operators" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#operators" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>We can also define operators. For example, the <code>DateTime</code> struct already defines a few operators for manipulating dates, but what if we wanted to add a new one to add days to a given date?</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="n">DateTime</span> <span class="kd">operator</span> <span class="p">+(</span><span class="n">DateTime</span> <span class="n">d</span><span class="p">,</span> <span class="n">TimeSpan</span> <span class="n">t</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="n">DateTime</span> <span class="kd">operator</span> <span class="p">-(</span><span class="n">DateTime</span> <span class="n">d</span><span class="p">,</span> <span class="n">TimeSpan</span> <span class="n">t</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="n">TimeSpan</span> <span class="kd">operator</span> <span class="p">-(</span><span class="n">DateTime</span> <span class="n">d1</span><span class="p">,</span> <span class="n">DateTime</span> <span class="n">d2</span><span class="p">)</span></span></span></code></pre></div></div>
<p>We could define a new operator that takes days instead of a <code>TimeSpan</code>, like this:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="k">class</span> <span class="nc">DateTimeHelpers</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">extension</span><span class="p">(</span><span class="n">DateTime</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kd">static</span> <span class="n">DateTime</span> <span class="kd">operator</span> <span class="p">+(</span><span class="n">DateTime</span> <span class="n">dt</span><span class="p">,</span> <span class="kt">int</span> <span class="n">days</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="p">=&gt;</span> <span class="n">dt</span><span class="p">.</span><span class="n">AddDays</span><span class="p">(</span><span class="n">days</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">added</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span> <span class="p">+</span> <span class="m">5</span><span class="p">;</span>  <span class="c1">// 12/7/2025 12:55:29 PM</span></span></span></code></pre></div></div>
<p>Of course, I wouldn&rsquo;t recommend using this <em>exact</em> code, since it&rsquo;s not obvious whether <code>5</code> represents days, years, or something else, but the point is that you can do things like this if you have a use for them!</p>

<h2 class="relative group">Everything All At Once
    <div id="everything-all-at-once" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#everything-all-at-once" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Finally, we can also define extension methods in the same class with extension members, as well as other things. Here&rsquo;s a class that&rsquo;s doing bits of everything we talked about above and more:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="k">class</span> <span class="nc">DateTimeHelpers</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="k">readonly</span> <span class="n">DateTime</span> <span class="n">Yesterday</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Static Constructor, because why not..</span>
</span></span><span class="line"><span class="cl">    <span class="kd">static</span> <span class="n">DateTimeHelpers</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Yesterday</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">Yesterday</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Simple Method</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">string</span> <span class="n">GetStats</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">i</span> <span class="p">=</span> <span class="k">typeof</span><span class="p">(</span><span class="n">DateTimeHelpers</span><span class="p">).</span><span class="n">GetMethods</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">BindingFlags</span><span class="p">.</span><span class="n">Static</span> <span class="p">|</span> <span class="n">BindingFlags</span><span class="p">.</span><span class="n">Public</span><span class="p">).</span><span class="n">Length</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s">$&#34;{nameof(DateTimeHelpers)} has {i} properties.&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Extension Method</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">int</span> <span class="n">GetPreviousLeapYear</span><span class="p">(</span><span class="k">this</span> <span class="n">DateTime</span> <span class="n">dt</span><span class="p">)</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Enumerable</span><span class="p">.</span><span class="n">Range</span><span class="p">(</span><span class="n">dt</span><span class="p">.</span><span class="n">Year</span> <span class="p">-</span> <span class="m">4</span><span class="p">,</span> <span class="m">4</span><span class="p">).</span><span class="n">Single</span><span class="p">(</span><span class="n">DateTime</span><span class="p">.</span><span class="n">IsLeapYear</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Extension Members</span>
</span></span><span class="line"><span class="cl">    <span class="n">extension</span><span class="p">(</span><span class="n">DateTime</span> <span class="n">dt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Static Method</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kd">static</span> <span class="kt">string</span> <span class="n">GetDailyHoroscope</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="p">=&gt;</span> <span class="s">&#34;Carpe die.. seize the dice! 🎲🎲&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Static Properties</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kd">static</span> <span class="n">DateTime</span> <span class="n">Yesterday</span> <span class="p">=&gt;</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">.</span><span class="n">AddDays</span><span class="p">(-</span><span class="m">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kd">static</span> <span class="n">DateTime</span> <span class="n">Tomorrow</span> <span class="p">=&gt;</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">.</span><span class="n">AddDays</span><span class="p">(</span><span class="m">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Instance Method</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kt">int</span> <span class="n">GetNextLeapYear</span><span class="p">()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="n">Enumerable</span><span class="p">.</span><span class="n">Range</span><span class="p">(</span><span class="n">dt</span><span class="p">.</span><span class="n">Year</span> <span class="p">+</span> <span class="m">1</span><span class="p">,</span> <span class="m">4</span><span class="p">).</span><span class="n">Single</span><span class="p">(</span><span class="n">DateTime</span><span class="p">.</span><span class="n">IsLeapYear</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Instance Properties</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="n">DateTime</span> <span class="n">NextWeek</span> <span class="p">=&gt;</span> <span class="n">dt</span><span class="p">.</span><span class="n">AddDays</span><span class="p">(</span><span class="m">7</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kt">bool</span> <span class="n">IsFutureDate</span> <span class="p">=&gt;</span> <span class="n">dt</span> <span class="p">&gt;</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h2 class="relative group">Thoughts
    <div id="thoughts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#thoughts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Having not actually used them in a prod environment yet, outside of the post here, I like them so far. I like that it makes things a little more DRY by defining the variable once in the extension container, and I like having the ability to define extension <em>properties</em> now too. I also like that it makes a class a bit more organized by keeping the extension methods and properties grouped inside the extension block.</p>
<p>Will I use this a lot in the future? I&rsquo;m not sure.. what do you think? Do you have any good uses for this in mind?</p>

<h2 class="relative group">Learn More
    <div id="learn-more" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#learn-more" aria-label="Anchor">#</a>
    </span>
    
</h2>
<ul>
<li><a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/extension"  target="_blank" rel="noreferrer">Extension member declarations - C# reference | Microsoft Learn</a></li>
<li><a href="https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods"  target="_blank" rel="noreferrer">Extension members - C# | Microsoft Learn</a></li>
</ul>
]]></content:encoded><media:content url="https://grantwinney.com/csharp-extension-members/feature.webp" medium="image" type="image/webp"/></item><item><title>New (old) Beginnings</title><link>https://grantwinney.com/new-old-beginnings/</link><pubDate>Thu, 27 Nov 2025 08:39:00 +0000</pubDate><guid>https://grantwinney.com/new-old-beginnings/</guid><description>A month into a new job, and a return to a company I was at 20 years ago, there&amp;rsquo;s a lot to be thankful for.</description><content:encoded><![CDATA[<p>It&rsquo;s Thanksgiving in the US, and one of the many things I&rsquo;m thankful for this year is to be back where I started my career nearly 20 years ago, working at a company with a product people need, that rewards hard work, embraces new tech, and provides opportunities to learn and grow. The only reason I left was that I knew I wanted to be a programmer, but I was very green and there weren&rsquo;t any positions I qualified for. I tried for a year, then left, got the experience I needed, and returned. I can&rsquo;t say that was always my plan, but I&rsquo;m glad it worked out the way it did.</p>
<p>The developer I am now, though, is a result of all the places, environments, and people I&rsquo;ve worked with over the years, good and bad, and today&rsquo;s as good a day as any to reflect on the experiences and lessons I&rsquo;m thankful for.</p>
<p><strong>Agile / Scrum</strong> - It&rsquo;s slightly different everywhere, but I like that it makes visible the invisible (through kanban boards) and promotes conversation (through standups) where someone might not otherwise speak up about a problem. I&rsquo;m also grateful for a team lead who pushed it before I realized what it was or why anyone would want it, and I&rsquo;m happy for the companies since then where upper management saw the value too.</p>
<p><strong>Pair Programming</strong> - I&rsquo;ve worked in environments with 100% pair programming and others where any amount of pair programming was seen as a waste of resources. It&rsquo;s a useful tool - not <em>all</em> the time but definitely in certain circumstances like mentoring or getting a second set of eyes on a tough problem to carry it through to completion.</p>
<p><strong>CI / Automated Testing</strong> - Whether in <a href="https://www.jenkins.io/"  target="_blank" rel="noreferrer">Jenkins</a>, <a href="https://www.jetbrains.com/teamcity/"  target="_blank" rel="noreferrer">TeamCity</a>, <a href="https://azure.microsoft.com/en-us/products/devops/"  target="_blank" rel="noreferrer">Azure DevOps</a>, or some other tool (I&rsquo;ve gotten to play around with quite a few), I&rsquo;m grateful for the teams that saw the value in making builds an automated, repeatable process, and running tests as a part of it. I was once on a team that manually deployed whatever code was ready on Monday morning, with minimal testing, and I cannot stress enough how poor that experience was for <em>everyone</em> involved. Guaranteed way to make people lose faith in the IT department. 😓</p>
<p><strong>Cross Functional Teams</strong> - Since working on a more traditional team, with devs and QAs and whoever else working in completely separate areas, and I&rsquo;ve come to appreciate since then how well smaller teams with several members representing each concern can work. The last team I was on included some very knowledgeable people who had spent <em>decades</em> learning the system, and I got to work closely with them on multiple projects. It made for much quicker collaboration, rather than tossing something over the wall to a group of people you get to work with very little.</p>
<p><strong>Engaged Managers</strong> - It&rsquo;s been interesting (and sometimes frustrating) to see how different managers perform their jobs. I&rsquo;m thankful for those managers who prioritize 1x1s and get to know their team. I once had a manager who mailed everyone on his team some goodies at home, along with a personal note, during a rough project that involved a lot of overtime and stress.</p>
<p>Some of the nicest people I&rsquo;ve worked with have worked for a company for 20 or 30 years, and I certainly hope I&rsquo;ve found that now. But for those who are still looking, who are unhappy with their current situation for whatever reason, I hope you believe in yourself and your ability to learn something new! It may take awhile, but it&rsquo;ll happen!</p>
<p>Today marks a month of a new (old) beginning for me, of returning to where I started, and of moving out of my comfort zone to learn new things. I&rsquo;m glad I pushed myself, but I&rsquo;m especially thankful others saw something in me and gave me the chance!</p>
]]></content:encoded><media:content url="https://grantwinney.com/new-old-beginnings/feature.webp" medium="image" type="image/webp"/></item><item><title>Pull requests aren't a stamp of approval</title><link>https://grantwinney.com/pull-requests-are-not-a-stamp-of-approval/</link><pubDate>Sun, 02 Nov 2025 01:04:00 +0000</pubDate><guid>https://grantwinney.com/pull-requests-are-not-a-stamp-of-approval/</guid><description>Pull requests are a chance to ask, learn, and make sure that the code being merged is something EVERYONE is comfortable owning.</description><content:encoded><![CDATA[<p>Despite a few side projects and a lot of playing around (like on this blog), the vast majority of the code I&rsquo;ll ever write will be for large projects shared by dozens or even hundreds of developers, all of them contributing to millions of lines written over <em>decades</em>. I&rsquo;ll come along, merge a few changes and add a few more layers to the code cake, and a hundred more developers (who knows, maybe even you?) will add even more in the years to come.</p>
<p>That&rsquo;s the way it is with writing code for large companies, where we don&rsquo;t get to see the beginning or the end. Our changes build on top of other changes, layer on layer, and the bigger the change, the bigger the impact down the line. When one layer has mistakes though, everything around it is tainted too. Wouldn&rsquo;t it be nice if we could check out each other&rsquo;s code <em>before</em> it got added to the mix? <em>Oh wait&hellip;</em></p>
<p>It took me a long time to see the value of <a href="https://grantwinney.com/what-is-a-code-review/"  target="_blank" rel="noreferrer">pull requests</a> (I used to be arrogant about my code - hey, it works on <em>my</em> machine), but now that I do, it&rsquo;s frustrating when I reach out hoping for advice and feedback, only to be given a quick stamp of approval. If I touch a piece of code that Bob the dev worked on last month or a stored proc that Susan the DBA created, and I tag them on a PR, it&rsquo;s <em>not</em> a formality. This process is for <em>everyone&rsquo;s</em> benefit!</p>
<p><strong>What I write today (good, bad, or ugly) is what <em>they</em> inherit and have to work with tomorrow, and vice-versa.</strong></p>
<p>And so, I <em>want</em> their feedback, their constructive criticism, their optimizations. I want to know if I&rsquo;m going wrong and where, and that they&rsquo;re comfortable with what I&rsquo;m about to merge in, because <em>they</em> may be the next person who has to deal with it&hellip; and who knows if I&rsquo;ll be around to explain (or help fix) it when they do!</p>
<p>If you get tagged on one and you&rsquo;re wondering what&rsquo;s the point, remember their code will be yours to deal with tomorrow. So spend a little time, ask some questions, and provide that constructive feedback. You&rsquo;ll be doing everyone a favor! 😉</p>
]]></content:encoded><media:content url="https://grantwinney.com/pull-requests-are-not-a-stamp-of-approval/feature.webp" medium="image" type="image/webp"/></item><item><title>Contact Me</title><link>https://grantwinney.com/contact/</link><pubDate>Sat, 01 Nov 2025 10:14:00 +0000</pubDate><guid>https://grantwinney.com/contact/</guid><description/><content:encoded><![CDATA[<p>I used to have a nice little contact form here, but it was mostly used by people who wanted to help me take my site to the next level. Or write guest posts in exchange for all the clicks. Sigh.</p>
<p>If you&rsquo;d like to reach out, you can:</p>
<ul>
<li>Leave a comment below,</li>
<li>Leave a comment under whatever post brought you here,</li>
<li>Create an issue on <a href="https://github.com/grantwinney"  target="_blank" rel="noreferrer">GitHub</a> if some project led you here,</li>
<li>Send a message on <a href="https://www.linkedin.com/in/grantwinney"  target="_blank" rel="noreferrer">LinkedIn</a> if you&rsquo;re still using it,</li>
<li><a href="https://flypigeon.co/"  target="_blank" rel="noreferrer">Use a carrier pigeon</a>, maybe..? <span class="relative inline-block align-text-bottom icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"/></svg></span></li>
</ul>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/contact/slurp.gif"
    width="585"
      height="33"></figure>
]]></content:encoded><media:content url="https://grantwinney.com/contact/feature.webp" medium="image" type="image/webp"/></item><item><title>A comparison of DateTime to DateTimeOffset</title><link>https://grantwinney.com/csharp-datetime-vs-datetimeoffset/</link><pubDate>Sat, 06 Sep 2025 17:16:00 +0000</pubDate><guid>https://grantwinney.com/csharp-datetime-vs-datetimeoffset/</guid><description>Should you use DateTime or DateTimeOffset? Well, it depends&amp;hellip;</description><content:encoded><![CDATA[<p>For the last 20 years, the .NET Framework (starting with 2.0 in 2005) has had two structures for storing date/time values - <code>DateTime</code> and <code>DateTimeOffset</code>. In the last 15+ years of programming, nearly every instance of any C# code I&rsquo;ve seen dealing with dates and times uses <code>DateTime</code>, and I have no idea why. My guess is that a lot of intro books and official docs used <code>DateTime</code>, since it&rsquo;s a little simpler, and everyone just went with it and didn&rsquo;t look back.</p>
<p>It&rsquo;s important to take a few minutes and understand the difference, so let&rsquo;s take a look at a couple short examples, which you can also <a href="https://dotnetfiddle.net/tyxO1X"  target="_blank" rel="noreferrer">find here</a>.</p>

<h2 class="relative group">A Short Example
    <div id="a-short-example" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#a-short-example" aria-label="Anchor">#</a>
    </span>
    
</h2>

<h3 class="relative group">DateTime
    <div id="datetime" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#datetime" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Here&rsquo;s a short example using three <code>DateTime</code> values. The first one is in the &ldquo;local&rdquo; time zone (but which one is that?), the second one is UTC, and the last one isn&rsquo;t specified&hellip; could be either, who knows.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;=== DATETIME ===&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">dtLocal</span> <span class="p">=</span> <span class="k">new</span> <span class="n">DateTime</span><span class="p">(</span><span class="m">2025</span><span class="p">,</span> <span class="m">9</span><span class="p">,</span> <span class="m">4</span><span class="p">,</span> <span class="m">12</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">DateTimeKind</span><span class="p">.</span><span class="n">Local</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">dtUTC</span> <span class="p">=</span> <span class="k">new</span> <span class="n">DateTime</span><span class="p">(</span><span class="m">2025</span><span class="p">,</span> <span class="m">9</span><span class="p">,</span> <span class="m">4</span><span class="p">,</span> <span class="m">12</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">DateTimeKind</span><span class="p">.</span><span class="n">Utc</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">dtUnk</span> <span class="p">=</span> <span class="k">new</span> <span class="n">DateTime</span><span class="p">(</span><span class="m">2025</span><span class="p">,</span> <span class="m">9</span><span class="p">,</span> <span class="m">4</span><span class="p">,</span> <span class="m">12</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">);</span>  <span class="c1">// DateTimeKind.Unspecified</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Local vs UTC vs Unknown DateTime</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Local: {dtLocal}&#34;</span><span class="p">);</span>      <span class="c1">// 9/4/2025 12:00:00 PM</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;UTC: {dtUTC}&#34;</span><span class="p">);</span>          <span class="c1">// 9/4/2025 12:00:00 PM</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Unspecified: {dtUnk}&#34;</span><span class="p">);</span>  <span class="c1">// 9/4/2025 12:00:00 PM</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Convert between Local and UTC</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Local to UTC: {dtLocal.ToUniversalTime()}&#34;</span><span class="p">);</span>  <span class="c1">// 9/4/2025 4:00:00 PM</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;UTC to Local: {dtUTC.ToLocalTime()}&#34;</span><span class="p">);</span>        <span class="c1">// 9/4/2025 8:00:00 AM</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Unspecified to UTC: {dtUnk.ToUniversalTime()}&#34;</span><span class="p">);</span>  <span class="c1">// 9/4/2025 4:00:00 PM</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Unspecified to Local: {dtUnk.ToLocalTime()}&#34;</span><span class="p">);</span>    <span class="c1">// 9/4/2025 8:00:00 AM</span></span></span></code></pre></div></div>
<p>Printing them all out to the console (lines 8-10) says nothing about which time zone the <code>DateTime</code> was recorded in. Still, it&rsquo;s tracking local vs UTC internally via the <code>DateKind</code> property, and converting the local one to UTC or vice-versa shows that it knows that at least (lines 13-14).</p>
<p>What is &ldquo;local&rdquo; though? Once the value is stored in the database in a <code>DateTime</code> column type, we can&rsquo;t know. Not only do we not know which local time zone it originally represented, we don&rsquo;t even know if it <em>was</em> local, or if it was UTC! That&rsquo;s the catch. We&rsquo;ve lost a critical bit of context.</p>
<p>As for the unspecified one (lines 16-17), it&rsquo;s even less helpful. If you convert it to UTC, it assumes you started with local. If you convert it to local, it assumes you started with UTC. After all, we never told it.</p>

<h3 class="relative group">DateTimeOffset
    <div id="datetimeoffset" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#datetimeoffset" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>And here&rsquo;s a short example using three <code>DateTimeOffset</code> values. The first one is in a specific &ldquo;local&rdquo; time zone (EDT in this case), the second one is UTC, and the last one isn&rsquo;t specified&hellip; which defaults to the current &ldquo;local&rdquo; time zone, so it actually <em>is</em> still known. No ambiguity here.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;=== DATETIMEOFFSET ===&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">dtoLocal</span> <span class="p">=</span> <span class="k">new</span> <span class="n">DateTimeOffset</span><span class="p">(</span><span class="m">2025</span><span class="p">,</span> <span class="m">9</span><span class="p">,</span> <span class="m">4</span><span class="p">,</span> <span class="m">12</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">TimeSpan</span><span class="p">.</span><span class="n">FromHours</span><span class="p">(-</span><span class="m">4</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">dtoUTC</span> <span class="p">=</span> <span class="k">new</span> <span class="n">DateTimeOffset</span><span class="p">(</span><span class="m">2025</span><span class="p">,</span> <span class="m">9</span><span class="p">,</span> <span class="m">4</span><span class="p">,</span> <span class="m">12</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">TimeSpan</span><span class="p">.</span><span class="n">FromHours</span><span class="p">(</span><span class="m">0</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">dtoUnk</span> <span class="p">=</span> <span class="k">new</span> <span class="n">DateTimeOffset</span><span class="p">(</span><span class="n">dtUnk</span><span class="p">);</span>  <span class="c1">// treated as current local time zone</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Local vs UTC vs Unknown DateTimeOffset</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Local: {dtoLocal}&#34;</span><span class="p">);</span>      <span class="c1">// 9/4/2025 12:00:00 PM -04:00</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;UTC: {dtoUTC}&#34;</span><span class="p">);</span>          <span class="c1">// 9/4/2025 12:00:00 PM +00:00</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Unspecified: {dtoUnk}&#34;</span><span class="p">);</span>  <span class="c1">// 9/4/2025 12:00:00 PM -04:00</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Convert between Local and UTC</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Local to UTC: {dtoLocal.ToUniversalTime()}&#34;</span><span class="p">);</span>  <span class="c1">// 9/4/2025 4:00:00 PM +00:00</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;UTC to Local: {dtoUTC.ToLocalTime()}&#34;</span><span class="p">);</span>        <span class="c1">// 9/4/2025 8:00:00 AM -04:00</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Unspecified to UTC: {dtoUnk.ToUniversalTime()}&#34;</span><span class="p">);</span>  <span class="c1">// 9/4/2025 4:00:00 PM +00:00</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Unspecified to Local: {dtoUnk.ToLocalTime()}&#34;</span><span class="p">);</span>    <span class="c1">// 9/4/2025 12:00:00 PM -04:00</span></span></span></code></pre></div></div>
<p>Now, printing local (or unspecified) uses the current local time zone at the time it was defined, and printing UTC uses UTC of course (lines 8-10). Converting between the two (lines 13-14) is consistent, and there&rsquo;s no unspecified or unknown value (lines 16-17) anymore since it&rsquo;s just treated as local too.</p>
<p>Storing these values in a <code>DateTimeOffset</code> column type retains the extra context. So we should use <code>DateTimeOffset</code> <em>everywhere</em>, right? Well, no&hellip;</p>

<h2 class="relative group">Which one do we use?
    <div id="which-one-do-we-use" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#which-one-do-we-use" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Sometimes we need the extra context&hellip;</p>
<ul>
<li>Imagine User A and User B live in time zones <strong>3 hours apart</strong>.</li>
<li>User A uses an app to schedule a meeting for 8am (for User B it&rsquo;s 11am), and it&rsquo;s stored as a local <code>DateTime</code> value.</li>
<li>User B uses the same app to reopen the schedule and the system, having no idea <em>which</em> local time zone it was created in, shows it at 8am instead of 11. Confused, he moves it to 11am, but now User A has it scheduled for 11am as well.</li>
</ul>
<p>One solution is to convert all <code>DateTime</code> values to UTC before saving to the database, and then read them out assuming they&rsquo;re UTC as well. Anyone who&rsquo;s worked in a system that a hundred other people have touched over the course of a couple <em>decades</em> knows that&rsquo;s a fool&rsquo;s dream.</p>
<p>The better solution is to use <code>DateTimeOffset</code> which has time zone built in. You can&rsquo;t <em>not</em> specify a time zone, so User A schedules for 8am PST, and when User B opens it later, the system can see they&rsquo;re in EST and convert it to 11am for him.</p>
<p>But sometimes we <em>don&rsquo;t</em> need that, like when we&rsquo;re talking about a specific point in time that&rsquo;s independent of time zone. If Acme Inc is a nationwide retail chain that always closes at 8pm, and their next inventory is at closing time on Fri Oct 3rd, then maybe there&rsquo;s a <code>NextInventoryDate</code> column in the database somewhere that&rsquo;s set to &ldquo;8/3/2025 20:00:00&rdquo; and that&rsquo;s the same <em>everywhere</em>. No matter what time zone you&rsquo;re in, your store closes at 8pm on Aug 3rd.</p>
<p>And so the answer, as usual, is &ldquo;it depends&rdquo;. <em>(But it seems like <code>DateTimeOffset</code> is better, more often than not.)</em></p>

<h2 class="relative group">References
    <div id="references" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#references" aria-label="Anchor">#</a>
    </span>
    
</h2>
<ul>
<li><a href="https://learn.microsoft.com/en-us/dotnet/api/system.datetime?view=net-9.0"  target="_blank" rel="noreferrer">DateTime Struct (System) | Microsoft Learn</a> (and <a href="https://learn.microsoft.com/en-us/dotnet/api/system.datetime.kind?view=net-9.0"  target="_blank" rel="noreferrer">DateTime.Kind</a>)</li>
<li><a href="https://learn.microsoft.com/en-us/dotnet/api/system.datetimeoffset?view=net-9.0"  target="_blank" rel="noreferrer">DateTimeOffset Struct (System) | Microsoft Learn</a></li>
<li><a href="https://learn.microsoft.com/en-us/dotnet/standard/datetime/choosing-between-datetime"  target="_blank" rel="noreferrer">Compare types related to date and time - .NET | Microsoft Learn</a></li>
</ul>
]]></content:encoded><media:content url="https://grantwinney.com/csharp-datetime-vs-datetimeoffset/feature.webp" medium="image" type="image/webp"/></item><item><title>WebView2, a browser for WinForms in .NET 5</title><link>https://grantwinney.com/webview2-a-browser-for-winforms/</link><pubDate>Fri, 17 Jan 2025 22:41:08 +0000</pubDate><guid>https://grantwinney.com/webview2-a-browser-for-winforms/</guid><description>In .NET 5, WinForms got a WebView2 control for displaying web pages.. even ones we create on-the-fly while the app&amp;rsquo;s running. Let&amp;rsquo;s kick the tires.</description><content:encoded><![CDATA[<p>When one thinks of WinForms, one does <em>not</em> generally think of the web at the same time, unless it&rsquo;s how they wish they could move their app from one to the other. However, there&rsquo;s a number of controls for displaying web pages in a WinForms app, and with .NET 5 we got a new one called <a href="https://learn.microsoft.com/en-us/dotnet/desktop/winforms/whats-new/net50?view=netdesktop-9.0#new-controls"  target="_blank" rel="noreferrer">WebView2</a>.</p>
<blockquote><p>The code in this post is available on <a href="https://github.com/grantwinney/Surviving-WinForms/tree/master/.NET%2005/WebView2Sample"  target="_blank" rel="noreferrer">GitHub</a>, for you to use, extend, or just follow along while you read&hellip; and hopefully discover something new along the way!</p>
</blockquote><p>I spent a couple evenings playing around and barely scratched the surface. Here&rsquo;s what I learned.</p>

<h2 class="relative group">What is WebView2?
    <div id="what-is-webview2" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-is-webview2" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The WebView2 control is, in <a href="https://learn.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.winforms.webview2?view=webview2-dotnet-1.0.2903.40#remarks"  target="_blank" rel="noreferrer">their own words</a>, a wrapper around the <a href="https://aka.ms/webview2"  target="_blank" rel="noreferrer">WebView2 COM API</a>, which in a more general sense &ldquo;allows you to embed web technologies (HTML, CSS, and JavaScript) in your native apps [using] Microsoft Edge as the rendering engine to display the web content&rdquo;. Since <a href="https://support.microsoft.com/en-us/microsoft-edge/download-the-new-microsoft-edge-based-on-chromium-0f4a3dd7-55df-60f5-739f-00010dba52cf"  target="_blank" rel="noreferrer">Edge is based on Chromium</a> (<a href="https://en.wikipedia.org/wiki/Chromium_%28web_browser%29#Browsers_based_on_Chromium"  target="_blank" rel="noreferrer">as are most major browsers</a>), it&rsquo;s effectively a wrapper around Chromium.</p>
<p>Basically, we get a Chromium-based browser in our app, similar to other third-party tools like <a href="https://grantwinney.com/hosting-a-simple-webpage-in-winforms-with-cefsharp/"  target="_blank" rel="noreferrer">CefSharp</a>, <a href="https://teamdev.com/dotnetbrowser/"  target="_blank" rel="noreferrer">DotNetBrowser</a>, etc.</p>

<h2 class="relative group">Initializing WebView2
    <div id="initializing-webview2" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#initializing-webview2" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The WebView2 control is <a href="https://www.nuget.org/packages/Microsoft.Web.WebView2"  target="_blank" rel="noreferrer">available on NuGet</a>. Just fire up a WinForms app, install the package, and (in VS2022 at least) it appears in the &ldquo;Toolbox&rdquo; fairly quickly. From there, we can drag and drop it onto a Form and we&rsquo;re off the races&hellip;</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/webview2-a-browser-for-winforms/webview2_nuget_package.png"
    width="664"
      height="339"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/webview2-a-browser-for-winforms/webview2_winforms.png"
    width="844"
      height="387"></figure>
<p>Adding WebView2 to a WinForms project</p>
<p>After that, the control should be initialized: <em>(maybe, probably.. read on)</em></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="n">Form1</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">InitializeComponent</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">InitializeWebView2</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">async</span> <span class="k">void</span> <span class="n">InitializeWebView2</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="n">webView2</span><span class="p">.</span><span class="n">EnsureCoreWebView2Async</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Nearly everything on the WebView2 control is accessed through a <code>CoreWebView2</code> property. Per the docs, that property is <code>null</code> initially because creating it is an expensive operation, but in a little test app like this we want it right away. By calling <code>EnsureCoreWebView2Async()</code>, the <code>CoreWebView2</code> property is guaranteed to be initialized, so that subsequent calls won&rsquo;t throw a null reference exception.</p>

<h2 class="relative group">Navigate to a Website
    <div id="navigate-to-a-website" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#navigate-to-a-website" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Navigating to a site is super easy - just call the <code>Navigate()</code> method. The only catch is it has to start with &ldquo;https&rdquo; or &ldquo;http&rdquo;:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// Load Microsoft</span>
</span></span><span class="line"><span class="cl"><span class="n">webView2</span><span class="p">.</span><span class="n">CoreWebView2</span><span class="p">.</span><span class="n">Navigate</span><span class="p">(</span><span class="s">&#34;https://microsoft.com&#34;</span><span class="p">);</span></span></span></code></pre></div></div>
<p>Done! lol</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/webview2-a-browser-for-winforms/iABCas3xW1.gif"
    width="741"
      height="429"></figure>

<h2 class="relative group">Load Custom HTML
    <div id="load-custom-html" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#load-custom-html" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Alternatively, we can throw together some HTML on-the-fly (up to 2 MB) and pass that to the control instead, like this:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// Load HTML with inline JS/CSS</span>
</span></span><span class="line"><span class="cl"><span class="n">webView2</span><span class="p">.</span><span class="n">NavigateToString</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="n">html</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="n">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="n">style</span><span class="p">&gt;</span><span class="n">p</span><span class="p">{</span><span class="n">color</span><span class="p">:</span><span class="n">green</span><span class="p">;</span> <span class="n">font</span><span class="p">-</span><span class="n">weight</span><span class="p">:</span><span class="n">bold</span><span class="p">}&lt;/</span><span class="n">style</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="n">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="n">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="n">p</span><span class="p">&gt;</span><span class="n">Not</span> <span class="n">much</span> <span class="n">to</span> <span class="n">see</span> <span class="n">here</span><span class="p">.....&lt;/</span><span class="n">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="n">button</span> <span class="n">onClick</span><span class="p">=</span><span class="s">&#34;alert(&#39;Unbelievable...&#39;)&#34;</span><span class="p">&gt;</span><span class="n">DON</span><span class="err">&#39;</span><span class="n">T</span> <span class="n">PRESS</span> <span class="n">ME</span><span class="p">&lt;/</span><span class="n">button</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="n">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="n">html</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;&#34;&#34;);
</span></span></span></code></pre></div></div>
<p>Or instead of including the CSS and JavaScript in the HTML, we can inject it separately with a call to <code>AddScriptToExecuteOnDocumentCreatedAsync()</code>:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// Define some JS and CSS to add to the page after it loads</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">scriptID</span> <span class="p">=</span> <span class="k">await</span> <span class="n">webView2</span><span class="p">.</span><span class="n">CoreWebView2</span><span class="p">.</span><span class="n">AddScriptToExecuteOnDocumentCreatedAsync</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl">    <span class="n">document</span><span class="p">.</span><span class="n">addEventListener</span><span class="p">(</span><span class="err">&#39;</span><span class="n">readystatechange</span><span class="err">&#39;</span><span class="p">,</span> <span class="n">evt</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">document</span><span class="p">.</span><span class="n">readyState</span> <span class="p">===</span> <span class="err">&#39;</span><span class="n">complete</span><span class="err">&#39;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">document</span><span class="p">.</span><span class="n">getElementById</span><span class="p">(</span><span class="err">&#39;</span><span class="n">btn</span><span class="err">&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="p">.</span><span class="n">addEventListener</span><span class="p">(</span><span class="err">&#39;</span><span class="n">click</span><span class="err">&#39;</span><span class="p">,</span> <span class="n">function</span><span class="p">()</span> <span class="p">{</span><span class="n">alert</span><span class="p">(</span><span class="err">&#39;</span><span class="n">Just</span><span class="p">..</span> <span class="n">why</span><span class="p">?</span><span class="err">&#39;</span><span class="p">)});</span>
</span></span><span class="line"><span class="cl">            <span class="n">document</span><span class="p">.</span><span class="n">head</span><span class="p">.</span><span class="n">insertAdjacentHTML</span><span class="p">(</span><span class="err">&#39;</span><span class="n">beforeend</span><span class="err">&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="err">&#39;</span><span class="p">&lt;</span><span class="n">style</span><span class="p">&gt;</span><span class="n">p</span><span class="p">{</span><span class="n">color</span><span class="p">:</span><span class="n">red</span><span class="p">;</span> <span class="n">font</span><span class="p">-</span><span class="n">style</span><span class="p">:</span><span class="n">italic</span><span class="p">}&lt;/</span><span class="n">style</span><span class="p">&gt;</span><span class="err">&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;&#34;&#34;);
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Load HTML sans JS/CSS</span>
</span></span><span class="line"><span class="cl"><span class="n">webView2</span><span class="p">.</span><span class="n">NavigateToString</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="n">p</span><span class="p">&gt;</span><span class="n">Move</span> <span class="n">along</span><span class="p">..</span> <span class="n">nothing</span> <span class="n">to</span> <span class="n">see</span> <span class="n">here</span><span class="p">...&lt;/</span><span class="n">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="n">button</span> <span class="n">id</span><span class="p">=</span><span class="err">&#39;</span><span class="n">btn</span><span class="err">&#39;</span><span class="p">&gt;</span><span class="n">DON</span><span class="err">&#39;</span><span class="n">T</span> <span class="n">PRESS</span> <span class="n">ME</span><span class="p">&lt;/</span><span class="n">button</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;&#34;&#34;);
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">webView2</span><span class="p">.</span><span class="n">CoreWebView2</span><span class="p">.</span><span class="n">RemoveScriptToExecuteOnDocumentCreated</span><span class="p">(</span><span class="n">scriptID</span><span class="p">);</span></span></span></code></pre></div></div>
<p>The &ldquo;AddScript&hellip;&rdquo; method runs before the HTML has actually been parsed, so I was getting javascript errors aplenty when I tried to access <code>document.head</code>, <code>document.getElementById()</code>, etc. Checking that the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState#interactive"  target="_blank" rel="noreferrer">readyState</a> for the document is &ldquo;complete&rdquo; seems to work. Probably a better way, who knows.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/webview2-a-browser-for-winforms/prjRiSq2H8.gif"
    width="651"
      height="427"></figure>
<p>The last line of code is worth mentioning too, where I call <code>RemoveScriptToExecuteOnDocumentCreated()</code>. Once a script is added, it&rsquo;ll stick around and run again anytime a new document is loaded. To see what I mean, we can add something like this to the <code>InitializeWebView2()</code> method:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// Scripts like this one run on every page load</span>
</span></span><span class="line"><span class="cl"><span class="c1">// until you call RemoveScriptToExecuteOnDocumentCreated()</span>
</span></span><span class="line"><span class="cl"><span class="k">await</span> <span class="n">webView2</span><span class="p">.</span><span class="n">CoreWebView2</span><span class="p">.</span><span class="n">AddScriptToExecuteOnDocumentCreatedAsync</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl">    <span class="n">document</span><span class="p">.</span><span class="n">addEventListener</span><span class="p">(</span><span class="err">&#39;</span><span class="n">readystatechange</span><span class="err">&#39;</span><span class="p">,</span> <span class="n">evt</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">document</span><span class="p">.</span><span class="n">readyState</span> <span class="p">===</span> <span class="err">&#39;</span><span class="n">complete</span><span class="err">&#39;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">document</span><span class="p">.</span><span class="n">head</span><span class="p">.</span><span class="n">insertAdjacentHTML</span><span class="p">(</span><span class="err">&#39;</span><span class="n">beforeend</span><span class="err">&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="err">&#39;</span><span class="p">&lt;</span><span class="n">style</span><span class="p">&gt;</span><span class="n">body</span><span class="p">,</span><span class="n">div</span> <span class="p">{</span><span class="n">background</span><span class="p">:</span> <span class="n">lightyellow</span> <span class="p">!</span><span class="n">important</span><span class="p">}&lt;/</span><span class="n">style</span><span class="p">&gt;</span><span class="err">&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;&#34;&#34;);
</span></span></span></code></pre></div></div>
<p>Now the body and div on every page loaded has a light yellow background:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/webview2-a-browser-for-winforms/6ft8KXWbZY.gif"
    width="849"
      height="442"></figure>
<p>I remove the script so that it won&rsquo;t load for the next page, but I have no clue what the best practices are around calling that. It seems to leave the scripts loaded as-is on the page just fine, but navigating back and forth breaks things, like the red text below that reverts back to black as I navigate around.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/webview2-a-browser-for-winforms/brWZZ9uwib.gif"
    width="652"
      height="354"></figure>
<p>Like I said, barely scratching the surface here. 😄</p>

<h2 class="relative group">Execute a Script
    <div id="execute-a-script" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#execute-a-script" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>It&rsquo;s super easy to execute a one-off script against the loaded document too:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// Execute a script in the top-level document</span>
</span></span><span class="line"><span class="cl"><span class="n">webView2</span><span class="p">.</span><span class="n">ExecuteScriptAsync</span><span class="p">(</span><span class="s">&#34;alert(&#39;Hi there, hello.&#39;)&#34;</span><span class="p">);</span></span></span></code></pre></div></div>
<p>No matter what page is loaded, it works the same:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/webview2-a-browser-for-winforms/7RTQUVLUsM.gif"
    width="703"
      height="427"></figure>

<h2 class="relative group">Send Messages Back and Forth
    <div id="send-messages-back-and-forth" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#send-messages-back-and-forth" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>It&rsquo;s also possible to communicate between the loaded website and the <code>Form</code>. We can subscribe to both events in the <code>InitializeWebView2()</code> method, one a WinForms event, and the other a JavaScript event listener:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">async</span> <span class="k">void</span> <span class="n">InitializeWebView2</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="n">webView2</span><span class="p">.</span><span class="n">EnsureCoreWebView2Async</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Handle messages from WinForms to the WebView2</span>
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="n">webView2</span><span class="p">.</span><span class="n">CoreWebView2</span><span class="p">.</span><span class="n">AddScriptToExecuteOnDocumentCreatedAsync</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;window.chrome.webview.addEventListener(&#39;message&#39;, evt =&gt; alert(evt.data));&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Handle messages from the WebView2 to WinForms</span>
</span></span><span class="line"><span class="cl">    <span class="n">webView2</span><span class="p">.</span><span class="n">WebMessageReceived</span> <span class="p">+=</span> <span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="n">MessageBox</span><span class="p">.</span><span class="n">Show</span><span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">TryGetWebMessageAsString</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Now we can create some C# code that&rsquo;ll send a message to our website&hellip;</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// See the &#34;InitializeWebView2()&#34; method, where we tell the WebView2 control</span>
</span></span><span class="line"><span class="cl"><span class="c1">// what to do with messages we send to it... display them in an alert box</span>
</span></span><span class="line"><span class="cl"><span class="n">webView2</span><span class="p">.</span><span class="n">CoreWebView2</span><span class="p">.</span><span class="n">PostWebMessageAsString</span><span class="p">(</span><span class="n">txtMsgToSendToHtml</span><span class="p">.</span><span class="n">Text</span><span class="p">);</span></span></span></code></pre></div></div>
<p>&hellip; and some JS that&rsquo;ll send messages back to our Form:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// Add a button that, upon clicking, sends a message to WinForms</span>
</span></span><span class="line"><span class="cl"><span class="n">webView2</span><span class="p">.</span><span class="n">NavigateToString</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="n">html</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="n">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="n">style</span><span class="p">&gt;</span><span class="n">button</span> <span class="p">{</span><span class="n">color</span><span class="p">:</span><span class="n">blue</span><span class="p">;</span> <span class="n">font</span><span class="p">-</span><span class="n">weight</span><span class="p">:</span><span class="n">bold</span><span class="p">}&lt;/</span><span class="n">style</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="n">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="n">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="n">p</span><span class="p">&gt;</span><span class="n">Press</span> <span class="n">the</span> <span class="n">button</span> <span class="n">to</span> <span class="n">send</span> <span class="n">a</span> <span class="n">message</span> <span class="n">to</span> <span class="n">the</span> <span class="n">Form</span><span class="p">,</span> <span class="n">which</span> <span class="n">will</span> <span class="n">display</span> <span class="n">it</span> <span class="k">in</span> <span class="n">a</span> <span class="n">MessageBox</span><span class="p">.&lt;/</span><span class="n">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="n">input</span> <span class="n">type</span><span class="p">=</span><span class="s">&#34;text&#34;</span> <span class="n">id</span><span class="p">=</span><span class="s">&#34;user_msg&#34;</span> <span class="k">value</span><span class="p">=</span><span class="s">&#34;Type your message here...&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="n">button</span> <span class="n">onclick</span><span class="p">=</span><span class="s">&#34;window.chrome.webview.postMessage(document.getElementById(&#39;user_msg&#39;).value);&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="n">SEND</span> <span class="n">MESSAGE</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;/</span><span class="n">button</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="n">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="n">html</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="p">);</span></span></span></code></pre></div></div>
<p>Now we can send messages back and forth:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/webview2-a-browser-for-winforms/HoBNkPM6yv.gif"
    width="949"
      height="391"></figure>

<h2 class="relative group">Subscribe to Events
    <div id="subscribe-to-events" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#subscribe-to-events" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There&rsquo;s all kinds of events we can subscribe to, like when navigation has started or completed, or the source in the control has changed (which is how I&rsquo;m changing the text at the bottom of the Form in all the above images).</p>
<p>If you want the usual browser buttons, you can subscribe to those too:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">btnGoBack</span><span class="p">.</span><span class="n">Click</span> <span class="p">+=</span> <span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">webView2</span><span class="p">.</span><span class="n">CoreWebView2</span><span class="p">.</span><span class="n">GoBack</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="n">btnGoFwd</span><span class="p">.</span><span class="n">Click</span> <span class="p">+=</span> <span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">webView2</span><span class="p">.</span><span class="n">CoreWebView2</span><span class="p">.</span><span class="n">GoForward</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="n">btnReload</span><span class="p">.</span><span class="n">Click</span> <span class="p">+=</span> <span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">webView2</span><span class="p">.</span><span class="n">CoreWebView2</span><span class="p">.</span><span class="n">Reload</span><span class="p">();</span></span></span></code></pre></div></div>
<p>I think that&rsquo;s enough for now though!</p>

<h2 class="relative group">Learn More
    <div id="learn-more" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#learn-more" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Microsoft has quite a bit of documentation to sift through, some of which I already linked to above. There&rsquo;s tons of info on <a href="https://learn.microsoft.com/en-us/microsoft-edge/webview2/landing/"  target="_blank" rel="noreferrer">WebView2</a> in general, <a href="https://learn.microsoft.com/en-us/microsoft-edge/webview2/get-started/winforms"  target="_blank" rel="noreferrer">WebView2 in WinForms</a> specifically, and a <a href="https://github.com/MicrosoftEdge/WebView2Feedback/discussions?discussions_q=is%3Aopen&#43;winform"  target="_blank" rel="noreferrer">discussion forum on GitHub</a> if you just need to vent.</p>
<p>If you found this content useful and would like to learn more, check out my <a href="https://github.com/grantwinney/surviving-winforms"  target="_blank" rel="noreferrer">Surviving WinForms</a> repo, where you&rsquo;ll find links to plenty more blog posts and practical examples!</p>
]]></content:encoded><media:content url="https://grantwinney.com/webview2-a-browser-for-winforms/feature.webp" medium="image" type="image/webp"/></item><item><title>TaskDialog, a new message box for WinForms in .NET 5</title><link>https://grantwinney.com/using-taskdialog-in-winforms/</link><pubDate>Tue, 31 Dec 2024 23:41:30 +0000</pubDate><guid>https://grantwinney.com/using-taskdialog-in-winforms/</guid><description>In .NET 5, WinForms got a major upgrade to the MessageBox called TaskDialog. It&amp;rsquo;s way more flexible and powerful - let&amp;rsquo;s check it out!</description><content:encoded><![CDATA[<p>Since the earliest versions of .NET, the <a href="https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.messagebox"  target="_blank" rel="noreferrer">MessageBox</a> class has given WinForms developers a way to send notifications <em>(usually alerts and warnings)</em> to users. It&rsquo;s always been a very limited control though. Besides the message itself, we can change the icon and choose from a few different button combinations, but that&rsquo;s about it.</p>
<p>In <a href="https://learn.microsoft.com/en-us/dotnet/desktop/winforms/whats-new/net50?view=netdesktop-9.0#new-controls"  target="_blank" rel="noreferrer">.NET 5</a>, we got a new control called <a href="https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.taskdialog"  target="_blank" rel="noreferrer">TaskDialog</a> that allows for much more customization. Instead of one main text area, the UI supports a more complex interface for displaying larger messages. Instead of a few preset button combos, we can create our own.. and even define new buttons.</p>
<blockquote><p>The code in this post is available on <a href="https://github.com/grantwinney/Surviving-WinForms/tree/master/.NET%2005/TaskDialogSample"  target="_blank" rel="noreferrer">GitHub</a>, for you to use, extend, or just follow along while you read&hellip; and hopefully discover something new along the way!</p>
</blockquote><p>It&rsquo;s great that the team behind WinForms has the bandwidth to add new features, not just bandaid old bugs. Let&rsquo;s compare the two controls and take a look at what we can do with <code>TaskDialog</code>.</p>

<h2 class="relative group">Replacing MessageBox
    <div id="replacing-messagebox" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#replacing-messagebox" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Without considering anything fancier, it&rsquo;s super easy to replace a standard <code>MessageBox</code>. Here&rsquo;s a simple message to warn the user when they&rsquo;re about to delete some files:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-taskdialog-in-winforms/image-12.png"
    width="356"
      height="152"></figure>
<p>The code for the above is pretty succinct. We want Yes/No buttons with No as the default:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">result</span> <span class="p">=</span> <span class="n">MessageBox</span><span class="p">.</span><span class="n">Show</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">caption</span><span class="p">:</span> <span class="s">&#34;Continue?&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">text</span><span class="p">:</span> <span class="s">&#34;You&#39;re about to delete the selected files! Continue?&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">icon</span><span class="p">:</span> <span class="n">MessageBoxIcon</span><span class="p">.</span><span class="n">Warning</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">buttons</span><span class="p">:</span> <span class="n">MessageBoxButtons</span><span class="p">.</span><span class="n">YesNo</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">defaultButton</span><span class="p">:</span> <span class="n">MessageBoxDefaultButton</span><span class="p">.</span><span class="n">Button2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">result</span> <span class="p">==</span> <span class="n">DialogResult</span><span class="p">.</span><span class="n">Yes</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// proceed to delete files...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>And here&rsquo;s the a prompt with the same behavior, recreated using <code>TaskDialog</code>:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-taskdialog-in-winforms/image-11.png"
    width="352"
      height="125"></figure>
<p>The code is <em>very</em> similar. We set the properties on a <code>TaskDialogPage</code> object and pass that to the <code>TaskDialog.ShowDialog</code> method:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">result</span> <span class="p">=</span> <span class="n">TaskDialog</span><span class="p">.</span><span class="n">ShowDialog</span><span class="p">(</span><span class="k">new</span> <span class="n">TaskDialogPage</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Caption</span> <span class="p">=</span> <span class="s">&#34;Continue?&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;You&#39;re about to delete the selected files! Continue?&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">Icon</span> <span class="p">=</span> <span class="n">TaskDialogIcon</span><span class="p">.</span><span class="n">Warning</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">Buttons</span> <span class="p">=</span> <span class="p">{</span> <span class="n">TaskDialogButton</span><span class="p">.</span><span class="n">Yes</span><span class="p">,</span> <span class="n">TaskDialogButton</span><span class="p">.</span><span class="n">No</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="n">DefaultButton</span> <span class="p">=</span> <span class="n">TaskDialogButton</span><span class="p">.</span><span class="n">No</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">SizeToContent</span> <span class="p">=</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">result</span> <span class="p">==</span> <span class="n">TaskDialogButton</span><span class="p">.</span><span class="n">Yes</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// proceed to delete files...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>There&rsquo;s a couple interesting things to point out straightaway. Firstly, we&rsquo;ve specified the buttons we wanted <em>separately</em> – we&rsquo;re not constrained by specific combinations of buttons. Secondly, we specified the default button using the <em>name</em> of the button, not the generic <code>Button1</code>, <code>Button2</code>, etc.</p>
<p>What else can we do?</p>

<h2 class="relative group">A Friendlier Error Message
    <div id="a-friendlier-error-message" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#a-friendlier-error-message" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>For the sake of argument, let&rsquo;s say we&rsquo;re working on an app where the decision was made to display unhandled exceptions to the user, with the full stack trace. In the <code>static void Main()</code> method of the <code>Program</code> class, we can subscribe to the <code>ThreadException</code> event to catch all otherwise uncaught exceptions:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">Application</span><span class="p">.</span><span class="n">ThreadException</span> <span class="p">+=</span> <span class="n">Application_ThreadException</span><span class="p">;</span></span></span></code></pre></div></div>
<p>And then the method itself, to just display everything in a <code>MessageBox</code>:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">static</span> <span class="k">void</span> <span class="n">Application_ThreadException</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">ThreadExceptionEventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">MessageBox</span><span class="p">.</span><span class="n">Show</span><span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">Exception</span><span class="p">.</span><span class="n">ToString</span><span class="p">(),</span> <span class="s">&#34;Application Exception&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Here&rsquo;s what we get. Ouch.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-taskdialog-in-winforms/image-13.png"
    width="410"
      height="354"></figure>
<p>With the new <code>TaskDialog</code>, we can do so much more:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">submitError</span> <span class="p">=</span> <span class="k">new</span> <span class="n">TaskDialogButton</span><span class="p">(</span><span class="s">&#34;Submit Error&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">submitError</span><span class="p">.</span><span class="n">Click</span> <span class="p">+=</span> <span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">MessageBox</span><span class="p">.</span><span class="n">Show</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;Thank you for notifying us. We&#39;re on it. Seriously.&#34;</span><span class="p">,</span> <span class="s">&#34;Submit Error&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">TaskDialog</span><span class="p">.</span><span class="n">ShowDialog</span><span class="p">(</span><span class="k">new</span> <span class="n">TaskDialogPage</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Caption</span> <span class="p">=</span> <span class="s">&#34;Application Exception&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">Heading</span> <span class="p">=</span> <span class="s">&#34;An exception has occurred!&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">Text</span> <span class="p">=</span> <span class="n">e</span><span class="p">.</span><span class="n">Exception</span><span class="p">.</span><span class="n">Message</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">Icon</span> <span class="p">=</span> <span class="n">TaskDialogIcon</span><span class="p">.</span><span class="n">Error</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">AllowCancel</span> <span class="p">=</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">Expander</span> <span class="p">=</span> <span class="k">new</span> <span class="n">TaskDialogExpander</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Text</span> <span class="p">=</span> <span class="n">e</span><span class="p">.</span><span class="n">Exception</span><span class="p">.</span><span class="n">StackTrace</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">CollapsedButtonText</span> <span class="p">=</span> <span class="s">&#34;Stack Trace&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="n">Buttons</span> <span class="p">=</span> <span class="p">{</span> <span class="n">TaskDialogButton</span><span class="p">.</span><span class="n">OK</span><span class="p">,</span> <span class="n">submitError</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="n">DefaultButton</span> <span class="p">=</span> <span class="n">TaskDialogButton</span><span class="p">.</span><span class="n">OK</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">SizeToContent</span> <span class="p">=</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span></span></span></code></pre></div></div>
<p>We can hide the details in a collapsed section, so they&rsquo;re out of the way initially. We can add our own buttons for additional actions, like submitting the error. The <code>AllowCancel</code> flag allows them to hit <code>Esc</code> to dismiss the message, and <code>SizeToContent</code> widens the box to support the contents better, so the stack trace text isn&rsquo;t more awkwardly wrapped than necessary.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-taskdialog-in-winforms/m5Y8utQh4u.gif"
    width="568"
      height="379"></figure>

<h2 class="relative group">Accept Our EULA
    <div id="accept-our-eula" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#accept-our-eula" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Or maybe we need users to accept our completely uninvasive, non-creepy EULA before using our app?</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">eulaSummary</span> <span class="p">=</span> <span class="s">&#34;omitted for brevity&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">eulaDetail</span> <span class="p">=</span> <span class="s">&#34;omitted for brevity&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">vcb</span> <span class="p">=</span> <span class="k">new</span> <span class="n">TaskDialogVerificationCheckBox</span><span class="p">(</span><span class="s">&#34;Accept EULA&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">tdp</span> <span class="p">=</span> <span class="k">new</span> <span class="n">TaskDialogPage</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Caption</span> <span class="p">=</span> <span class="s">&#34;Accept EULA&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">Heading</span> <span class="p">=</span> <span class="s">&#34;EULA&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">Text</span> <span class="p">=</span> <span class="n">eulaSummary</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">Footnote</span> <span class="p">=</span> <span class="s">$@&#34;© &lt;a href=&#34;&#34;https://example.org/&#34;&#34;&gt;ShadyBiz LLC&lt;/a&gt;, 2013 - {DateTime.Now.Year}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">Icon</span> <span class="p">=</span> <span class="n">TaskDialogIcon</span><span class="p">.</span><span class="n">ShieldWarningYellowBar</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">AllowCancel</span> <span class="p">=</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">Expander</span> <span class="p">=</span> <span class="k">new</span> <span class="n">TaskDialogExpander</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Text</span> <span class="p">=</span> <span class="n">eulaDetail</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">CollapsedButtonText</span> <span class="p">=</span> <span class="s">&#34;Nothing to see here...&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="n">Buttons</span> <span class="p">=</span> <span class="p">{</span> <span class="k">new</span> <span class="n">TaskDialogButton</span><span class="p">(</span><span class="s">&#34;OK&#34;</span><span class="p">,</span> <span class="kc">false</span><span class="p">)</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="n">SizeToContent</span> <span class="p">=</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">EnableLinks</span> <span class="p">=</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">Verification</span> <span class="p">=</span> <span class="n">vcb</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">vcb</span><span class="p">.</span><span class="n">CheckedChanged</span> <span class="p">+=</span> <span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">tdp</span><span class="p">.</span><span class="n">Buttons</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="n">Enabled</span> <span class="p">=</span> <span class="n">vcb</span><span class="p">.</span><span class="n">Checked</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">tdp</span><span class="p">.</span><span class="n">LinkClicked</span> <span class="p">+=</span> <span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">Process</span><span class="p">.</span><span class="n">Start</span><span class="p">(</span><span class="s">&#34;explorer&#34;</span><span class="p">,</span> <span class="n">e</span><span class="p">.</span><span class="n">LinkHref</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">TaskDialog</span><span class="p">.</span><span class="n">ShowDialog</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="n">tdp</span><span class="p">)</span> <span class="p">==</span> <span class="n">TaskDialogButton</span><span class="p">.</span><span class="n">OK</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// record the user&#39;s acceptance of the EULA</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The <code>Verification</code> property lets us add a custom <code>TaskDialogVerificationCheckBox</code>, and by subscribing to its <code>CheckedChanged</code> event, we can force users to select it before hitting <code>OK</code>. There&rsquo;s an icon option that adds a yellow bar to really grab attention when needed, and a flag that lets us include hyperlinks in the text.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-taskdialog-in-winforms/ffTco6IDxq.gif"
    width="580"
      height="379"></figure>

<h2 class="relative group">Self destruct in&hellip;
    <div id="self-destruct-in" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#self-destruct-in" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Or maybe we want to notify the user that <em><strong>they only have 5 seconds to take some action!!!</strong></em> This doesn&rsquo;t work very well, since once a dialog is displayed, the user must take some action to make it go away. But I want to show off another feature of the <code>TaskDialog</code> control and I&rsquo;m running low on ideas. 😏</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">pb</span> <span class="p">=</span> <span class="k">new</span> <span class="n">TaskDialogProgressBar</span> <span class="p">{</span> <span class="n">Value</span> <span class="p">=</span> <span class="m">5</span><span class="p">,</span> <span class="n">Minimum</span> <span class="p">=</span> <span class="m">0</span><span class="p">,</span> <span class="n">Maximum</span> <span class="p">=</span> <span class="m">5</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">msg</span> <span class="p">=</span> <span class="s">&#34;This message will self-destruct in {0} seconds...&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">tdp</span> <span class="p">=</span> <span class="k">new</span> <span class="n">TaskDialogPage</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Caption</span> <span class="p">=</span> <span class="s">&#34;Final Countdown&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">Text</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Format</span><span class="p">(</span><span class="n">msg</span><span class="p">,</span> <span class="m">5</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">ProgressBar</span> <span class="p">=</span> <span class="n">pb</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">Icon</span> <span class="p">=</span> <span class="n">TaskDialogIcon</span><span class="p">.</span><span class="n">Information</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">tmr</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Timer</span><span class="p">()</span> <span class="p">{</span> <span class="n">Interval</span> <span class="p">=</span> <span class="m">1000</span><span class="p">,</span> <span class="n">Enabled</span> <span class="p">=</span> <span class="kc">true</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="n">tmr</span><span class="p">.</span><span class="n">Tick</span> <span class="p">+=</span> <span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">pb</span><span class="p">.</span><span class="n">Value</span> <span class="p">&gt;</span> <span class="m">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">pb</span><span class="p">.</span><span class="n">Value</span> <span class="p">-=</span> <span class="m">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">tdp</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Format</span><span class="p">(</span><span class="n">msg</span><span class="p">,</span> <span class="n">pb</span><span class="p">.</span><span class="n">Value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">        <span class="n">tdp</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;Boom? ¯\\_(ツ)_/¯&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">tdResult</span> <span class="p">=</span> <span class="n">TaskDialog</span><span class="p">.</span><span class="n">ShowDialog</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="n">tdp</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">tdResult</span> <span class="p">==</span> <span class="n">TaskDialogButton</span><span class="p">.</span><span class="n">OK</span> <span class="p">&amp;&amp;</span> <span class="n">pb</span><span class="p">.</span><span class="n">Value</span> <span class="p">==</span> <span class="m">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// You were warned...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The <code>ProgressBar</code> property lets us add a custom <code>TaskDialogProgressBar</code> to show progress. Here, we&rsquo;ve set it to 5 initially, and then we use a standard WinForms <code>Timer</code> to count down for 5 seconds. I love that we can add a <code>ProgressBar</code>, although a good use-case is escaping me at the moment. Maybe you have one you&rsquo;d like to share?</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-taskdialog-in-winforms/eUKr4NsTRr.gif"
    width="580"
      height="215"></figure>
<p>If you found this content useful and would like to learn more, check out my <a href="https://github.com/grantwinney/surviving-winforms"  target="_blank" rel="noreferrer">Surviving WinForms</a> repo, where you&rsquo;ll find links to plenty more blog posts and practical examples!</p>
]]></content:encoded><media:content url="https://grantwinney.com/using-taskdialog-in-winforms/feature.webp" medium="image" type="image/webp"/></item><item><title>Selecting multiple directories with the FolderBrowserDialog in .NET 9</title><link>https://grantwinney.com/selecting-multiple-directories-with-the-winforms-folderbrowserdialog-in-dotnet/</link><pubDate>Thu, 19 Dec 2024 22:41:10 +0000</pubDate><guid>https://grantwinney.com/selecting-multiple-directories-with-the-winforms-folderbrowserdialog-in-dotnet/</guid><description>One of the smaller updates to make it into .NET 9 for WinForms was allowing multi-selection in the FolderBrowserDialog. Let&amp;rsquo;s see how.</description><content:encoded><![CDATA[<p>It&rsquo;s great that, even after so many years, the teams at Microsoft continue to add updates to their oldest technologies with every .NET release. WinForms recently got a particularly small one, in .NET 9, that allows the FolderBrowserDialog to select multiple directories instead of one, so let&rsquo;s check it out (it won&rsquo;t take long, lol).</p>
<blockquote><p>The code in this post is available on <a href="https://github.com/grantwinney/Surviving-WinForms/tree/master/.NET%2009/FolderBrowserDialogMultiSelect"  target="_blank" rel="noreferrer">GitHub</a>, for you to use, extend, or just follow along while you read&hellip; and hopefully discover something new along the way!</p>
</blockquote>
<h2 class="relative group">The old FolderBrowserDialog (one at a time, please)
    <div id="the-old-folderbrowserdialog-one-at-a-time-please" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-old-folderbrowserdialog-one-at-a-time-please" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The FolderBrowserDialog control has always given us an easy way to select a single folder in an app. After confirming the user pressed OK, we just read in the <code>SelectedPath</code> property and move on with life:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">fbd</span><span class="p">.</span><span class="n">ShowDialog</span><span class="p">()</span> <span class="p">==</span> <span class="n">DialogResult</span><span class="p">.</span><span class="n">OK</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">MessageBox</span><span class="p">.</span><span class="n">Show</span><span class="p">(</span><span class="s">$&#34;The path to process:\n\n{fbd.SelectedPath}&#34;</span><span class="p">);</span></span></span></code></pre></div></div>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/selecting-multiple-directories-with-the-winforms-folderbrowserdialog-in-dotnet/image-8.png"
    width="405"
      height="211"></figure>
<p>There&rsquo;s a lot of other available properties with this control too, but they didn&rsquo;t change so I won&rsquo;t bother with them. They&rsquo;re there though. 😏</p>

<h2 class="relative group">The new FolderBrowserDialog (the more, the merrier)
    <div id="the-new-folderbrowserdialog-the-more-the-merrier" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-new-folderbrowserdialog-the-more-the-merrier" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The only change we have to make with the updated control in .NET 9 is to set the <a href="https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.folderbrowserdialog.multiselect"  target="_blank" rel="noreferrer"><code>Multiselect</code> property</a> to <code>true</code>, either in the designer or at runtime:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">fbd</span><span class="p">.</span><span class="n">Multiselect</span> <span class="p">=</span> <span class="kc">true</span><span class="p">;</span></span></span></code></pre></div></div>
<p>Then we call it the same way but reference the new <a href="https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.folderbrowserdialog.selectedpaths"  target="_blank" rel="noreferrer"><code>SelectedPaths</code> property</a>, which is an array of strings:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">fbd</span><span class="p">.</span><span class="n">ShowDialog</span><span class="p">()</span> <span class="p">==</span> <span class="n">DialogResult</span><span class="p">.</span><span class="n">OK</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">MessageBox</span><span class="p">.</span><span class="n">Show</span><span class="p">(</span><span class="s">$&#34;The path(s) to process:\n\n{string.Join(&#34;</span><span class="err">\</span><span class="n">n</span><span class="s">&#34;, fbd.SelectedPaths)}&#34;</span><span class="p">);</span></span></span></code></pre></div></div>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/selecting-multiple-directories-with-the-winforms-folderbrowserdialog-in-dotnet/image-10.png"
    width="722"
      height="421"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/selecting-multiple-directories-with-the-winforms-folderbrowserdialog-in-dotnet/image-9.png"
    width="546"
      height="280"></figure>
<p>That&rsquo;s it! I can&rsquo;t decide if it&rsquo;s stranger that it never had this capability, or that someone decided to add it now after all these years. Did something else change that made this a priority? Or is someone who&rsquo;s been with WinForms since the very beginning retiring, and this was on their bucket list? Either way, we could only choose one folder at a time before, and now we can choose as many as we&rsquo;d like!</p>
]]></content:encoded><media:content url="https://grantwinney.com/selecting-multiple-directories-with-the-winforms-folderbrowserdialog-in-dotnet/feature.webp" medium="image" type="image/webp"/></item><item><title>How to Use GetStockIcon for WinForms in .NET 8</title><link>https://grantwinney.com/how-to-use-getstockicon-for-winforms/</link><pubDate>Wed, 18 Dec 2024 15:50:31 +0000</pubDate><guid>https://grantwinney.com/how-to-use-getstockicon-for-winforms/</guid><description>Buried deep in the list of .NET 8 improvements for WinForms is the GetStockIcon method. It gives us a way to access stock Windows icons at runtime for the OS the app is running on. Let&amp;rsquo;s check it out.</description><content:encoded><![CDATA[<p>Scouring the features that WinForms got in .NET 8, I found one slipped in near the very bottom of the list under &ldquo;<a href="https://learn.microsoft.com/en-us/dotnet/desktop/winforms/whats-new/net80?view=netdesktop-9.0#miscellaneous-improvements"  target="_blank" rel="noreferrer">miscellaneous improvements</a>&rdquo; called <a href="https://learn.microsoft.com/en-us/dotnet/api/system.drawing.systemicons.getstockicon"  target="_blank" rel="noreferrer">GetStockIcon</a>. It&rsquo;s a new method for grabbing Windows stock icons (i.e. save, folder, etc) at runtime, to use in the UI.</p>
<p>When I&rsquo;ve wanted to add system icons to buttons, toolbars, etc in the past, it usually meant extracting them from shell32.dll, imageres.dll.mun, the <a href="https://www.microsoft.com/en-us/download/details.aspx?id=35825"  target="_blank" rel="noreferrer">Visual Studio Image Library</a>, etc, and then copying them into the project. Then I&rsquo;d add them to an <code>ImageList</code> and hook that up to various UI elements. So I&rsquo;m wondering.. does this new method give us an easier way to use system icons?</p>
<blockquote><p>The code in this post is available on <a href="https://github.com/grantwinney/Surviving-WinForms/tree/master/.NET%2008/GetStockIcon"  target="_blank" rel="noreferrer">GitHub</a>, for you to use, extend, or just follow along while you read&hellip; and hopefully discover something new along the way!</p>
</blockquote>
<h2 class="relative group">Usage
    <div id="usage" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#usage" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The way it&rsquo;s called is simple enough. We just pass a <a href="https://learn.microsoft.com/en-us/dotnet/api/system.drawing.stockiconid"  target="_blank" rel="noreferrer">StockIconId enum</a> value to tell it which icon to retrieve, and then do whatever we like with the icon:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">Icon</span> <span class="n">driveIcon</span> <span class="p">=</span> <span class="n">SystemIcons</span><span class="p">.</span><span class="n">GetStockIcon</span><span class="p">(</span><span class="n">StockIconId</span><span class="p">.</span><span class="n">DriveNet</span><span class="p">);</span></span></span></code></pre></div></div>
<p>Once we have it, we could add it to an <code>ImageList</code> and then use that on a <code>Button</code> or other controls. We could also specify a size in the second parameter, like I did here with a <code>PictureBox</code> to display a larger icon that&rsquo;s still sharp:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// Populate image list with default size, which should be 32 px</span>
</span></span><span class="line"><span class="cl"><span class="n">imageList1</span><span class="p">.</span><span class="n">Images</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="s">&#34;Conn&#34;</span><span class="p">,</span> <span class="n">SystemIcons</span><span class="p">.</span><span class="n">GetStockIcon</span><span class="p">(</span><span class="n">StockIconId</span><span class="p">.</span><span class="n">DriveNet</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="n">imageList1</span><span class="p">.</span><span class="n">Images</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="s">&#34;Disconn&#34;</span><span class="p">,</span> <span class="n">SystemIcons</span><span class="p">.</span><span class="n">GetStockIcon</span><span class="p">(</span><span class="n">StockIconId</span><span class="p">.</span><span class="n">DriveNetDisabled</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">button1</span><span class="p">.</span><span class="n">ImageKey</span> <span class="p">=</span> <span class="s">&#34;Conn&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">pictureBox1</span><span class="p">.</span><span class="n">Image</span> <span class="p">=</span> <span class="n">SystemIcons</span><span class="p">.</span><span class="n">GetStockIcon</span><span class="p">(</span><span class="n">StockIconId</span><span class="p">.</span><span class="n">DriveNet</span><span class="p">,</span> <span class="m">128</span><span class="p">).</span><span class="n">ToBitmap</span><span class="p">();</span></span></span></code></pre></div></div>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/WcaF5EAVqE.gif"
    width="293"
      height="90"></figure>
<p>Or we could loop through <em>all</em> of them, adding each to an <code>ImageList</code>, and then use that to create the world&rsquo;s busiest toolbar: 😏</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">foreach</span> <span class="p">(</span><span class="n">StockIconId</span> <span class="n">icon</span> <span class="k">in</span> <span class="n">Enum</span><span class="p">.</span><span class="n">GetValues</span><span class="p">(</span><span class="k">typeof</span><span class="p">(</span><span class="n">StockIconId</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">    <span class="n">imageList2</span><span class="p">.</span><span class="n">Images</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="n">icon</span><span class="p">.</span><span class="n">ToString</span><span class="p">(),</span> <span class="n">SystemIcons</span><span class="p">.</span><span class="n">GetStockIcon</span><span class="p">(</span><span class="n">icon</span><span class="p">,</span> <span class="m">64</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">toolStrip1</span><span class="p">.</span><span class="n">Items</span><span class="p">.</span><span class="n">AddRange</span><span class="p">(</span><span class="n">imageList2</span><span class="p">.</span><span class="n">Images</span><span class="p">.</span><span class="n">Keys</span><span class="p">.</span><span class="n">Cast</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;().</span><span class="n">Select</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span> <span class="n">ToolStripButton</span><span class="p">(</span><span class="n">imageList2</span><span class="p">.</span><span class="n">Images</span><span class="p">[</span><span class="n">x</span><span class="p">])</span> <span class="p">{</span> <span class="n">ToolTipText</span> <span class="p">=</span> <span class="n">x</span> <span class="p">}).</span><span class="n">ToArray</span><span class="p">());</span></span></span></code></pre></div></div>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/image-6.png"
    width="786"
      height="123"></figure>

<h2 class="relative group">Pros and Cons
    <div id="pros-and-cons" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#pros-and-cons" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The biggest limitation is only being able to call this at runtime. Of course, that&rsquo;s just the nature of this being a method call, but one of the best things about WinForms is its drag-and-drop designer and this definitely works against that. I&rsquo;m not sure how much usage this will get if it means having a designer with blank toolbars, incomplete buttons, etc. Maybe I&rsquo;m missing an obvious use case?</p>
<p>A nice feature, though, is that this method <em>&ldquo;returns icons that are themed for the running version of Windows&rdquo;.</em> If we copy icons into the project, they are what they are, unchanged no matter what version of Windows someone happens to be running. But with this new call, when someone runs our app in a different version of Windows from the one we designed it in, they&rsquo;ll see the icons that are normal for their OS.</p>

<h2 class="relative group">Learning More
    <div id="learning-more" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#learning-more" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you want to learn more, check out the <a href="https://learn.microsoft.com/en-us/dotnet/api/system.drawing.systemicons.getstockicon"  target="_blank" rel="noreferrer">GetStockIcon</a> docs, the <a href="https://learn.microsoft.com/en-us/dotnet/api/system.drawing.stockiconid"  target="_blank" rel="noreferrer">StockIconId</a> enum that lists all the available images, and the <a href="https://learn.microsoft.com/en-us/dotnet/api/system.drawing.stockiconoptions"  target="_blank" rel="noreferrer">StockIconOptions</a> enum that lets us set a few options like adding a link overlay to the image. Since a couple of the options involve resizing the icon, we&rsquo;re not allowed to specify a size <em>and</em> options, but that also means it&rsquo;s not possible to request a larger image that has a link overlay.. seems like an odd choice.</p>
<p>Lastly, since the StockIconId page doesn&rsquo;t include the actual <em>images</em> of the icons, here&rsquo;s a list so you can see what they look like, at least in Windows 11:</p>
<table>
  <thead>
      <tr>
          <th>Name</th>
          <th>Value</th>
          <th>Description</th>
          <th>Image</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>DocumentNoAssociation</td>
          <td>0</td>
          <td>Document (blank page), no associated program.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/DocumentNoAssociation-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>DocumentWithAssociation</td>
          <td>1</td>
          <td>Document with an associated program.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/DocumentWithAssociation-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>Application</td>
          <td>2</td>
          <td>Generic application with no custom icon.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/Application-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>Folder</td>
          <td>3</td>
          <td>Closed folder.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/Folder-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>FolderOpen</td>
          <td>4</td>
          <td>Open folder.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/FolderOpen-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>Drive525</td>
          <td>5</td>
          <td>5.25&quot; floppy disk drive.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/Drive525-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>Drive35</td>
          <td>6</td>
          <td>3.5&quot; floppy disk drive.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/Drive35-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>DriveRemovable</td>
          <td>7</td>
          <td>Removable drive.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/DriveRemovable-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>DriveFixed</td>
          <td>8</td>
          <td>Fixed drive.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/DriveFixed-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>DriveNet</td>
          <td>9</td>
          <td>Network drive.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/DriveNet-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>DriveNetDisabled</td>
          <td>10</td>
          <td>Disabled network drive.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/DriveNetDisabled-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>DriveCD</td>
          <td>11</td>
          <td>CD drive.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/DriveCD-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>DriveRam</td>
          <td>12</td>
          <td>RAM disk drive.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/DriveRam-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>World</td>
          <td>13</td>
          <td>Entire network.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/World-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>Server</td>
          <td>15</td>
          <td>A computer on the network.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/Server-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>Printer</td>
          <td>16</td>
          <td>Printer.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/Printer-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MyNetwork</td>
          <td>17</td>
          <td>My network places.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MyNetwork-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>Find</td>
          <td>22</td>
          <td>Find.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/Find-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>Help</td>
          <td>23</td>
          <td>Help.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/Help-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>Share</td>
          <td>28</td>
          <td>Overlay for shared items.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/Share-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>Link</td>
          <td>29</td>
          <td>Overlay for shortcuts to items.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/Link-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>SlowFile</td>
          <td>30</td>
          <td>Overlay for slow items.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/SlowFile-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>Recycler</td>
          <td>31</td>
          <td>Empty recycle bin.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/Recycler-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>RecyclerFull</td>
          <td>32</td>
          <td>Full recycle bin.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/RecyclerFull-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaCDAudio</td>
          <td>40</td>
          <td>Audio CD media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaCDAudio-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>Lock</td>
          <td>47</td>
          <td>Security lock.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/Lock-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>AutoList</td>
          <td>49</td>
          <td>AutoList.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/AutoList-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>PrinterNet</td>
          <td>50</td>
          <td>Network printer.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/PrinterNet-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>ServerShare</td>
          <td>51</td>
          <td>Server share.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/ServerShare-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>PrinterFax</td>
          <td>52</td>
          <td>Fax printer.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/PrinterFax-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>PrinterFaxNet</td>
          <td>53</td>
          <td>Networked fax printer.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/PrinterFaxNet-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>PrinterFile</td>
          <td>54</td>
          <td>Print to file.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/PrinterFile-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>Stack</td>
          <td>55</td>
          <td>Stack.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/Stack-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaSVCD</td>
          <td>56</td>
          <td>SVCD media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaSVCD-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>StuffedFolder</td>
          <td>57</td>
          <td>Folder containing other items.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/StuffedFolder-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>DriveUnknown</td>
          <td>58</td>
          <td>Unknown drive.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/DriveUnknown-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>DriveDVD</td>
          <td>59</td>
          <td>DVD drive.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/DriveDVD-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaDVD</td>
          <td>60</td>
          <td>DVD media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaDVD-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaDVDRAM</td>
          <td>61</td>
          <td>DVD-RAM media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaDVDRAM-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaDVDRW</td>
          <td>62</td>
          <td>DVD-RW media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaDVDRW-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaDVDR</td>
          <td>63</td>
          <td>DVD-R media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaDVDR-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaDVDROM</td>
          <td>64</td>
          <td>DVD-ROM media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaDVDROM-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaCDAudioPlus</td>
          <td>65</td>
          <td>CD+ (Enhanced CD) media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaCDAudioPlus-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaCDRW</td>
          <td>66</td>
          <td>CD-RW media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaCDRW-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaCDR</td>
          <td>67</td>
          <td>CD-R media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaCDR-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaCDBurn</td>
          <td>68</td>
          <td>Burning CD.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaCDBurn-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaBlankCD</td>
          <td>69</td>
          <td>Blank CD media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaBlankCD-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaCDROM</td>
          <td>70</td>
          <td>CD-ROM media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaCDROM-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>AudioFiles</td>
          <td>71</td>
          <td>Audio files.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/AudioFiles-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>ImageFiles</td>
          <td>72</td>
          <td>Image files.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/ImageFiles-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>VideoFiles</td>
          <td>73</td>
          <td>Video files.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/VideoFiles-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MixedFiles</td>
          <td>74</td>
          <td>Mixed files.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MixedFiles-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>FolderBack</td>
          <td>75</td>
          <td>Folder back.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/FolderBack-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>FolderFront</td>
          <td>76</td>
          <td>Folder front.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/FolderFront-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>Shield</td>
          <td>77</td>
          <td>Security shield. Use for UAC prompts only.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/Shield-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>Warning</td>
          <td>78</td>
          <td>Warning.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/Warning-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>Info</td>
          <td>79</td>
          <td>Informational.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/Info-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>Error</td>
          <td>80</td>
          <td>Error.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/Error-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>Key</td>
          <td>81</td>
          <td>Key / secure.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/Key-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>Software</td>
          <td>82</td>
          <td>Software.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/Software-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>Rename</td>
          <td>83</td>
          <td>Rename.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/Rename-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>Delete</td>
          <td>84</td>
          <td>Delete.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/Delete-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaAudioDVD</td>
          <td>85</td>
          <td>Audio DVD media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaAudioDVD-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaMovieDVD</td>
          <td>86</td>
          <td>Movied DVD media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaMovieDVD-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaEnhancedCD</td>
          <td>87</td>
          <td>Enhanced CD media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaEnhancedCD-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaEnhancedDVD</td>
          <td>88</td>
          <td>Enhanced DVD media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaEnhancedDVD-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaHDDVD</td>
          <td>89</td>
          <td>HD-DVD media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaHDDVD-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaBluRay</td>
          <td>90</td>
          <td>BluRay media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaBluRay-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaVCD</td>
          <td>91</td>
          <td>VCD media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaVCD-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaDVDPlusR</td>
          <td>92</td>
          <td>DVD+R media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaDVDPlusR-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaDVDPlusRW</td>
          <td>93</td>
          <td>DVD+RW media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaDVDPlusRW-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>DesktopPC</td>
          <td>94</td>
          <td>Desktop computer.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/DesktopPC-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MobilePC</td>
          <td>95</td>
          <td>Mobile computer.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MobilePC-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>Users</td>
          <td>96</td>
          <td>Users.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/Users-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaSmartMedia</td>
          <td>97</td>
          <td>Smart media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaSmartMedia-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaCompactFlash</td>
          <td>98</td>
          <td>Compact Flash.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaCompactFlash-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>DeviceCellPhone</td>
          <td>99</td>
          <td>Cell phone.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/DeviceCellPhone-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>DeviceCamera</td>
          <td>100</td>
          <td>Camera.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/DeviceCamera-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>DeviceVideoCamera</td>
          <td>101</td>
          <td>Video camera.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/DeviceVideoCamera-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>DeviceAudioPlayer</td>
          <td>102</td>
          <td>Audio player.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/DeviceAudioPlayer-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>NetworkConnect</td>
          <td>103</td>
          <td>Connect to network.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/NetworkConnect-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>Internet</td>
          <td>104</td>
          <td>Internet.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/Internet-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>ZipFile</td>
          <td>105</td>
          <td>ZIP file.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/ZipFile-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>Settings</td>
          <td>106</td>
          <td>Settings.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/Settings-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>DriveHDDVD</td>
          <td>132</td>
          <td>HD-DVD drive.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/DriveHDDVD-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>DriveBD</td>
          <td>133</td>
          <td>BluRay drive.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/DriveBD-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaHDDVDROM</td>
          <td>134</td>
          <td>HD-DVD-ROM media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaHDDVDROM-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaHDDVDR</td>
          <td>135</td>
          <td>HD-DVD-R media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaHDDVDR-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaHDDVDRAM</td>
          <td>136</td>
          <td>HD-DVD-RAM media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaHDDVDRAM-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaBDROM</td>
          <td>137</td>
          <td>BluRay-ROM media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaBDROM-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaBDR</td>
          <td>138</td>
          <td>BluRay-R media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaBDR-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>MediaBDRE</td>
          <td>139</td>
          <td>BluRay-RE media.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/MediaBDRE-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
      <tr>
          <td>ClusteredDrive</td>
          <td>140</td>
          <td>Clustered disk.</td>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-getstockicon-for-winforms/ClusteredDrive-2.png"
    width="32"
      height="32"></figure>
</td>
      </tr>
  </tbody>
</table>
<p>If you found this content useful, and would like to learn more about a variety of <a href="https://grantwinney.com/tags/csharp/"  target="_blank" rel="noreferrer">C#</a> features, check out my <a href="https://github.com/grantwinney/CSharpDotNetFeatures"  target="_blank" rel="noreferrer">CSharpDotNetFeatures repo</a>, where you&rsquo;ll find links to plenty more blog posts and practical examples!</p>
]]></content:encoded><media:content url="https://grantwinney.com/how-to-use-getstockicon-for-winforms/feature.webp" medium="image" type="image/webp"/></item><item><title>Using Raw String Literals in C# 11 / .NET 7</title><link>https://grantwinney.com/using-raw-string-literals-in-csharp/</link><pubDate>Sat, 14 Dec 2024 22:32:43 +0000</pubDate><guid>https://grantwinney.com/using-raw-string-literals-in-csharp/</guid><description>C# 11 added raw string literals, not a life-altering new feature, but they could be useful in the right circumstances. Let&amp;rsquo;s see how to use them.</description><content:encoded><![CDATA[<p>Some of the many additions to C# and the .NET Framework are huge (like LINQ or async), while others are much smaller. I&rsquo;d definitely place raw string literals in the latter group, but then maybe I&rsquo;m missing something. They&rsquo;re still worth a look though.</p>
<p>Before we dig into them, let&rsquo;s take a brief look at what we had before that, and then see what new things they bring to the table.</p>
<blockquote><p>The code in this post is available on <a href="https://github.com/grantwinney/CSharpDotNetFeatures/tree/master/C%23%2011/RawStringLiterals"  target="_blank" rel="noreferrer">GitHub</a>, for you to use, extend, or just follow along while you read&hellip; and hopefully discover something new along the way!</p>
</blockquote>
<h2 class="relative group">Quoted Strings
    <div id="quoted-strings" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#quoted-strings" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Here&rsquo;s a normal, quoted string. They allow <a href="https://learn.microsoft.com/en-us/dotnet/standard/base-types/character-escapes-in-regular-expressions"  target="_blank" rel="noreferrer">escaped sequences</a> such as tabs, newlines, quotes, etc. If we want a quote in the string, it has to be escaped:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;Windows dir: \t\t \&#34;C:\\Windows\&#34;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Windows dir:             &#34;C:\Windows&#34;</span></span></span></code></pre></div></div>
<p>Normal string allows escape sequences</p>

<h2 class="relative group">Verbatim Strings
    <div id="verbatim-strings" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#verbatim-strings" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Adding the <code>@</code> character to the left of a string causes escaped sequences to be ignored. If we want a quote in the string, it has to be doubled:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">@&#34;Windows dir: \t\t &#34;&#34;C:\Windows&#34;&#34;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Windows dir: \t\t &#34;C:\Windows&#34;</span></span></span></code></pre></div></div>
<p>Verbatim strings ignore escape sequences</p>

<h2 class="relative group">String Interpolation
    <div id="string-interpolation" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#string-interpolation" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I&rsquo;ve written separately about <a href="https://grantwinney.com/using-string-interpolation-to-craft-readable-strings/"  target="_blank" rel="noreferrer">string interpolation</a>, but adding a <code>$</code> to the left of a string allows us to specify variables inline. If we want a curly braces in the string, it has to be doubled. Otherwise it behaves like a normal quoted string.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Today&#39;s date is: \t\t \&#34;</span><span class="p">{{{</span><span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span><span class="p">}}}</span><span class="err">\</span><span class="s">&#34;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Today&#39;s date is:                 &#34;{12/13/2024 9:31:09 PM}&#34;</span></span></span></code></pre></div></div>
<p>String interpolation allows inline variables</p>

<h2 class="relative group">Verbatim String Interpolation
    <div id="verbatim-string-interpolation" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#verbatim-string-interpolation" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I&rsquo;m not sure whether to call this a verbatim interpolated string or an interpolated verbatim string. Or a stringy verbapolation (lol). Inline variables are allowed, and double curly braces are required to show a curly brace, but otherwise they behave like a verbatim string and ignore escaped sequences.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$@&#34;Today&#39;s date is: \t\t &#34;&#34;{{{DateTime.Now}}}&#34;&#34;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Today&#39;s date is: \t\t &#34;{12/13/2024 9:31:09 PM}&#34;</span></span></span></code></pre></div></div>
<p>Verbapolated strings mix up the behaviors</p>

<h2 class="relative group">Raw String Literals (single-line)
    <div id="raw-string-literals-single-line" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#raw-string-literals-single-line" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>By using triple (or more) quotes at the start and end of a string, we&rsquo;ve defined a raw string literal. It behaves mostly like a verbatim string, with some special rules for quotes:</p>
<ul>
<li>They&rsquo;re allowed in the string, as long as they don&rsquo;t occur in sequences longer than whatever we started or ended the string with</li>
<li>They can&rsquo;t butt right up to the quotes that start/end the string (hence the space at the end of the string below)</li>
</ul>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;&#34;&#34;Today&#39;s date is: \t &#34;</span><span class="p">{</span><span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span><span class="p">}</span><span class="s">&#34; &#34;&#34;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Today&#39;s date is: &#34;{DateTime.Now}&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;&#34;&#34;&#34;</span><span class="n">Today</span><span class="err">&#39;</span><span class="n">s</span> <span class="n">date</span> <span class="k">is</span><span class="p">:</span> <span class="err">\</span><span class="n">t</span> <span class="s">&#34;&#34;&#34;{DateTime.Now}&#34;&#34;&#34;</span> <span class="s">&#34;&#34;&#34;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Today&#39;s date is: &#34;&#34;&#34;{DateTime.Now}&#34;&#34;&#34;</span></span></span></code></pre></div></div>
<p>By adding the <code>$</code> string interpolation character, we can insert variables inline again. The main difference is that adding a literal curly brace to the string now requires increasing the number of <code>$</code> at the start of the string. Any number of them <em>less</em> than that in the string is assumed to be a literal brace. I can&rsquo;t figure out why that behavior changed, but I assume they had a reason. 🤷‍♂️</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;&#34;&#34;Today&#39;s date is: \t &#34;</span><span class="p">{</span><span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span><span class="p">}</span><span class="s">&#34; &#34;&#34;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Today&#39;s date is: &#34;12/13/2024 9:31:09 PM&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="err">$</span><span class="s">$&#34;&#34;&#34;Today&#39;s date is: \t &#34;</span><span class="p">{{{</span><span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span><span class="p">}}}</span><span class="s">&#34; &#34;&#34;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Today&#39;s date is: &#34;{12/13/2024 9:31:09 PM}&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="err">$$</span><span class="s">$&#34;&#34;&#34;Today&#39;s {{date}} is: \t &#34;</span><span class="p">{{{{{</span><span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span><span class="p">}}}}}</span><span class="s">&#34; &#34;&#34;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Today&#39;s {{date}} is: &#34;{{12/13/2024 9:31:09 PM}}&#34;</span></span></span></code></pre></div></div>

<h2 class="relative group">Raw String Literals (multi-line)
    <div id="raw-string-literals-multi-line" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#raw-string-literals-multi-line" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Here&rsquo;s where the behavior really changes, although as I alluded to at the beginning of this article, this doesn&rsquo;t seem like a earth-shattering addition to me. Am I missing something?</p>
<p>A raw string literal can span multiple lines, and doesn&rsquo;t require new lines in the string. In fact, they wouldn&rsquo;t be interpreted anyway, since the behavior is largely like a verbatim string.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// Multi-line raw string literals follow the same rules as above;</span>
</span></span><span class="line"><span class="cl"><span class="c1">// None of the lines may appear to the left of the closing set of quotes</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl">    <span class="n">Now</span> <span class="k">this</span> <span class="k">is</span> <span class="n">interesting</span><span class="p">!</span>
</span></span><span class="line"><span class="cl">            <span class="n">Is</span> <span class="k">this</span> <span class="n">indented</span><span class="p">?</span> <span class="err">\</span><span class="n">t</span> <span class="s">&#34;{DateTime.Now}&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;&#34;&#34;);
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Now this is interesting!</span>
</span></span><span class="line"><span class="cl"><span class="c1">//         Is this indented? \t &#34;{DateTime.Now}&#34;</span></span></span></code></pre></div></div>
<p>Raw string literals behave largely like verbatim strings</p>
<p>If we add the <code>$</code> to the start though, then we can add variables inline. And while escaped characters are ignored when placed directly in the string, we can store them in a separate variable and let interpolation do the work for us. That&rsquo;s a rawverbapolated string, for those keeping track.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">tab</span> <span class="p">=</span> <span class="s">$&#34;\t&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s">    Now this is interesting!\n\n\n
</span></span></span><span class="line"><span class="cl"><span class="s">            Is this indented? {tab} &#34;</span><span class="p">{</span><span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span><span class="p">}</span><span class="s">&#34;
</span></span></span><span class="line"><span class="cl">    <span class="s">&#34;&#34;&#34;);
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Now this is interesting!\n\n\n</span>
</span></span><span class="line"><span class="cl"><span class="c1">//         Is this indented?        &#34;12/13/2024 9:44:39 PM&#34;</span></span></span></code></pre></div></div>
<p>We can increase the number of quotes and dollar signs around the string too, just like before, when we have a need to display multiple quotes or literal curly braces inside the raw string literal:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;&#34;&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s">    Now this is interesting!
</span></span></span><span class="line"><span class="cl"><span class="s">            Is this indented? {tab} &#34;&#34;&#34;&#34;{DateTime.Now}&#34;&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s">    &#34;&#34;&#34;&#34;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Now this is interesting!</span>
</span></span><span class="line"><span class="cl"><span class="c1">//         Is this indented?        &#34;&#34;&#34;&#34;12/13/2024 9:44:39 PM&#34;&#34;&#34;&#34;</span></span></span></code></pre></div></div>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="err">$</span><span class="s">$&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s">    Now this is interesting!
</span></span></span><span class="line"><span class="cl"><span class="s">            Is this indented? {{tab}} &#34;</span><span class="p">{{{</span><span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span><span class="p">}}}</span><span class="s">&#34;
</span></span></span><span class="line"><span class="cl">    <span class="s">&#34;&#34;&#34;);
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Now this is interesting!</span>
</span></span><span class="line"><span class="cl"><span class="c1">//         Is this indented?        &#34;{12/13/2024 9:44:39 PM}&#34;</span></span></span></code></pre></div></div>
<p>And finally, we can use raw string literals in a whole lot of cases other than just writing out lines to a console. I can imagine a few, one of them being a multi-line message box like this:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-raw-string-literals-in-csharp/sample-messagebox-with-interpolated-string.png"
    width="313"
      height="159"></figure>

<h2 class="relative group">Learning More
    <div id="learning-more" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#learning-more" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Microsoft has a lot more to say about raw string literals, and strings in general, than what I can post here. They&rsquo;ve spread info out over a few different docs, but if you&rsquo;re interested then they&rsquo;re definitely worth a looksie:</p>
<ul>
<li><a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#raw-string-literals"  target="_blank" rel="noreferrer">What&rsquo;s new in C# 11 | Raw string literals</a></li>
<li><a href="https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/strings/#raw-string-literals"  target="_blank" rel="noreferrer">Strings - C# | Raw string literals</a></li>
<li><a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/reference-types#string-literals"  target="_blank" rel="noreferrer">Built-in reference types - C# reference | String literals</a></li>
<li><a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated#interpolated-raw-string-literals"  target="_blank" rel="noreferrer">$ - string interpolation | Interpolated raw string literals</a></li>
</ul>
<p>If you found this content useful, and would like to learn more about a variety of <a href="https://grantwinney.com/tags/csharp/"  target="_blank" rel="noreferrer">C#</a> features, check out my <a href="https://github.com/grantwinney/CSharpDotNetFeatures"  target="_blank" rel="noreferrer">CSharpDotNetFeatures repo</a>, where you&rsquo;ll find links to plenty more blog posts and practical examples!</p>
]]></content:encoded><media:content url="https://grantwinney.com/using-raw-string-literals-in-csharp/feature.webp" medium="image" type="image/webp"/></item><item><title>Using Primary Constructors with Classes and Structs in C# 12 / .NET 8</title><link>https://grantwinney.com/using-primary-constructors-with-classes-and-structs-in-csharp/</link><pubDate>Fri, 13 Dec 2024 02:05:42 +0000</pubDate><guid>https://grantwinney.com/using-primary-constructors-with-classes-and-structs-in-csharp/</guid><description>As part of C# 12, we got a new feature called primary constructors. Let&amp;rsquo;s see how they work and what we can do with them.</description><content:encoded><![CDATA[<p>The C# and .NET teams are always adding interesting, useful features, more frequently than ever since the norm has become annual releases. It&rsquo;s easy to lose track of <a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history"  target="_blank" rel="noreferrer">all the changes</a> though, and whenever I go back and dig around I usually find something interesting to try out.</p>
<p>One of the features we got as part of C# 12 is called primary constructors, which gives us a different way to define classes and structs. Let&rsquo;s take a closer look at what we can do with them and how they differ from traditional constructors.</p>
<blockquote><p>The code in this post is available on <a href="https://github.com/grantwinney/CSharpDotNetFeatures/tree/master/C%23%2012/PrimaryConstructors"  target="_blank" rel="noreferrer">GitHub</a>, for you to use, extend, or just follow along while you read&hellip; and hopefully discover something new along the way!</p>
</blockquote>
<h2 class="relative group">Using Primary Constructors in Classes
    <div id="using-primary-constructors-in-classes" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#using-primary-constructors-in-classes" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Here&rsquo;s a standard class, representing a satellite. There&rsquo;s a constructor that sets some properties, only one of which (<code>IsActive</code>) can be changed after instantiation: <em>(why? who knows, lol)</em></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">internal</span> <span class="k">class</span> <span class="nc">Satellite</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">init</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Owner</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">init</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">DateOnly</span> <span class="n">LaunchDate</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">init</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">bool</span> <span class="n">IsActive</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Satellite</span><span class="p">(</span><span class="kt">string</span> <span class="n">name</span><span class="p">,</span> <span class="kt">string</span> <span class="n">owner</span><span class="p">,</span> <span class="n">DateOnly</span> <span class="n">launchDate</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">isActive</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Name</span> <span class="p">=</span> <span class="n">name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Owner</span> <span class="p">=</span> <span class="n">owner</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">LaunchDate</span> <span class="p">=</span> <span class="n">launchDate</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">IsActive</span> <span class="p">=</span> <span class="n">isActive</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Writing the above code using a &ldquo;primary constructor&rdquo; instead (Visual Studio will helpfully do the conversion for us), we end up with something more compact, with the parameters relocated to the class signature line:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">internal</span> <span class="k">class</span> <span class="nc">Satellite</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span> <span class="n">name</span><span class="p">,</span> <span class="kt">string</span> <span class="n">owner</span><span class="p">,</span> <span class="n">DateOnly</span> <span class="n">launchDate</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">isActive</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">init</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Owner</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">init</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">owner</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">DateOnly</span> <span class="n">LaunchDate</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">init</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">launchDate</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">bool</span> <span class="n">IsActive</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">isActive</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>If we add the <code>record</code> modifier (something from a few versions before), then we can reduce the definition even more, since it causes read-only properties to be generated for us:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">internal</span> <span class="k">record</span> <span class="nc">class</span> <span class="n">Satellite</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span> <span class="n">Name</span><span class="p">,</span> <span class="kt">string</span> <span class="n">Owner</span><span class="p">,</span> <span class="n">DateOnly</span> <span class="n">LaunchDate</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">IsActive</span><span class="p">);</span></span></span></code></pre></div></div>
<p>But since the original class allowed us to change the <code>IsActive</code> property, we&rsquo;d actually have to override the default behavior by defining the property ourselves:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">internal</span> <span class="k">record</span> <span class="nc">class</span> <span class="n">Satellite</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span> <span class="n">Name</span><span class="p">,</span> <span class="kt">string</span> <span class="n">Owner</span><span class="p">,</span> <span class="n">DateOnly</span> <span class="n">LaunchDate</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">IsActive</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">bool</span> <span class="n">IsActive</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">IsActive</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Any of the above definitions allows us to intantiate the class like this, and then we can change the one property that allows it as needed:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">satellite</span> <span class="p">=</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span> <span class="n">Satellite</span><span class="p">(</span><span class="s">&#34;Navstar 82&#34;</span><span class="p">,</span> <span class="s">&#34;US&#34;</span><span class="p">,</span> <span class="k">new</span> <span class="n">DateOnly</span><span class="p">(</span><span class="m">2023</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="m">17</span><span class="p">),</span> <span class="kc">false</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">satellite</span><span class="p">.</span><span class="n">IsActive</span> <span class="p">=</span> <span class="kc">true</span><span class="p">;</span></span></span></code></pre></div></div>
<p>Records are supposed to be immutable though, so allowing properties to be changed could confuse someone later on. I just felt like showing it off.</p>

<h2 class="relative group">Using Primary Constructors in Structs
    <div id="using-primary-constructors-in-structs" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#using-primary-constructors-in-structs" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Structs are supposed to represent singular values, like primitive types or <code>DateTime</code>. Here&rsquo;s a struct that defines a &ldquo;satellite position&rdquo; value, which consists of a few things like longitude, latitude, etc.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">internal</span> <span class="k">struct</span> <span class="nc">SatellitePosition</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Latitude</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Longitude</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">decimal</span> <span class="n">Altitude</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">init</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">DateTimeOffset</span> <span class="n">Time</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">SatellitePosition</span><span class="p">(</span><span class="kt">string</span> <span class="n">latitude</span><span class="p">,</span> <span class="kt">string</span> <span class="n">longitude</span><span class="p">,</span> <span class="kt">decimal</span> <span class="n">altitude</span><span class="p">,</span> <span class="n">DateTimeOffset</span> <span class="n">time</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Latitude</span> <span class="p">=</span> <span class="n">latitude</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Longitude</span> <span class="p">=</span> <span class="n">longitude</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Altitude</span> <span class="p">=</span> <span class="n">altitude</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Time</span> <span class="p">=</span> <span class="n">time</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>All of the properties can be changed except for the altitude (again, who knows why.. we&rsquo;ll just pretend it&rsquo;s some crazy requirement).</p>
<p>As with classes, we can migrate it to a primary constructor easily enough:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">internal</span> <span class="k">struct</span> <span class="nc">SatellitePosition</span><span class="p">(</span><span class="kt">string</span> <span class="n">latitude</span><span class="p">,</span> <span class="kt">string</span> <span class="n">longitude</span><span class="p">,</span> <span class="kt">decimal</span> <span class="n">altitude</span><span class="p">,</span> <span class="n">DateTimeOffset</span> <span class="n">time</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Latitude</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">latitude</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Longitude</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">longitude</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">decimal</span> <span class="n">Altitude</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">init</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">altitude</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">DateTimeOffset</span> <span class="n">Time</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">time</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>And, again similar to classes, we can reduce things even further by using a <code>record</code>, if that fits our use case:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">internal</span> <span class="k">record</span> <span class="nc">struct</span> <span class="n">SatellitePosition</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span> <span class="n">Latitude</span><span class="p">,</span> <span class="kt">string</span> <span class="n">Longitude</span><span class="p">,</span> <span class="kt">decimal</span> <span class="n">Altitude</span><span class="p">,</span> <span class="n">DateTimeOffset</span> <span class="n">Time</span><span class="p">);</span></span></span></code></pre></div></div>
<p>It&rsquo;s worth mentioning there&rsquo;s some different behavior here though, than what we saw in the class. Whereas a <code>record class</code> generates readonly properties for us, a <code>record struct</code> generates properties that <em>can</em> be changed.</p>
<p>If we want to prevent an individual property from being changed, we can define it ourselves like this (or add <code>readonly</code> to the signature to make <em>everything</em> readonly):</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">internal</span> <span class="k">record</span> <span class="nc">struct</span> <span class="n">SatellitePosition</span><span class="p">(</span><span class="kt">string</span> <span class="n">Latitude</span><span class="p">,</span> <span class="kt">string</span> <span class="n">Longitude</span><span class="p">,</span> <span class="kt">decimal</span> <span class="n">Altitude</span><span class="p">,</span> <span class="n">DateTimeOffset</span> <span class="n">Time</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">decimal</span> <span class="n">Altitude</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">init</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">Altitude</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Then we can instantiate it as usual:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">satpos</span> <span class="p">=</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span> <span class="n">SatellitePosition1</span><span class="p">(</span><span class="s">&#34;44.3°N&#34;</span><span class="p">,</span> <span class="s">&#34;25.3°W&#34;</span><span class="p">,</span> <span class="m">20186</span><span class="p">,</span> <span class="n">DateTimeOffset</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">satpos</span><span class="p">.</span><span class="n">Latitude</span> <span class="p">=</span> <span class="s">&#34;44.3°S&#34;</span><span class="p">;</span></span></span></code></pre></div></div>

<h2 class="relative group">Other Concerns
    <div id="other-concerns" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#other-concerns" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If we need to do something else when a class or struct is instantiated, it <em>is</em> possible to add a normal constructor in addition to the primary constructor, as long as the constructor calls the primary constructor and passes some default values:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">internal</span> <span class="k">struct</span> <span class="nc">SatellitePosition</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span> <span class="n">latitude</span><span class="p">,</span> <span class="kt">string</span> <span class="n">longitude</span><span class="p">,</span> <span class="kt">decimal</span> <span class="n">altitude</span><span class="p">,</span> <span class="n">DateTimeOffset</span> <span class="n">time</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Latitude</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">latitude</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Longitude</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">longitude</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">decimal</span> <span class="n">Altitude</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">init</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">altitude</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">DateTimeOffset</span> <span class="n">Time</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">time</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">SatellitePosition</span><span class="p">()</span> <span class="p">:</span> <span class="k">this</span><span class="p">(</span><span class="s">&#34;&#34;</span><span class="p">,</span> <span class="s">&#34;&#34;</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">DateTimeOffset</span><span class="p">.</span><span class="n">MinValue</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// other important stuff to do on instantiation</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Unless the default values make sense though, then just sticking with a normal constructor is probably the better way to go.</p>

<h2 class="relative group">Learning More
    <div id="learning-more" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#learning-more" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>To learn more about primary constructors, explore the <a href="https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/instance-constructors#primary-constructors"  target="_blank" rel="noreferrer">official docs</a>. Or to learn more about records, see my other article here:</p>
<p><a href="https://grantwinney.com/records-classes-and-equality-in-csharp/"  target="_blank" rel="noreferrer">Records, Classes and Equality in C# 9 / .NET 5</a></p>
<p>If you found this content useful, and would like to learn more about a variety of <a href="https://grantwinney.com/tags/csharp/"  target="_blank" rel="noreferrer">C#</a> features, check out my <a href="https://github.com/grantwinney/CSharpDotNetFeatures"  target="_blank" rel="noreferrer">CSharpDotNetFeatures repo</a>, where you&rsquo;ll find links to plenty more blog posts and practical examples!</p>
]]></content:encoded><media:content url="https://grantwinney.com/using-primary-constructors-with-classes-and-structs-in-csharp/feature.webp" medium="image" type="image/webp"/></item><item><title>Records, Classes and Equality in C# 9 / .NET 5</title><link>https://grantwinney.com/records-classes-and-equality-in-csharp/</link><pubDate>Tue, 10 Dec 2024 12:35:55 +0000</pubDate><guid>https://grantwinney.com/records-classes-and-equality-in-csharp/</guid><description>The record modifier can define properties and equality in our classes for us, saving time and keeping our code cleaner. Let&amp;rsquo;s see how it works!</description><content:encoded><![CDATA[<p>When C# 9 was released in 2020, one of the main focuses was on &ldquo;removing ceremony&rdquo;, as they put it. I&rsquo;m sure that&rsquo;s a regular focus with each release, but this time they called it out specifically. As a result, some of the tedious, boilerplate types of syntax we find ourselves writing were removed.</p>
<p>Among other useful things (like top-level statements and init setters), they added the <code>record</code> modifier for classes. In the release a year after, they made it possible to mark structs as a <code>record</code> too. Adding the new modifier does a couple cool things for us that have me looking for opportunities to use them, so let&rsquo;s take a closer look.</p>
<blockquote><p>The code in this post is available on <a href="https://github.com/grantwinney/CSharpDotNetFeatures/tree/master/C%23%2009/RecordModifier"  target="_blank" rel="noreferrer">GitHub</a>, for you to use, extend, or just follow along while you read&hellip; and hopefully discover something new along the way!</p>
</blockquote>
<h2 class="relative group">Classes
    <div id="classes" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#classes" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>We&rsquo;ve all written classes, many of us hundreds or even thousands of times. They&rsquo;re the primary way we group data together into a logical entity, along with methods to manipulate and transform the data, etc. Here&rsquo;s a simple one:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Plane</span><span class="p">(</span><span class="kt">string</span> <span class="n">Make</span><span class="p">,</span> <span class="kt">string</span> <span class="n">Model</span><span class="p">,</span> <span class="kt">int</span> <span class="n">Year</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Make</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">Make</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Model</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">Model</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">Year</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">Year</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>It&rsquo;s no surprise that we need public properties to access the values passed in via the primary constructor. And it should be no surprise that two instances with all the same values will <em>not</em> be detected as &ldquo;equal&rdquo; to one another:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">plane1</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Plane</span><span class="p">(</span><span class="s">&#34;Cessna&#34;</span><span class="p">,</span> <span class="s">&#34;680A&#34;</span><span class="p">,</span> <span class="m">2015</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">plane2</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Plane</span><span class="p">(</span><span class="s">&#34;Cessna&#34;</span><span class="p">,</span> <span class="s">&#34;680A&#34;</span><span class="p">,</span> <span class="m">2015</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">plane1</span><span class="p">.</span><span class="n">Equals</span><span class="p">(</span><span class="n">plane2</span><span class="p">));</span>  <span class="c1">// false</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">plane1</span> <span class="p">==</span> <span class="n">plane2</span><span class="p">);</span>       <span class="c1">// false</span></span></span></code></pre></div></div>
<p>We haven&rsquo;t described what makes two <code>Plane</code> instances equal, so the references to each instance (which are always different) are used for the comparison by default.</p>

<h2 class="relative group">Classes (with equality defined by us)
    <div id="classes-with-equality-defined-by-us" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#classes-with-equality-defined-by-us" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If we want to <a href="https://grantwinney.com/csharp-compare-two-objects-for-equality/"  target="_blank" rel="noreferrer">compare two objects for equality</a>, there&rsquo;s a multitude of ways to do it. Here&rsquo;s two common ones – overriding equality operators and implementing the <code>IEquatable&lt;T&gt;</code> interface:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Train</span><span class="p">(</span><span class="kt">string</span> <span class="n">Make</span><span class="p">,</span> <span class="kt">string</span> <span class="n">Model</span><span class="p">,</span> <span class="kt">int</span> <span class="n">Year</span><span class="p">)</span> <span class="p">:</span> <span class="n">IEquatable</span><span class="p">&lt;</span><span class="n">Train</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Make</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">Make</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Model</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">Model</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">Year</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">Year</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">bool</span> <span class="kd">operator</span> <span class="p">==(</span><span class="n">Train</span><span class="p">?</span> <span class="n">x</span><span class="p">,</span> <span class="n">Train</span><span class="p">?</span> <span class="n">y</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="k">is</span> <span class="kc">null</span> <span class="p">||</span> <span class="n">y</span> <span class="k">is</span> <span class="kc">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">x</span><span class="p">.</span><span class="n">Make</span> <span class="p">==</span> <span class="n">y</span><span class="p">.</span><span class="n">Make</span> <span class="p">&amp;&amp;</span> <span class="n">x</span><span class="p">.</span><span class="n">Model</span> <span class="p">==</span> <span class="n">y</span><span class="p">.</span><span class="n">Model</span> <span class="p">&amp;&amp;</span> <span class="n">x</span><span class="p">.</span><span class="n">Year</span> <span class="p">==</span> <span class="n">y</span><span class="p">.</span><span class="n">Year</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">bool</span> <span class="kd">operator</span> <span class="p">!=(</span><span class="n">Train</span><span class="p">?</span> <span class="n">x</span><span class="p">,</span> <span class="n">Train</span><span class="p">?</span> <span class="n">y</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">!(</span><span class="n">x</span> <span class="p">==</span> <span class="n">y</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// IEquatable&lt;T&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">bool</span> <span class="n">Equals</span><span class="p">(</span><span class="n">Train</span><span class="p">?</span> <span class="n">other</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">this</span> <span class="p">==</span> <span class="n">other</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>I don&rsquo;t know how everyone else likes to do it, but I tend to define equality in just one of the methods, and then have the other ones call it. It keeps the code more DRY, and more easily updated later on if needed.</p>
<p>Now if we create a couple of trains and test to see if they&rsquo;re equal, they are:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">train1</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Train</span><span class="p">(</span><span class="s">&#34;Odakyu&#34;</span><span class="p">,</span> <span class="s">&#34;3000&#34;</span><span class="p">,</span> <span class="m">1958</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">train2</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Train</span><span class="p">(</span><span class="s">&#34;Odakyu&#34;</span><span class="p">,</span> <span class="s">&#34;3000&#34;</span><span class="p">,</span> <span class="m">1958</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">train1</span><span class="p">.</span><span class="n">Equals</span><span class="p">(</span><span class="n">train2</span><span class="p">));</span>  <span class="c1">// true</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">train1</span> <span class="p">==</span> <span class="n">train2</span><span class="p">);</span>       <span class="c1">// true</span></span></span></code></pre></div></div>

<h2 class="relative group">Records (with equality built-in)
    <div id="records-with-equality-built-in" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#records-with-equality-built-in" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>What&rsquo;s great about records is that a lot of these extra steps (aka ceremony) goes away.</p>
<p>Here&rsquo;s roughly the same class as above, with a different name and marked as a <code>record</code> this time. We <em>could</em> include the keyword <code>class</code> in there too, since it is one, but it&rsquo;s unnecessary and can be left out.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">record</span> <span class="nc">Automobile</span><span class="p">(</span><span class="kt">string</span> <span class="n">Make</span><span class="p">,</span> <span class="kt">string</span> <span class="n">Model</span><span class="p">,</span> <span class="kt">int</span> <span class="n">Year</span><span class="p">);</span></span></span></code></pre></div></div>
<p>Nearly everything that was defined in the <code>Train</code> class is gone, automatically generated for us behind the scenes! The <strong>parameters we define in the primary constructor get matching properties automatically</strong>, without us having to manually define them.</p>
<p>These properties are init-only properties, since <strong>records are intended to represent &ldquo;immutable data models&rdquo;</strong>, so by default we can&rsquo;t change values after a record is initialized. Instead, we&rsquo;re meant to create a new instance using a <code>with</code> expression:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">kia</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Automobile</span><span class="p">(</span><span class="s">&#34;Kia&#34;</span><span class="p">,</span> <span class="s">&#34;Forte&#34;</span><span class="p">,</span> <span class="m">2016</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">newerKia</span> <span class="p">=</span> <span class="n">kia</span> <span class="n">with</span> <span class="p">{</span> <span class="n">Year</span> <span class="p">=</span> <span class="m">2021</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">kia</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">newerKia</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">kia</span> <span class="p">==</span> <span class="n">newerKia</span><span class="p">);</span>  <span class="c1">// false</span></span></span></code></pre></div></div>
<p>If we really want to though, we can define one or more properties with public setters ourselves, like any other class. That behavior might surprise anyone delving into our code who expects properties to be a one-and-done thing, though.</p>
<p>Also, <strong>records implement the</strong> <em><code>*IEquatable&lt;T&gt;*</code></em> <strong>interface for us too</strong>, comparing the values of each property. In fact, if we define a record like this, calling out the <code>IEquatable&lt;T&gt;</code> interface explicitly, it doesn&rsquo;t complain&hellip; but it also doesn&rsquo;t warn us about the missing method since it&rsquo;s already defined behind-the-scenes:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">record</span> <span class="nc">Automobile</span><span class="p">(</span><span class="kt">string</span> <span class="n">Make</span><span class="p">,</span> <span class="kt">string</span> <span class="n">Model</span><span class="p">,</span> <span class="kt">int</span> <span class="n">Year</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">     <span class="p">:</span> <span class="n">IEquatable</span><span class="p">&lt;</span><span class="n">Automobile</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p>And if we try to overload the equality operators, we can&rsquo;t. It warns us that they&rsquo;re already defined.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/records-classes-and-equality-in-csharp/overload-equality-operator-warning.png"
    width="717"
      height="280"></figure>
<p>So now this works with just the original one-liner defining the record:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">auto1</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Automobile</span><span class="p">(</span><span class="s">&#34;Toyota&#34;</span><span class="p">,</span> <span class="s">&#34;Corolla&#34;</span><span class="p">,</span> <span class="m">2023</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">auto2</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Automobile</span><span class="p">(</span><span class="s">&#34;Toyota&#34;</span><span class="p">,</span> <span class="s">&#34;Corolla&#34;</span><span class="p">,</span> <span class="m">2023</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">auto1</span><span class="p">.</span><span class="n">Equals</span><span class="p">(</span><span class="n">auto2</span><span class="p">));</span>  <span class="c1">// true</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">auto1</span> <span class="p">==</span> <span class="n">auto2</span><span class="p">);</span>       <span class="c1">// true</span></span></span></code></pre></div></div>

<h2 class="relative group">Learning More
    <div id="learning-more" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#learning-more" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I feel like, personally, I oscillate between creating complex but useful things, and then simplifying them. Then I add to them which makes them more complex again, and then I go back and simplify again. It&rsquo;s a bit of a tug o&rsquo; war, and I&rsquo;m glad when the teams at Microsoft take the time to simplify things for us.</p>
<p>One last thought. I see a lot of &ldquo;records vs classes&rdquo; types of posts out there for C#, but a record isn&rsquo;t an <em>alternative</em> to classes – it&rsquo;s a modifier that changes their behavior. I think the docs make that clear by stating that the <code>record class</code> syntax is <em>&ldquo;a synonym to clarify a reference type&rdquo;</em>. We can replace the <code>class</code> keyword with <code>record</code> in a class definition, or keep them both if it helps make things clearer, but it means the same thing. On the other hand, other references like the <a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-12#primary-constructors"  target="_blank" rel="noreferrer">primary constructors</a> doc states that, <em>&ldquo;You can now create primary constructors in any</em> <em><code>_class_</code></em> <em>and</em> <em><code>_struct_</code>__. Primary constructors are no longer restricted to</em> <em><code>_record_</code></em> <em>types.&rdquo;</em>, which makes it sound very separate. Maybe the <code>record</code> modifier changes behavior so drastically that it <em>should</em> be considered a different type altogether?</p>
<p>If you want to learn more about records, I found <a href="https://falberthen.github.io/posts/cs10-records/"  target="_blank" rel="noreferrer">this article</a> by Felipe Henrique interesting. Then there&rsquo;s the <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/record"  target="_blank" rel="noreferrer">official docs</a> from Microsoft, as well as <a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-9"  target="_blank" rel="noreferrer">other changes in C# 9</a>. And if you&rsquo;d like to learn more about a variety of <a href="https://grantwinney.com/tags/csharp/"  target="_blank" rel="noreferrer">C#</a> features, check out my <a href="https://github.com/grantwinney/CSharpDotNetFeatures"  target="_blank" rel="noreferrer">CSharpDotNetFeatures repo</a>, where you&rsquo;ll find links to plenty more blog posts and practical examples!</p>
]]></content:encoded><media:content url="https://grantwinney.com/records-classes-and-equality-in-csharp/feature.webp" medium="image" type="image/webp"/></item><item><title>Set-based LINQ - ExceptBy, IntersectBy, UnionBy, DistinctBy</title><link>https://grantwinney.com/set-based-linq-exceptby-intersectby-unionby-distinctby/</link><pubDate>Sun, 08 Dec 2024 22:59:12 +0000</pubDate><guid>https://grantwinney.com/set-based-linq-exceptby-intersectby-unionby-distinctby/</guid><description>The .NET team has made some helpful additions to LINQ in recent years. Today let&amp;rsquo;s check out the various set-based updates from C# 10 / .NET 6.</description><content:encoded><![CDATA[<p>Microsoft recently released C# 13 with a couple new additions to LINQ, which <a href="https://grantwinney.com/using-linq-countby-and-aggregateby-in-csharp/"  target="_blank" rel="noreferrer">I wrote about last week</a>. That got me thinking about other recent additions to LINQ, like <a href="https://grantwinney.com/using-minby-and-maxby-in-csharp/"  target="_blank" rel="noreferrer">MaxBy and MinBy</a>. Continuing down the list, let&rsquo;s check out some set-based methods, including ExceptBy, IntersectBy, UnionBy, and DistinctBy.</p>
<blockquote><p>The code in this post is available on <a href="https://github.com/grantwinney/CSharpDotNetFeatures/tree/master/C%23%2010/SetBasedLinqMethods"  target="_blank" rel="noreferrer">GitHub</a>, for you to use, extend, or just follow along while you read&hellip; and hopefully discover something new along the way!</p>
</blockquote>
<h2 class="relative group">But first&hellip;
    <div id="but-first" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#but-first" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Let&rsquo;s define a couple classes and create a short list of books and movies, so we can try out the different LINQ methods against some data.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">internal</span> <span class="k">class</span> <span class="nc">Book</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">PublishYear</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">internal</span> <span class="k">class</span> <span class="nc">Movie</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">ReleaseYear</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">books</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Book</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span><span class="p">()</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;How to Stop Time&#34;</span><span class="p">,</span>   <span class="n">PublishYear</span> <span class="p">=</span> <span class="m">2017</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span><span class="p">()</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Little Women&#34;</span><span class="p">,</span>       <span class="n">PublishYear</span> <span class="p">=</span> <span class="m">1868</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span><span class="p">()</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Catcher in the Rye&#34;</span><span class="p">,</span> <span class="n">PublishYear</span> <span class="p">=</span> <span class="m">1951</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span><span class="p">()</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;The Princess Bride&#34;</span><span class="p">,</span> <span class="n">PublishYear</span> <span class="p">=</span> <span class="m">1973</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">oldBooks</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Book</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span><span class="p">()</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Little Women&#34;</span><span class="p">,</span>        <span class="n">PublishYear</span> <span class="p">=</span> <span class="m">1868</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span><span class="p">()</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;The Maid Of Orleans&#34;</span><span class="p">,</span> <span class="n">PublishYear</span> <span class="p">=</span> <span class="m">1801</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">movies</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Movie</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span><span class="p">()</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;The Princess Bride&#34;</span><span class="p">,</span> <span class="n">ReleaseYear</span> <span class="p">=</span> <span class="m">1987</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span><span class="p">()</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Good Will Hunting&#34;</span><span class="p">,</span>  <span class="n">ReleaseYear</span> <span class="p">=</span> <span class="m">1997</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span><span class="p">()</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Inception&#34;</span><span class="p">,</span>          <span class="n">ReleaseYear</span> <span class="p">=</span> <span class="m">2010</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span><span class="p">()</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Little Women&#34;</span><span class="p">,</span>       <span class="n">ReleaseYear</span> <span class="p">=</span> <span class="m">1994</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span><span class="p">()</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Little Women&#34;</span><span class="p">,</span>       <span class="n">ReleaseYear</span> <span class="p">=</span> <span class="m">2019</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span><span class="p">()</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Little Women&#34;</span><span class="p">,</span>       <span class="n">ReleaseYear</span> <span class="p">=</span> <span class="m">1949</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span></span></span></code></pre></div></div>

<h2 class="relative group">ExceptBy
    <div id="exceptby" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#exceptby" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The older <code>Except</code> method allows us to ask for all the items in one collection, minus the items in a second collection, as long as our code has some way to determine what makes any two items equal. Primary types, records, or classes where we&rsquo;ve implemented certain interfaces work as easily as:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">newerBooks</span> <span class="p">=</span> <span class="n">books</span><span class="p">.</span><span class="n">Except</span><span class="p">(</span><span class="n">oldBooks</span><span class="p">);</span></span></span></code></pre></div></div>
<p>In many cases though, we need to give the code a hint about our classes. Using the newer <a href="https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.exceptby"  target="_blank" rel="noreferrer"><code>ExceptBy</code></a> method lets us do that with a single property in a class, like &ldquo;Name&rdquo; in the sample class above:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">newerBooks</span> <span class="p">=</span> <span class="n">books</span><span class="p">.</span><span class="n">ExceptBy</span><span class="p">(</span><span class="n">oldBooks</span><span class="p">.</span><span class="n">Select</span><span class="p">(</span><span class="n">ob</span> <span class="p">=&gt;</span> <span class="n">ob</span><span class="p">.</span><span class="n">Name</span><span class="p">),</span> <span class="n">b</span> <span class="p">=&gt;</span> <span class="n">b</span><span class="p">.</span><span class="n">Name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Newer books: {string.Join(&#34;</span><span class="p">,</span> <span class="s">&#34;, newerBooks.Select(b =&gt; b.Name))}&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Newer books: How to Stop Time, Catcher in the Rye, The Princess Bride</span></span></span></code></pre></div></div>
<p>We can compare two <em>different</em> class types too, i.e. asking for all the books <em>except</em> those whose name occurs in the movies list:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">booksOnly</span> <span class="p">=</span> <span class="n">books</span><span class="p">.</span><span class="n">ExceptBy</span><span class="p">(</span><span class="n">movies</span><span class="p">.</span><span class="n">Select</span><span class="p">(</span><span class="n">m</span> <span class="p">=&gt;</span> <span class="n">m</span><span class="p">.</span><span class="n">Name</span><span class="p">),</span> <span class="n">b</span> <span class="p">=&gt;</span> <span class="n">b</span><span class="p">.</span><span class="n">Name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;Books that aren&#39;t movies: &#34;</span> <span class="p">+</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span><span class="p">.</span><span class="n">Join</span><span class="p">(</span><span class="s">&#34;, &#34;</span><span class="p">,</span> <span class="n">booksOnly</span><span class="p">.</span><span class="n">Select</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">Name</span><span class="p">)));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Books that aren&#39;t movies: How to Stop Time, Catcher in the Rye</span></span></span></code></pre></div></div>
<p>Or we can go the other way, and get all movies that are <em>not</em> also books:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">moviesOnly</span> <span class="p">=</span> <span class="n">movies</span><span class="p">.</span><span class="n">ExceptBy</span><span class="p">(</span><span class="n">books</span><span class="p">.</span><span class="n">Select</span><span class="p">(</span><span class="n">b</span> <span class="p">=&gt;</span> <span class="n">b</span><span class="p">.</span><span class="n">Name</span><span class="p">),</span> <span class="n">m</span> <span class="p">=&gt;</span> <span class="n">m</span><span class="p">.</span><span class="n">Name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;Movies that aren&#39;t books: &#34;</span> <span class="p">+</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span><span class="p">.</span><span class="n">Join</span><span class="p">(</span><span class="s">&#34;, &#34;</span><span class="p">,</span> <span class="n">moviesOnly</span><span class="p">.</span><span class="n">Select</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">Name</span><span class="p">)));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Movies that aren&#39;t books: Good Will Hunting, Inception</span></span></span></code></pre></div></div>

<h2 class="relative group">IntersectBy
    <div id="intersectby" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#intersectby" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Whereas <code>Except</code> and <code>ExceptBy</code> remove everything from one list that <em>is</em> in a second list, the <code>Intersect</code> and <a href="https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.intersectby"  target="_blank" rel="noreferrer"><code>IntersectBy</code></a> methods removes everything from the first that <em>isn&rsquo;t</em> in the second. It finds the intersection of two collections, or items that are in both.</p>
<p>Just like with <code>ExceptBy</code>, we can specify a property (like the name) that makes them equal:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">moviesANDbooks</span> <span class="p">=</span> <span class="n">books</span><span class="p">.</span><span class="n">IntersectBy</span><span class="p">(</span><span class="n">movies</span><span class="p">.</span><span class="n">Select</span><span class="p">(</span><span class="n">m</span> <span class="p">=&gt;</span> <span class="n">m</span><span class="p">.</span><span class="n">Name</span><span class="p">),</span> <span class="n">b</span> <span class="p">=&gt;</span> <span class="n">b</span><span class="p">.</span><span class="n">Name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;Books that are also movies: &#34;</span> <span class="p">+</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span><span class="p">.</span><span class="n">Join</span><span class="p">(</span><span class="s">&#34;, &#34;</span><span class="p">,</span> <span class="n">moviesANDbooks</span><span class="p">.</span><span class="n">Select</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">Name</span><span class="p">)));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Books that are also movies: Little Women, The Princess Bride</span></span></span></code></pre></div></div>

<h2 class="relative group">UnionBy
    <div id="unionby" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#unionby" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Instead of considering what <em>is</em> in the second collection like <code>ExceptBy</code>, or what <em>isn&rsquo;t</em> in it like <code>IntersectBy</code>, the <a href="https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.unionby"  target="_blank" rel="noreferrer"><code>UnionBy</code></a> method just grabs a unique list of everything in both. If the collections contain the same class type, we just specify the parameter name it should use for the comparison:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">allBooks</span> <span class="p">=</span> <span class="n">books</span><span class="p">.</span><span class="n">UnionBy</span><span class="p">(</span><span class="n">oldBooks</span><span class="p">,</span> <span class="n">b</span> <span class="p">=&gt;</span> <span class="n">b</span><span class="p">.</span><span class="n">Name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;All the books: &#34;</span> <span class="p">+</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span><span class="p">.</span><span class="n">Join</span><span class="p">(</span><span class="s">&#34;, &#34;</span><span class="p">,</span> <span class="n">allBooks</span><span class="p">.</span><span class="n">Select</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">Name</span><span class="p">)));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// All the books: How to Stop Time, Little Women,</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Catcher in the Rye, The Princess Bride, The Maid of Orleans</span></span></span></code></pre></div></div>
<p>If they&rsquo;re different types, like movies and books, we need to convert one type to the other in order to &ldquo;combine&rdquo; them:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">moviesORbooks</span> <span class="p">=</span> <span class="n">books</span><span class="p">.</span><span class="n">UnionBy</span><span class="p">(</span><span class="n">movies</span><span class="p">.</span><span class="n">Select</span><span class="p">(</span><span class="n">movie</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span> <span class="n">Book</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="n">movie</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span> <span class="n">PublishYear</span> <span class="p">=</span> <span class="m">2000</span> <span class="p">}),</span> <span class="n">book</span> <span class="p">=&gt;</span> <span class="n">book</span><span class="p">.</span><span class="n">Name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;Books, movies, or both: &#34;</span> <span class="p">+</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span><span class="p">.</span><span class="n">Join</span><span class="p">(</span><span class="s">&#34;, &#34;</span><span class="p">,</span> <span class="n">moviesORbooks</span><span class="p">.</span><span class="n">Select</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">Name</span><span class="p">)));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Books, movies, or both: How to Stop Time, Little Women,</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Catcher in the Rye, The Princess Bride, Good Will Hunting, Inception</span></span></span></code></pre></div></div>

<h2 class="relative group">DistinctBy
    <div id="distinctby" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#distinctby" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>And finally, there&rsquo;s <a href="https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.distinctby"  target="_blank" rel="noreferrer"><code>DistinctBy</code></a>, where we can specify the property by which it can be determined how to get a unique list of some item. When there&rsquo;s multiple items with the same value for the property, it goes with the first one:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">uniqueMovies</span> <span class="p">=</span> <span class="n">movies</span><span class="p">.</span><span class="n">DistinctBy</span><span class="p">(</span><span class="n">m</span> <span class="p">=&gt;</span> <span class="n">m</span><span class="p">.</span><span class="n">Name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;Unique list of movies: &#34;</span> <span class="p">+</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span><span class="p">.</span><span class="n">Join</span><span class="p">(</span><span class="s">&#34;, &#34;</span><span class="p">,</span> <span class="n">uniqueMovies</span><span class="p">.</span><span class="n">Select</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="s">$&#34;{x.Name} ({x.ReleaseYear})&#34;</span><span class="p">)));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Unique list of movies: The Princess Bride (1987), Good Will Hunting (1997),</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Inception (2010), Little Women (1994)</span></span></span></code></pre></div></div>

<h2 class="relative group">Specifying Multiple Properties
    <div id="specifying-multiple-properties" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#specifying-multiple-properties" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>All the examples above compared a single property to define equality, but in large collections that might not be enough. When it isn&rsquo;t, we can specify more than one property by just creating a tuple on-the-fly.</p>
<p>Here&rsquo;s another <code>ExceptBy</code> example, but it compares the name <em>and</em> the publish year:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">newerBooks_multiple</span> <span class="p">=</span> <span class="n">books</span><span class="p">.</span><span class="n">ExceptBy</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">oldBooks</span><span class="p">.</span><span class="n">Select</span><span class="p">(</span><span class="n">ob</span> <span class="p">=&gt;</span> <span class="p">(</span><span class="n">ob</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span> <span class="n">ob</span><span class="p">.</span><span class="n">PublishYear</span><span class="p">)),</span> <span class="n">b</span> <span class="p">=&gt;</span> <span class="p">(</span><span class="n">b</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span> <span class="n">b</span><span class="p">.</span><span class="n">PublishYear</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Newer books: &#34;</span> <span class="p">+</span>
</span></span><span class="line"><span class="cl">    <span class="s">$&#34;{string.Join(&#34;</span><span class="p">,</span> <span class="s">&#34;, newerBooks_multiple.Select(b =&gt; b.Name))}&#34;</span><span class="p">);</span></span></span></code></pre></div></div>
<p>We can use Tuples when comparing collections of different types too, as long as the types of the arguments in the Tuple are the same:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">booksOnly_multiple</span> <span class="p">=</span> <span class="n">books</span><span class="p">.</span><span class="n">ExceptBy</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">movies</span><span class="p">.</span><span class="n">Select</span><span class="p">(</span><span class="n">m</span> <span class="p">=&gt;</span> <span class="p">(</span><span class="n">m</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span> <span class="n">m</span><span class="p">.</span><span class="n">ReleaseYear</span><span class="p">)),</span> <span class="n">b</span> <span class="p">=&gt;</span> <span class="p">(</span><span class="n">b</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span> <span class="n">b</span><span class="p">.</span><span class="n">PublishYear</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;Books that aren&#39;t movies: &#34;</span> <span class="p">+</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span><span class="p">.</span><span class="n">Join</span><span class="p">(</span><span class="s">&#34;, &#34;</span><span class="p">,</span> <span class="n">booksOnly_multiple</span><span class="p">.</span><span class="n">Select</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">Name</span><span class="p">)));</span></span></span></code></pre></div></div>
<p>You probably get the idea, but here&rsquo;s one more – a distinct list of movies, where &ldquo;distinct&rdquo; is a unique title and release year:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">uniqueMovies</span> <span class="p">=</span> <span class="n">movies</span><span class="p">.</span><span class="n">DistinctBy</span><span class="p">(</span><span class="n">m</span> <span class="p">=&gt;</span> <span class="p">(</span><span class="n">m</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span> <span class="n">m</span><span class="p">.</span><span class="n">ReleaseYear</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;Unique list of movie/year combos: &#34;</span> <span class="p">+</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span><span class="p">.</span><span class="n">Join</span><span class="p">(</span><span class="s">&#34;, &#34;</span><span class="p">,</span> <span class="n">uniqueMovies</span><span class="p">.</span><span class="n">Select</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="s">$&#34;{x.Name} ({x.ReleaseYear})&#34;</span><span class="p">)));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Unique list of movie/year combos: The Princess Bride (1987),</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Good Will Hunting (1997), Inception (2010), Little Women (1994),</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Little Women (2019), Little Women (1949)</span></span></span></code></pre></div></div>

<h2 class="relative group">Learning More&hellip;
    <div id="learning-more" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#learning-more" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There&rsquo;s more details in the MS docs, although they&rsquo;re pretty dry.</p>
<ul>
<li><a href="https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.exceptby"  target="_blank" rel="noreferrer">Enumerable.ExceptBy Method (System.Linq) | Microsoft Learn</a></li>
<li><a href="https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.intersectby"  target="_blank" rel="noreferrer">Enumerable.IntersectBy Method (System.Linq) | Microsoft Learn</a></li>
<li><a href="https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.unionby"  target="_blank" rel="noreferrer">Enumerable.UnionBy Method (System.Linq) | Microsoft Learn</a></li>
<li><a href="https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.distinctby"  target="_blank" rel="noreferrer">Enumerable.DistinctBy Method (System.Linq) | Microsoft Learn</a></li>
</ul>
<p>A better page is the one that shows <a href="https://learn.microsoft.com/en-us/dotnet/csharp/linq/standard-query-operators/set-operations"  target="_blank" rel="noreferrer">example usages of Set operations</a>.</p>
<p>If you found this content useful, and would like to learn more about a variety of <a href="https://grantwinney.com/tags/csharp/"  target="_blank" rel="noreferrer">C#</a> features, check out my <a href="https://github.com/grantwinney/CSharpDotNetFeatures"  target="_blank" rel="noreferrer">CSharpDotNetFeatures repo</a>, where you&rsquo;ll find links to plenty more blog posts and practical examples!</p>
]]></content:encoded><media:content url="https://grantwinney.com/set-based-linq-exceptby-intersectby-unionby-distinctby/feature.webp" medium="image" type="image/webp"/></item><item><title>Using MinBy and MaxBy in C# 10 / .NET 6</title><link>https://grantwinney.com/using-minby-and-maxby-in-csharp/</link><pubDate>Thu, 05 Dec 2024 22:24:55 +0000</pubDate><guid>https://grantwinney.com/using-minby-and-maxby-in-csharp/</guid><description>The .NET team has made some helpful additions to LINQ over the last few years. Today let&amp;rsquo;s check out MinBy and MaxBy from C# 10 / .NET 6.</description><content:encoded><![CDATA[<p>Microsoft made a couple new additions to LINQ as part of the C# 13 / .NET 9 release a few weeks ago, and, since I happen to really like LINQ, I wrote about <a href="https://grantwinney.com/using-linq-countby-and-aggregateby-in-csharp/"  target="_blank" rel="noreferrer">how to use them</a>. That got me thinking about other recent additions I might&rsquo;ve missed in the last few releases, so I started <a href="https://learn.microsoft.com/en-us/dotnet/maui/whats-new"  target="_blank" rel="noreferrer">looking back</a>. And whatdya know, we got a slew of updates to LINQ in C# 10 / .NET 6 a few years ago.</p>
<p>Let&rsquo;s take a look at two of them - MaxBy and MinBy - right after a brief overview of what we had before. Makes it a little easier to appreciate the new stuff!</p>
<blockquote><p>The code in this post is available on <a href="https://github.com/grantwinney/CSharpDotNetFeatures/tree/master/C%23%2010/MaxByMinBy"  target="_blank" rel="noreferrer">GitHub</a>, for you to use, extend, or just follow along while you read&hellip; and hopefully discover something new along the way!</p>
</blockquote>
<h2 class="relative group">Finding a Simple &lsquo;Max&rsquo;
    <div id="finding-a-simple-max" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#finding-a-simple-max" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Normally, when we want to find the max or min value of some collection of primitive types, then a simple call to <code>Max</code> or <code>Min</code> will do. With a list of integers, for example, the max number is the highest. No surprise there.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">maxNumber</span> <span class="p">=</span> <span class="k">new</span><span class="p">[]</span> <span class="p">{</span> <span class="m">4</span><span class="p">,</span> <span class="m">3</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="m">17</span><span class="p">,</span> <span class="m">9</span><span class="p">,</span> <span class="m">0</span> <span class="p">}.</span><span class="n">Max</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;The max number is: {maxNumber}&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// The max number is: 17</span></span></span></code></pre></div></div>
<p>Finding the max value in a list of integers</p>

<h2 class="relative group">Implementing <code>IComparer&lt;T&gt;</code>
    <div id="implementing-icomparert" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#implementing-icomparert" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>For other types, we can implement the <a href="https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.icomparer-1"  target="_blank" rel="noreferrer"><code>IComparer&lt;T&gt;</code> interface</a>, telling <code>Max</code> and <code>Min</code> what to use for the comparison. It can be really complex, or as simple as looking at one property:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">internal</span> <span class="k">record</span> <span class="nc">Employee</span><span class="p">(</span><span class="kt">string</span> <span class="n">Name</span><span class="p">,</span> <span class="kt">string</span> <span class="n">Dept</span><span class="p">,</span> <span class="kt">decimal</span> <span class="n">Salary</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">DateOnly</span> <span class="n">HireDate</span><span class="p">,</span> <span class="kt">int</span> <span class="n">SecurityLevel</span><span class="p">)</span> <span class="p">:</span> <span class="n">IComparable</span><span class="p">&lt;</span><span class="n">Employee</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">CompareTo</span><span class="p">(</span><span class="n">Employee</span><span class="p">?</span> <span class="n">other</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">other</span> <span class="p">==</span> <span class="kc">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="m">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">Salary</span> <span class="p">&gt;</span> <span class="n">other</span><span class="p">.</span><span class="n">Salary</span> <span class="p">?</span> <span class="m">1</span> <span class="p">:</span> <span class="n">Salary</span> <span class="p">&lt;</span> <span class="n">other</span><span class="p">.</span><span class="n">Salary</span> <span class="p">?</span> <span class="p">-</span><span class="m">1</span> <span class="p">:</span> <span class="m">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Employee record, implementing <code>IComparable&lt;T&gt;</code></p>
<p>In this case, the max or min <code>Employee</code> is just whoever makes the most or least:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">employees</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Employee</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span><span class="p">(</span><span class="s">&#34;Ed&#34;</span><span class="p">,</span>  <span class="s">&#34;Accounting&#34;</span><span class="p">,</span>  <span class="m">78_000</span><span class="p">,</span> <span class="k">new</span><span class="p">(</span><span class="m">2010</span><span class="p">,</span><span class="m">3</span><span class="p">,</span><span class="m">2</span><span class="p">),</span> <span class="m">1</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span><span class="p">(</span><span class="s">&#34;Ned&#34;</span><span class="p">,</span> <span class="s">&#34;Accounting&#34;</span><span class="p">,</span> <span class="m">120_000</span><span class="p">,</span> <span class="k">new</span><span class="p">(</span><span class="m">2000</span><span class="p">,</span><span class="m">2</span><span class="p">,</span><span class="m">6</span><span class="p">),</span> <span class="m">2</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span><span class="p">(</span><span class="s">&#34;Ted&#34;</span><span class="p">,</span> <span class="s">&#34;Accounting&#34;</span><span class="p">,</span>  <span class="m">94_000</span><span class="p">,</span> <span class="k">new</span><span class="p">(</span><span class="m">2020</span><span class="p">,</span><span class="m">1</span><span class="p">,</span><span class="m">1</span><span class="p">),</span> <span class="m">0</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span><span class="p">(</span><span class="s">&#34;Bob&#34;</span><span class="p">,</span> <span class="s">&#34;Accounting&#34;</span><span class="p">,</span>  <span class="m">55_000</span><span class="p">,</span> <span class="k">new</span><span class="p">(</span><span class="m">2015</span><span class="p">,</span><span class="m">7</span><span class="p">,</span><span class="m">2</span><span class="p">),</span> <span class="m">3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">max</span> <span class="p">=</span> <span class="n">employees</span><span class="p">.</span><span class="n">Max</span><span class="p">();</span>  <span class="c1">// Ned</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">min</span> <span class="p">=</span> <span class="n">employees</span><span class="p">.</span><span class="n">Min</span><span class="p">();</span>  <span class="c1">// Bob</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;{max!.Name} makes the most at {max.Salary:C}, &#34;</span> <span class="p">+</span>
</span></span><span class="line"><span class="cl">    <span class="s">$&#34;while {min!.Name} makes the least at {min.Salary:C}.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Ned makes the most at $120,000.00, while Bob makes the least at $55,000.00.</span></span></span></code></pre></div></div>

<h2 class="relative group">Using MinBy and MaxBy
    <div id="using-minby-and-maxby" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#using-minby-and-maxby" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><strong>The new</strong> <em><code>*MaxBy*</code></em> <strong>and</strong> <em><code>*MinBy*</code></em> <strong>methods let us specify a property to sort by, which allows for more flexbility in simple cases.</strong> We can sort employees by hire date, for example, and then sort by their security level immediately after:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">newestHire</span> <span class="p">=</span> <span class="n">employees</span><span class="p">.</span><span class="n">MaxBy</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">HireDate</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">oldestHire</span> <span class="p">=</span> <span class="n">employees</span><span class="p">.</span><span class="n">MinBy</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">HireDate</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;{newestHire!.Name} was the newest hire on {newestHire.HireDate}, &#34;</span> <span class="p">+</span>
</span></span><span class="line"><span class="cl">    <span class="s">$&#34;while {oldestHire!.Name} was hired long ago on {oldestHire.HireDate}.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Ted was the newest hire on 1/1/2020, while Ned was hired long ago on 2/6/2000.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">mostSecurePersonnel</span> <span class="p">=</span> <span class="n">employees</span><span class="p">.</span><span class="n">MaxBy</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">SecurityLevel</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">leastSecurePersonnel</span> <span class="p">=</span> <span class="n">employees</span><span class="p">.</span><span class="n">MinBy</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">SecurityLevel</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;{mostSecurePersonnel!.Name} has the highest security level, &#34;</span> <span class="p">+</span>
</span></span><span class="line"><span class="cl">    <span class="s">$&#34;while {leastSecurePersonnel!.Name} has the lowest.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Bob has the highest security level, while Ted has the lowest.</span></span></span></code></pre></div></div>
<p>Before, if we weren&rsquo;t implementing the interface, then we had to order the collection first and grab the first item, like this:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">maxBySalary</span> <span class="p">=</span> <span class="n">employees</span><span class="p">.</span><span class="n">OrderByDescending</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Salary</span><span class="p">).</span><span class="n">First</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">minBySalary</span> <span class="p">=</span> <span class="n">employees</span><span class="p">.</span><span class="n">OrderBy</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Salary</span><span class="p">).</span><span class="n">First</span><span class="p">();</span></span></span></code></pre></div></div>
<p>That isn&rsquo;t much longer, but <code>MaxBy</code> and <code>MinBy</code> just <em>read</em> better. <strong>The new methods make it easier to tell at a glance what the code is doing</strong>, and that&rsquo;s not a trivial thing. We don&rsquo;t have to reason out what the <code>OrderBy</code> is doing, nor do we have to open the class and investigate the <code>CompareTo</code> method.</p>
<p>Like anything though, there&rsquo;s limitations. Since we can only specify one property, we still need something like this to consider multiple properties:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">mostCompensatedEmployee</span> <span class="p">=</span> <span class="n">employees</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">OrderByDescending</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Salary</span><span class="p">)</span>   <span class="c1">// what if two people earn the same salary?</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">ThenByDescending</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Overtime</span><span class="p">)</span>  <span class="c1">// then we should consider overtime too</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">ThenByDescending</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Bonus</span><span class="p">)</span>     <span class="c1">// and their bonus, as a final tie-breaker</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">First</span><span class="p">();</span></span></span></code></pre></div></div>
<p>We can also use methods that provide a useful value, like the <code>Count()</code> method on a collection. One more example and then I&rsquo;m done! Here&rsquo;s two companies, and a quick use of <code>MaxBy</code> to grab the one with more employees:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">warnerEmps</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Company</span><span class="p">(</span><span class="s">&#34;Warner Bros&#34;</span><span class="p">,</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Employee</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span><span class="p">(</span><span class="s">&#34;Yakko&#34;</span><span class="p">,</span> <span class="s">&#34;IT&#34;</span><span class="p">,</span>  <span class="m">78_000</span><span class="p">,</span> <span class="k">new</span><span class="p">(</span><span class="m">2010</span><span class="p">,</span><span class="m">3</span><span class="p">,</span><span class="m">2</span><span class="p">),</span> <span class="m">1</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span><span class="p">(</span><span class="s">&#34;Wakko&#34;</span><span class="p">,</span> <span class="s">&#34;IT&#34;</span><span class="p">,</span> <span class="m">90_000</span><span class="p">,</span>  <span class="k">new</span><span class="p">(</span><span class="m">2000</span><span class="p">,</span><span class="m">2</span><span class="p">,</span><span class="m">6</span><span class="p">),</span> <span class="m">2</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span><span class="p">(</span><span class="s">&#34;Dot&#34;</span><span class="p">,</span> <span class="s">&#34;IT&#34;</span><span class="p">,</span> <span class="m">90_000</span><span class="p">,</span>  <span class="k">new</span><span class="p">(</span><span class="m">2000</span><span class="p">,</span><span class="m">2</span><span class="p">,</span><span class="m">6</span><span class="p">),</span> <span class="m">2</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">acmeEmps</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Company</span><span class="p">(</span><span class="s">&#34;Acme Inc&#34;</span><span class="p">,</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Employee</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span><span class="p">(</span><span class="s">&#34;Wile E Coyote&#34;</span><span class="p">,</span> <span class="s">&#34;IT&#34;</span><span class="p">,</span>  <span class="m">78_000</span><span class="p">,</span> <span class="k">new</span><span class="p">(</span><span class="m">2010</span><span class="p">,</span><span class="m">3</span><span class="p">,</span><span class="m">2</span><span class="p">),</span> <span class="m">1</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span><span class="p">(</span><span class="s">&#34;Road Runner&#34;</span><span class="p">,</span> <span class="s">&#34;IT&#34;</span><span class="p">,</span> <span class="m">90_000</span><span class="p">,</span>  <span class="k">new</span><span class="p">(</span><span class="m">2000</span><span class="p">,</span><span class="m">2</span><span class="p">,</span><span class="m">6</span><span class="p">),</span> <span class="m">2</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">largestCompany</span> <span class="p">=</span> <span class="k">new</span><span class="p">[]</span> <span class="p">{</span> <span class="n">warnerEmps</span><span class="p">,</span> <span class="n">acmeEmps</span> <span class="p">}.</span><span class="n">MaxBy</span><span class="p">(</span><span class="n">c</span> <span class="p">=&gt;</span> <span class="n">c</span><span class="p">.</span><span class="n">Employees</span><span class="p">.</span><span class="n">Count</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;{largestCompany!.Name} is the largest company.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Warner Bros is the largest company.</span></span></span></code></pre></div></div>
<p>I, for one, welcome any and all additions to LINQ, and can imagine that in many situations <code>MaxBy</code> and <code>MinBy</code> will make the code we write a little cleaner. Every bit counts!</p>

<h2 class="relative group">Learning More
    <div id="learning-more" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#learning-more" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you found this content useful, and would like to learn more about a variety of <a href="https://grantwinney.com/tags/csharp/"  target="_blank" rel="noreferrer">C#</a> features, check out my <a href="https://github.com/grantwinney/CSharpDotNetFeatures"  target="_blank" rel="noreferrer">CSharpDotNetFeatures repo</a>, where you&rsquo;ll find links to plenty more blog posts and practical examples!</p>
]]></content:encoded><media:content url="https://grantwinney.com/using-minby-and-maxby-in-csharp/feature.webp" medium="image" type="image/webp"/></item><item><title>Using CountBy and AggregateBy in C# 13 / .NET 9</title><link>https://grantwinney.com/using-linq-countby-and-aggregateby-in-csharp/</link><pubDate>Tue, 03 Dec 2024 19:23:03 +0000</pubDate><guid>https://grantwinney.com/using-linq-countby-and-aggregateby-in-csharp/</guid><description>It&amp;rsquo;s great to see Microsoft still giving us new things in LINQ. With C# 13 / .NET 9, we get CountBy and AggregateBy, so let&amp;rsquo;s see how to use them.</description><content:encoded><![CDATA[<p>Personally, I&rsquo;ve been a big fan of LINQ ever since it was added in C# 3 over 15 years ago. I prefer the sleek SQL-like syntax to verbose, nested <code>foreach</code> blocks. It&rsquo;s nice to see that Microsoft still values it enough to keep adding new things.</p>
<p>A few weeks ago, when they announced the <a href="https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-core#lifecycle"  target="_blank" rel="noreferrer">official release</a> of C# 13 / .NET 9, we got a couple new additions to LINQ – <code>CountBy</code> and <code>AggregateBy</code>. Let&rsquo;s see how to use them.</p>

<h2 class="relative group">But First&hellip;
    <div id="but-first" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#but-first" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Let&rsquo;s assume we have an <code>Employee</code> record and a handful of employees, with a variety of departments, job titles, and salaries:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">internal</span> <span class="k">record</span> <span class="nc">Employee</span><span class="p">(</span><span class="kt">string</span> <span class="n">Name</span><span class="p">,</span> <span class="kt">string</span> <span class="n">Dept</span><span class="p">,</span> <span class="kt">string</span> <span class="n">Title</span><span class="p">,</span> <span class="kt">decimal</span> <span class="n">Salary</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">EmployeeHelper</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">internal</span> <span class="kd">static</span> <span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">Employee</span><span class="p">&gt;</span> <span class="n">GetEmployees</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span>
</span></span><span class="line"><span class="cl"><span class="na">        [
</span></span></span><span class="line"><span class="cl"><span class="na">            new(&#34;Mary&#34;, &#34;Accounting&#34;, &#34;Accountant&#34;, 80_000),
</span></span></span><span class="line"><span class="cl"><span class="na">            new(&#34;Jean&#34;, &#34;Accounting&#34;, &#34;Manager&#34;, 140_000),
</span></span></span><span class="line"><span class="cl"><span class="na">            new(&#34;Bill&#34;, &#34;Accounting&#34;, &#34;Accountant&#34;, 90_000),
</span></span></span><span class="line"><span class="cl"><span class="na">            new(&#34;Dean&#34;, &#34;Accounting&#34;, &#34;Accountant&#34;, 84_000),
</span></span></span><span class="line"><span class="cl"><span class="na">            
</span></span></span><span class="line"><span class="cl"><span class="na">            new(&#34;Suzy&#34;, &#34;IT&#34;, &#34;Developer&#34;, 125_000),
</span></span></span><span class="line"><span class="cl"><span class="na">            new(&#34;Mike&#34;, &#34;IT&#34;, &#34;Manager&#34;, 160_000),
</span></span></span><span class="line"><span class="cl"><span class="na">            new(&#34;Adam&#34;, &#34;IT&#34;, &#34;Developer&#34;, 75_000),
</span></span></span><span class="line"><span class="cl"><span class="na">            
</span></span></span><span class="line"><span class="cl"><span class="na">            new(&#34;Leah&#34;, &#34;Sales&#34;, &#34;Manager&#34;, 99_000),
</span></span></span><span class="line"><span class="cl"><span class="na">            new(&#34;Glen&#34;, &#34;Sales&#34;, &#34;Sales Rep&#34;, 65_000),
</span></span></span><span class="line"><span class="cl"><span class="na">            new(&#34;Katy&#34;, &#34;Sales&#34;, &#34;Sales Rep&#34;, 102_000),
</span></span></span><span class="line"><span class="cl"><span class="na">            new(&#34;Mark&#34;, &#34;Sales&#34;, &#34;Sales Rep&#34;, 55_000),
</span></span></span><span class="line"><span class="cl"><span class="na">            new(&#34;Nate&#34;, &#34;Sales&#34;, &#34;Manager&#34;, 110_000),
</span></span></span><span class="line"><span class="cl"><span class="na">        ]</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>We&rsquo;ll use this for all the examples below.</p>

<h2 class="relative group">CountBy
    <div id="countby" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#countby" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The new <a href="https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.countby?view=net-9.0"  target="_blank" rel="noreferrer">CountBy</a> method returns a count of elements, grouped by key. This is something we could already do using <code>GroupBy</code> and <code>Count</code>, but now it&rsquo;s more streamlined.</p>
<p>Traditionally, if we wanted to get the number of employees in each department, we could write a bit of LINQ like this, grouping by department and then counting each group:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">empCountByDept</span> <span class="p">=</span> <span class="n">employees</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">GroupBy</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Dept</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">Select</span><span class="p">(</span><span class="n">grp</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="n">KeyValuePair</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">int</span><span class="p">&gt;(</span><span class="n">grp</span><span class="p">.</span><span class="n">Key</span><span class="p">,</span> <span class="n">grp</span><span class="p">.</span><span class="n">Count</span><span class="p">()));</span></span></span></code></pre></div></div>
<p>Using the new <code>CountBy</code> method, this becomes a <em>very</em> short statement:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">empCountByDept</span> <span class="p">=</span> <span class="n">employees</span><span class="p">.</span><span class="n">CountBy</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Dept</span><span class="p">);</span></span></span></code></pre></div></div>
<p>The output (in the <code>Program</code> class) for both is identical:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">Total employees in Accounting: 4
Total employees in IT: 3
Total employees in Sales: 5</code></pre></div>
<p>The only thing we need to specify is which value to group by, and then <code>CountBy</code> takes care of counting each of the groups for us. It&rsquo;s as simple as that!</p>

<h2 class="relative group">AggregateBy
    <div id="aggregateby" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#aggregateby" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The <code>Aggregate</code> method is one of the few LINQ methods that I&rsquo;ve struggled to find a good use for. It&rsquo;s flexible and powerful, but in many cases there are more straight-forward methods with a singular focus.</p>
<p>If you&rsquo;re unfamiliar with it, the basic process is to:</p>
<ol>
<li>Start with an initial seed value (or the first element in the collection, if omitted).</li>
<li>Run some function or operation over the first two elements.</li>
<li>Loop through the remaining elements, running the same function or operation against the result of step 2 and each new element.</li>
</ol>
<p>We can do a lot with it, but for common cases it&rsquo;s just not my go-to. Here&rsquo;s two ways to count the total number of managers, for example. One using <code>Aggregate</code> and the other using <code>Count</code>. Which is easier to read?</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">mgrCount1</span> <span class="p">=</span>
</span></span><span class="line"><span class="cl">    <span class="n">employees</span><span class="p">.</span><span class="n">Aggregate</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="p">(</span><span class="n">count</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Title</span> <span class="p">==</span> <span class="s">&#34;Manager&#34;</span> <span class="p">?</span> <span class="n">count</span> <span class="p">+</span> <span class="m">1</span> <span class="p">:</span> <span class="n">count</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">mgrCount2</span> <span class="p">=</span> <span class="n">employees</span><span class="p">.</span><span class="n">Count</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Title</span> <span class="p">==</span> <span class="s">&#34;Manager&#34;</span><span class="p">);</span></span></span></code></pre></div></div>
<p>That&rsquo;s all I&rsquo;ll say about that. Just keep it in mind when you look at the examples below. If you start thinking, <em>&ldquo;wait, couldn&rsquo;t you just use&hellip;?&rdquo;,</em> you&rsquo;re probably right.</p>
<p>Here&rsquo;s how we might use <code>GroupBy</code> and <code>Aggregate</code> to create a list of departments and the total salaries for each of those departments. First, we group by department, then we return a list of <code>KeyValuePair</code> objects with the department name (stored in <code>deptGroup.Key</code>) and an aggregate value. For the aggregate, we start with 0 (the seed), and then add each employee&rsquo;s salary to the previous total, one by one.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">totalSalariesByDept</span> <span class="p">=</span> <span class="n">employees</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">GroupBy</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Dept</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">Select</span><span class="p">(</span><span class="n">deptGroup</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="n">KeyValuePair</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">decimal</span><span class="p">&gt;(</span><span class="n">deptGroup</span><span class="p">.</span><span class="n">Key</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">deptGroup</span><span class="p">.</span><span class="n">Aggregate</span><span class="p">(</span><span class="m">0</span><span class="n">m</span><span class="p">,</span> <span class="p">(</span><span class="n">deptSal</span><span class="p">,</span> <span class="n">nextEmp</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">deptSal</span> <span class="p">+</span> <span class="n">nextEmp</span><span class="p">.</span><span class="n">Salary</span><span class="p">)));</span></span></span></code></pre></div></div>
<p>With the new <code>AggregateBy</code> method, the separate <code>GroupBy</code> goes away, just like with the <code>CountBy</code> method. The first parameter becomes the value to group by, the next is our starting value again, and finally the calculation to perform. The <code>deptSal</code> value is our ongoing total, and each employee salary is added to that, one at a time.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">totalSalariesByDept</span> <span class="p">=</span> <span class="n">employees</span><span class="p">.</span><span class="n">AggregateBy</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Dept</span><span class="p">,</span> <span class="m">0</span><span class="n">m</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="n">deptSal</span><span class="p">,</span> <span class="n">nextEmp</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">deptSal</span> <span class="p">+</span> <span class="n">nextEmp</span><span class="p">.</span><span class="n">Salary</span><span class="p">);</span></span></span></code></pre></div></div>
<p>The output from both is the same:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">Total salaries for Accounting: $394,000.00
Total salaries for IT: $360,000.00
Total salaries for Sales: $431,000.00</code></pre></div>
<p>Let&rsquo;s look at one more example, querying all employees making over 100k, by job title. First, we group by titles, then aggregate an ongoing total again. Unlike the simple salary before, we now have a Tuple that stores a counter <em>and</em> a total salary:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">employeesOver100k</span> <span class="p">=</span> <span class="n">employees</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">GroupBy</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Title</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">Select</span><span class="p">(</span><span class="k">group</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="n">KeyValuePair</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="p">(</span><span class="kt">int</span> <span class="n">Count</span><span class="p">,</span> <span class="kt">decimal</span> <span class="n">Salary</span><span class="p">)&gt;(</span><span class="k">group</span><span class="p">.</span><span class="n">Key</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="k">group</span><span class="p">.</span><span class="n">Aggregate</span><span class="p">((</span><span class="n">Count</span><span class="p">:</span> <span class="m">0</span><span class="p">,</span> <span class="n">Salary</span><span class="p">:</span> <span class="m">0</span><span class="n">m</span><span class="p">),</span> <span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">nextEmp</span><span class="p">)</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="n">nextEmp</span><span class="p">.</span><span class="n">Salary</span> <span class="p">&gt;</span> <span class="m">100_000</span> <span class="p">?</span> <span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">Count</span> <span class="p">+</span> <span class="m">1</span><span class="p">,</span> <span class="n">t</span><span class="p">.</span><span class="n">Salary</span> <span class="p">+</span> <span class="n">nextEmp</span><span class="p">.</span><span class="n">Salary</span><span class="p">)</span> <span class="p">:</span> <span class="n">t</span><span class="p">)));</span></span></span></code></pre></div></div>
<p>With the new method, <code>GroupBy</code> and explicitly creating a <code>KeyValuePair</code> goes away again, and the overall LINQ statement is shorter:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">employeesOver100k</span> <span class="p">=</span> <span class="n">employees</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">AggregateBy</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Title</span><span class="p">,</span> <span class="p">(</span><span class="n">Count</span><span class="p">:</span> <span class="m">0</span><span class="p">,</span> <span class="n">Salary</span><span class="p">:</span> <span class="m">0</span><span class="n">m</span><span class="p">),</span> <span class="p">(</span><span class="n">totals</span><span class="p">,</span> <span class="n">nextEmp</span><span class="p">)</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="n">nextEmp</span><span class="p">.</span><span class="n">Salary</span> <span class="p">&gt;</span> <span class="m">100_000</span> <span class="p">?</span> <span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">Count</span> <span class="p">+</span> <span class="m">1</span><span class="p">,</span> <span class="n">t</span><span class="p">.</span><span class="n">Salary</span> <span class="p">+</span> <span class="n">nextEmp</span><span class="p">.</span><span class="n">Salary</span><span class="p">)</span> <span class="p">:</span> <span class="n">t</span><span class="p">);</span></span></span></code></pre></div></div>
<p>And here&rsquo;s the output from both of these:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">0 accountant personnel earn over 100k, for a total of $0.00
3 manager personnel earn over 100k, for a total of $410,000.00
1 developer personnel earn over 100k, for a total of $125,000.00
1 sales rep personnel earn over 100k, for a total of $102,000.00</code></pre></div>

<h2 class="relative group">Learn More
    <div id="learn-more" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#learn-more" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you&rsquo;re interested in other changes in .NET 9 / C# 13, a good place to start (though definitely not to end) is with the official MS docs.</p>
<ul>
<li><a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-13"  target="_blank" rel="noreferrer">What&rsquo;s new in C# 13 | Microsoft Learn</a></li>
<li><a href="https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-9/overview"  target="_blank" rel="noreferrer">What&rsquo;s new in .NET 9 | Microsoft Learn</a></li>
</ul>
<p>If you found this content useful, and would like to learn more about a variety of <a href="https://grantwinney.com/tags/csharp/"  target="_blank" rel="noreferrer">C#</a> features, check out my <a href="https://github.com/grantwinney/CSharpDotNetFeatures"  target="_blank" rel="noreferrer">CSharpDotNetFeatures repo</a>, where you&rsquo;ll find links to plenty more blog posts and practical examples!</p>
]]></content:encoded><media:content url="https://grantwinney.com/using-linq-countby-and-aggregateby-in-csharp/feature.webp" medium="image" type="image/webp"/></item><item><title>Async, CancellationToken, and IProgress in 5 Short Examples</title><link>https://grantwinney.com/async-in-5-short-examples/</link><pubDate>Mon, 07 Oct 2024 02:43:55 +0000</pubDate><guid>https://grantwinney.com/async-in-5-short-examples/</guid><description>Async code isn&amp;rsquo;t always intuitive, but practicing helps. Let&amp;rsquo;s take a look at Async, CancellationToken, and IProgress, in a few short examples.</description><content:encoded><![CDATA[<p>Learning to write code asynchronously does <em>not</em> come naturally, at least not for this dev. We&rsquo;re wired to give the majority of our attention to <a href="https://www.psychologytoday.com/us/blog/creativity-without-borders/201405/the-myth-of-multitasking"  target="_blank" rel="noreferrer">one thing at a time</a>, so it can be difficult to write code that takes advantage of the fact that a computer can multitask <em>very</em> well.</p>
<p>A few years ago, I wrote <a href="https://grantwinney.com/using-async-await-and-task-to-keep-the-winforms-ui-more-responsive/"  target="_blank" rel="noreferrer">an async article</a> that shows (in a small way) how much faster it can be when we tell the computer to do many things at once. The trick is that we have to tell it which things can be done in parallel, and safely bring everything back together at the end.</p>
<p>I don&rsquo;t feel completely comfortable with <code>async</code> yet, but learning to use and get comfortable with it really intrigues me, in the same way that LINQ did years ago. Here&rsquo;s a few examples I put together to show off a little <code>async</code> code as well as cancellation tokens and how to report progress. I&rsquo;m not running a lot of parallel code here, but I <em>am</em> running things in a way that the task can be canceled and the UI won&rsquo;t be locked up.</p>
<blockquote><p>The code in this post is available on <a href="https://github.com/grantwinney/Surviving-WinForms/tree/master/Threading/SimpleAsyncExamples"  target="_blank" rel="noreferrer">GitHub</a>, for you to use, extend, or just follow along while you read&hellip; and hopefully discover something new along the way!</p>
</blockquote>
<h2 class="relative group">A 5-second Task that just completes
    <div id="a-5-second-task-that-just-completes" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#a-5-second-task-that-just-completes" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Here&rsquo;s a really simple example that just waits 5 seconds and then completes.</p>
<ul>
<li>Either the Run or the Cancel button (in later examples) should be enabled – never both at once</li>
<li>Task.Delay effectively pauses for x seconds, in a way that won&rsquo;t block the UI</li>
</ul>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="c1">// * AsyncUI form *</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Ex 1: Runs 5-second task and then completes</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">async</span> <span class="k">void</span> <span class="n">btnRunTask1_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">btnRunTask1</span><span class="p">.</span><span class="n">Enabled</span> <span class="p">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">lblStatusAsync1</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;Running...&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">try</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">await</span> <span class="n">SimpleAsyncMethods</span><span class="p">.</span><span class="n">Example1Async</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">MessageBox</span><span class="p">.</span><span class="n">Show</span><span class="p">(</span><span class="n">ex</span><span class="p">.</span><span class="n">Message</span><span class="p">,</span> <span class="s">&#34;An error occurred&#34;</span><span class="p">,</span> <span class="n">MessageBoxButtons</span><span class="p">.</span><span class="n">OK</span><span class="p">,</span> <span class="n">MessageBoxIcon</span><span class="p">.</span><span class="n">Error</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">btnRunTask1</span><span class="p">.</span><span class="n">Enabled</span> <span class="p">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">lblStatusAsync1</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;Completed!&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// * SimpleAsyncMethods class *</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Ex 1: Task.Delay is an easy way to simulate a long-running job</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="kd">async</span> <span class="n">Task</span> <span class="n">Example1Async</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="n">Task</span><span class="p">.</span><span class="n">Delay</span><span class="p">(</span><span class="m">5000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>For all of these examples, like the one above, there&rsquo;s two classes at play.</p>
<p>The first is the code-behind file of a WinForms app, where the button click event methods live, but you can use this kind of logic in any .NET app. The second is a static class that defines what each <code>Task</code> actually does.</p>

<h2 class="relative group">A 5-second Task that auto-cancels in 3 seconds
    <div id="a-5-second-task-that-auto-cancels-in-3-seconds" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#a-5-second-task-that-auto-cancels-in-3-seconds" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Here&rsquo;s another simple example. It also waits 5 seconds, although it checks for a pending cancellation once a second, and cancels the <code>Task</code> if needed. It also sets the <code>CancellationTokenSource</code> to automatically cancel after 3 seconds, so it&rsquo;ll never run to completion.</p>
<ul>
<li>The <code>CancellationTokeSource</code> is configured to automatically cancel after 3 seconds</li>
<li>The <code>Task</code> checks for a cancellation request every second, which throws an <code>OperationCancelledException</code> if needed</li>
<li>The caller is catching the <code>OperationCanceledException</code> to update the user</li>
</ul>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="c1">// * AsyncUI form *</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Ex 2: Runs 5-second task for 3 seconds and then cancels it</span>
</span></span><span class="line"><span class="cl"><span class="c1">//  A CancellationTokenSource can be automatically canceled after a set delay</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">async</span> <span class="k">void</span> <span class="n">btnRunTask2_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// By using &#39;using&#39;, the CancellationTokenSource will be disposed of automatically</span>
</span></span><span class="line"><span class="cl">    <span class="k">using</span> <span class="nn">CancellationTokenSource</span> <span class="n">cancelTokenSource2</span> <span class="p">=</span> <span class="k">new</span><span class="p">(</span><span class="n">TimeSpan</span><span class="p">.</span><span class="n">FromSeconds</span><span class="p">(</span><span class="m">3</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">try</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">await</span> <span class="n">SimpleAsyncMethods</span><span class="p">.</span><span class="n">Example2Async</span><span class="p">(</span><span class="n">cancelTokenSource2</span><span class="p">.</span><span class="n">Token</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">lblStatusAsync2</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;Completed!&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">catch</span> <span class="p">(</span><span class="n">OperationCanceledException</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// We can catch the OperationCanceledException and act on that</span>
</span></span><span class="line"><span class="cl">        <span class="n">lblStatusAsync2</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;Canceled!&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// * SimpleAsyncMethods class *</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Ex 2: By periodically calling ThrowIfCancellationRequested, we effectively check for a</span>
</span></span><span class="line"><span class="cl"><span class="c1">//  pending cancellation and throw an OperationCanceledException with a single line of code</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="kd">async</span> <span class="n">Task</span> <span class="n">Example2Async</span><span class="p">(</span><span class="n">CancellationToken</span> <span class="n">cToken</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kt">var</span> <span class="n">i</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="p">&lt;</span> <span class="m">5</span><span class="p">;</span> <span class="n">i</span><span class="p">++)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">cToken</span><span class="p">.</span><span class="n">ThrowIfCancellationRequested</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="k">await</span> <span class="n">Task</span><span class="p">.</span><span class="n">Delay</span><span class="p">(</span><span class="m">1000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>I&rsquo;ve left out most of the code that&rsquo;s enabling buttons, updating labels, etc, outside of stuff directly related to the <code>Task</code> to reduce some of the clutter, but if you check out the code on GitHub, you&rsquo;ll see it&rsquo;s still doing it.</p>

<h2 class="relative group">A 10-second Task that&rsquo;s user cancellable
    <div id="a-10-second-task-thats-user-cancellable" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#a-10-second-task-thats-user-cancellable" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>This example adds something new – a button the user can press to cancel the <code>Task</code>. It checks for a cancellation request every half-second for 10 seconds.</p>
<ul>
<li>The <code>Token.Register()</code> method lets us specify code to run on cancellation, instead of catching the exception</li>
<li>The code will likely still throw an <code>OperationCanceledException</code>, and here we&rsquo;ll just ignore it</li>
</ul>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="c1">// * AsyncUI form *</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Ex 3: Runs 10-second task, during which it can be canceled</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">CancellationTokenSource</span> <span class="n">_cancelTokenSource3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">async</span> <span class="k">void</span> <span class="n">btnRunTask3_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">_cancelTokenSource3</span> <span class="p">=</span> <span class="k">new</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Instead of catching the OperationCanceledException, we can register some code to run if the token is canceled.</span>
</span></span><span class="line"><span class="cl">    <span class="n">_cancelTokenSource3</span><span class="p">.</span><span class="n">Token</span><span class="p">.</span><span class="n">Register</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="n">lblStatusAsync3</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;Canceled!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">try</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">await</span> <span class="n">SimpleAsyncMethods</span><span class="p">.</span><span class="n">Example3Async</span><span class="p">(</span><span class="n">_cancelTokenSource3</span><span class="p">.</span><span class="n">Token</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">lblStatusAsync3</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;Completed!&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Since cancellation is being handled by Register() above, we&#39;ll ignore it here</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">ex</span> <span class="k">is</span> <span class="n">not</span> <span class="n">OperationCanceledException</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">MessageBox</span><span class="p">.</span><span class="n">Show</span><span class="p">(</span><span class="n">ex</span><span class="p">.</span><span class="n">Message</span><span class="p">,</span> <span class="s">&#34;An error occurred&#34;</span><span class="p">,</span> <span class="n">MessageBoxButtons</span><span class="p">.</span><span class="n">OK</span><span class="p">,</span> <span class="n">MessageBoxIcon</span><span class="p">.</span><span class="n">Error</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">finally</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Dispose of the CancellationTokenSource to free up resources</span>
</span></span><span class="line"><span class="cl">        <span class="n">_cancelTokenSource3</span><span class="p">.</span><span class="n">Dispose</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnCancelTask3_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">_cancelTokenSource3</span><span class="p">.</span><span class="n">Cancel</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// * SimpleAsyncMethods class *</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Ex 3: CancellationToken can be passed to, and handled by, other .NET classes that accept it </span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="kd">async</span> <span class="n">Task</span> <span class="n">Example3Async</span><span class="p">(</span><span class="n">CancellationToken</span> <span class="n">cToken</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kt">var</span> <span class="n">i</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="p">&lt;</span> <span class="m">20</span><span class="p">;</span> <span class="n">i</span><span class="p">++)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">await</span> <span class="n">Task</span><span class="p">.</span><span class="n">Delay</span><span class="p">(</span><span class="m">500</span><span class="p">,</span> <span class="n">cToken</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h2 class="relative group">An endless Task that&rsquo;s user cancellable
    <div id="an-endless-task-thats-user-cancellable" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#an-endless-task-thats-user-cancellable" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>This one&rsquo;s just a slight variation of the last one, representing some <code>Task</code> that runs a very long time (maybe any time the app is running).</p>
<ul>
<li>The <code>Task</code> runs forever until the user presses the Cancel button to stop it</li>
<li>It checks every tenth of a second for a cancellation request</li>
</ul>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="c1">// * AsyncUI form *</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Ex 4: Runs task indefinitely, during which it can be manually canceled</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">CancellationTokenSource</span> <span class="n">_cancelTokenSource4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">async</span> <span class="k">void</span> <span class="n">btnRunTask4_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">_cancelTokenSource4</span> <span class="p">=</span> <span class="k">new</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">_cancelTokenSource4</span><span class="p">.</span><span class="n">Token</span><span class="p">.</span><span class="n">Register</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="n">lblStatusAsync4</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;Canceled!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">try</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">await</span> <span class="n">SimpleAsyncMethods</span><span class="p">.</span><span class="n">Example4Async</span><span class="p">(</span><span class="n">_cancelTokenSource4</span><span class="p">.</span><span class="n">Token</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">lblStatusAsync4</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;Completed!&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">catch</span> <span class="p">(</span><span class="n">OperationCanceledException</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Eat the exception or, assuming exceptions bubble up to be handled elsewhere (i.e. logging),</span>
</span></span><span class="line"><span class="cl">        <span class="c1">//  remove this catch and allow it to be handled the same way</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">finally</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">_cancelTokenSource4</span><span class="p">.</span><span class="n">Dispose</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnCancelTask4_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">_cancelTokenSource4</span><span class="p">.</span><span class="n">Cancel</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// * SimpleAsyncMethods class *</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Ex 4: A task can run indefinitely until its canceled</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="kd">async</span> <span class="n">Task</span> <span class="n">Example4Async</span><span class="p">(</span><span class="n">CancellationToken</span> <span class="n">cToken</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="kc">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">cToken</span><span class="p">.</span><span class="n">ThrowIfCancellationRequested</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="k">await</span> <span class="n">Task</span><span class="p">.</span><span class="n">Delay</span><span class="p">(</span><span class="m">100</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h2 class="relative group">A 10-second Task that reports progress updates
    <div id="a-10-second-task-that-reports-progress-updates" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#a-10-second-task-that-reports-progress-updates" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>This last example introduces one more concept – the <code>IProgress&lt;T&gt;</code> construct, which allows us to send progress updates from the <code>Task</code> to the caller.</p>
<ul>
<li>The <code>T</code> can be any type – here it&rsquo;s a <code>Tuple&lt;int, string&gt;</code> representing a percentage complete and a status message</li>
<li>The <code>Task</code> returns a status update every second, which may be a message that it&rsquo;s been canceled</li>
<li>The <code>Task</code> also throws an <code>OperationCanceledException</code> on cancellation, because that&rsquo;s expected; as the caller, we can choose to ignore it</li>
<li>Since we&rsquo;re using a status message to update the user, we just eat the exception</li>
</ul>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="c1">// * AsyncUI form *</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Ex 5: Runs 10-second task that&#39;s cancellable AND reports progress as it runs</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">CancellationTokenSource</span> <span class="n">_cancelTokenSource5</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">async</span> <span class="k">void</span> <span class="n">btnRunTask5_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">_cancelTokenSource5</span> <span class="p">=</span> <span class="k">new</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">progress</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Progress</span><span class="p">&lt;(</span><span class="kt">int</span> <span class="n">percentage</span><span class="p">,</span> <span class="kt">string</span> <span class="n">message</span><span class="p">)&gt;();</span>
</span></span><span class="line"><span class="cl">    <span class="n">progress</span><span class="p">.</span><span class="n">ProgressChanged</span> <span class="p">+=</span> <span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="n">update</span><span class="p">)</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">lblStatusAsync5</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="n">update</span><span class="p">.</span><span class="n">message</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">progBarTask5</span><span class="p">.</span><span class="n">Value</span> <span class="p">=</span> <span class="n">update</span><span class="p">.</span><span class="n">percentage</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">try</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">await</span> <span class="n">SimpleAsyncMethods</span><span class="p">.</span><span class="n">Example5Async</span><span class="p">(</span><span class="n">_cancelTokenSource5</span><span class="p">.</span><span class="n">Token</span><span class="p">,</span> <span class="n">progress</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">catch</span> <span class="p">(</span><span class="n">OperationCanceledException</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// eat the exception.. nom nom nom</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">finally</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">_cancelTokenSource5</span><span class="p">.</span><span class="n">Dispose</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnCancelTask5_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">_cancelTokenSource5</span><span class="p">.</span><span class="n">Cancel</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// * SimpleAsyncMethods class *</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Ex 5: By checking IsCancellationRequested, we can cancel a long-running task</span>
</span></span><span class="line"><span class="cl"><span class="c1">//  Also, the IProgress&lt;T&gt; construct lets us pass progress updates to the caller</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="kd">async</span> <span class="n">Task</span> <span class="n">Example5Async</span><span class="p">(</span><span class="n">CancellationToken</span> <span class="n">cToken</span><span class="p">,</span> <span class="n">IProgress</span><span class="p">&lt;(</span><span class="kt">int</span><span class="p">,</span><span class="kt">string</span><span class="p">)&gt;</span> <span class="n">progress</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">secondsToRun</span> <span class="p">=</span> <span class="m">10</span><span class="n">m</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kt">var</span> <span class="n">i</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="p">&lt;</span> <span class="n">secondsToRun</span><span class="p">;</span> <span class="n">i</span><span class="p">++)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">percentage</span> <span class="p">=</span> <span class="p">(</span><span class="kt">int</span><span class="p">)((</span><span class="n">i</span> <span class="p">+</span> <span class="m">1</span><span class="p">)</span> <span class="p">/</span> <span class="n">secondsToRun</span> <span class="p">*</span> <span class="m">100</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">await</span> <span class="n">Task</span><span class="p">.</span><span class="n">Delay</span><span class="p">(</span><span class="m">1000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// If we want to perform some additional logic on cancellation,</span>
</span></span><span class="line"><span class="cl">        <span class="c1">//  we can throw an OperationCanceledException instead of using ThrowIfCancellationRequested</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">cToken</span><span class="p">.</span><span class="n">IsCancellationRequested</span> <span class="p">&amp;&amp;</span> <span class="n">percentage</span> <span class="p">!=</span> <span class="m">100</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">progress</span><span class="p">.</span><span class="n">Report</span><span class="p">((</span><span class="n">percentage</span><span class="p">,</span> <span class="s">$&#34;Canceled at {percentage}%&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">            <span class="k">throw</span> <span class="k">new</span> <span class="n">OperationCanceledException</span><span class="p">(</span><span class="s">&#34;Ex 5 Canceled!&#34;</span><span class="p">,</span> <span class="n">cToken</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span>
</span></span><span class="line"><span class="cl">            <span class="n">progress</span><span class="p">.</span><span class="n">Report</span><span class="p">((</span><span class="n">percentage</span><span class="p">,</span> <span class="s">$&#34;{percentage}% Complete!&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h2 class="relative group">Learning More
    <div id="learning-more" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#learning-more" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you&rsquo;d like to read more, I&rsquo;ve written a few other articles on <a href="https://grantwinney.com/tags/async/"  target="_blank" rel="noreferrer">async</a> that may or may not be helpful.</p>
<p>One of the best sources I&rsquo;ve found for all things async is Stephen Cleary&rsquo;s blog. After reading through his 5-part <em>(as of the time of this writing)</em> series on <a href="https://blog.stephencleary.com/2022/02/cancellation-1-overview.html"  target="_blank" rel="noreferrer">Cancellation</a> recently, I decided to play around a bit and share what I learned – hence the article you just read. 😄</p>
]]></content:encoded><media:content url="https://grantwinney.com/async-in-5-short-examples/feature.webp" medium="image" type="image/webp"/></item><item><title>Using TimeProvider and FakeTimeProvider in WinForms</title><link>https://grantwinney.com/using-timeprovider-and-faketimeprovider-in-winforms/</link><pubDate>Mon, 05 Feb 2024 05:07:51 +0000</pubDate><guid>https://grantwinney.com/using-timeprovider-and-faketimeprovider-in-winforms/</guid><description>Testing .NET code involving time has always been a pain, but the TimeProvider class (backported to the .NET Framework) gives us awesome new tools.</description><content:encoded><![CDATA[<p>Each new version of .NET brings great new tools. We got generics and LINQ in .NET 2 and 3, the <a href="https://grantwinney.com/using-async-await-and-task-to-keep-the-winforms-ui-more-responsive/"  target="_blank" rel="noreferrer">async/await model</a> in .NET 4.5, and <a href="https://grantwinney.com/using-string-interpolation-to-craft-readable-strings"  target="_blank" rel="noreferrer">string interpolation</a> in .NET 4.6. Okay, that last one&rsquo;s not in the same league as the other ones, but I use string interpolation <em>all</em> the time.</p>
<p>Unfortunately for those of us working on legacy WinForms apps, we don&rsquo;t often get to use the latest and greatest, like <a href="https://grantwinney.com/csharp-generic-math-support/"  target="_blank" rel="noreferrer">generic math support</a> or <a href="https://grantwinney.com/whats-a-list-pattern-in-csharp"  target="_blank" rel="noreferrer">list patterns</a> from .NET 7. One new feature from .NET 8 though – the <code>TimeProvider</code> class – <em>is</em> available to .NET Framework users. Let&rsquo;s see how.</p>

<h2 class="relative group">Backporting TimeProvider
    <div id="backporting-timeprovider" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#backporting-timeprovider" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Without getting into all the basics here (I&rsquo;ve already written about <a href="https://grantwinney.com/how-to-use-timeprovider-and-faketimeprovider/"  target="_blank" rel="noreferrer">TimeProvider, testing TimeProvider</a>, and <a href="https://grantwinney.com/how-to-use-timeprovider-and-faketimeprovider-to-test-timers/"  target="_blank" rel="noreferrer">testing TimeProvider timers</a> before), one of the nice things the .NET team did for us was to backport <code>TimeProvider</code>.</p>
<p>It&rsquo;s available for use in earlier .NET versions, including .NET Framework 4.62 and above, thanks to the <a href="https://www.nuget.org/packages/Microsoft.Bcl.TimeProvider/#readme-body-tab"  target="_blank" rel="noreferrer">Microsoft.Bcl.TimeProvider</a> NuGet package:</p>
<blockquote><p>Microsoft.Bcl.TimeProvider provides time abstraction support for apps targeting .NET 7 and earlier, as well as those intended for the .NET Framework. For apps targeting .NET 8 and newer versions, referencing this package is unnecessary, as the types it contains are already included in the .NET 8 and higher platform versions.</p>
</blockquote>
<h2 class="relative group">Using TimeProvider in WinForms
    <div id="using-timeprovider-in-winforms" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#using-timeprovider-in-winforms" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I&rsquo;ll try to keep this fairly simple, but we should setup a few things first before we get to the good stuff.</p>
<blockquote><p>The code in this post is available on <a href="https://github.com/grantwinney/Surviving-WinForms/tree/master/Time/TimeAbstraction"  target="_blank" rel="noreferrer">GitHub</a>, for you to use, expand upon, or just follow along while you read&hellip; and hopefully discover something new!</p>
</blockquote>
<h3 class="relative group">Reference the TimeProvider Package
    <div id="reference-the-timeprovider-package" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#reference-the-timeprovider-package" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The first thing we need to add is a reference to <a href="https://www.nuget.org/packages/Microsoft.Bcl.TimeProvider/#readme-body-tab"  target="_blank" rel="noreferrer">Microsoft.Bcl.TimeProvider</a> – won&rsquo;t get very far without that. 😄</p>

<h3 class="relative group">Configure Dependency Injection
    <div id="configure-dependency-injection" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#configure-dependency-injection" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>And while we could just reference <code>TimeProvider.System</code> directly to access the new class, that makes it tough to do any meaningful testing later on. So let&rsquo;s configure the app for Dependency Injection, by referencing the <a href="https://www.nuget.org/packages/Microsoft.Extensions.DependencyInjection/"  target="_blank" rel="noreferrer">Microsoft.Extensions.DependencyInjection</a> package and then creating a separate class to register any dependencies.</p>
<p>Right now, all we need is <code>TimeProvider.System</code>, so that whenever a request is made for the abstract <code>TimeProvider</code> class, the <a href="https://grantwinney.com/difference-between-singleton-scoped-transient/"  target="_blank" rel="noreferrer">singleton</a> instance is returned instead:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Services</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="n">IServiceProvider</span> <span class="n">ServiceProvider</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="k">void</span> <span class="n">RegisterServices</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">services</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ServiceCollection</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">services</span><span class="p">.</span><span class="n">AddSingleton</span><span class="p">(</span><span class="n">TimeProvider</span><span class="p">.</span><span class="n">System</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">ServiceProvider</span> <span class="p">=</span> <span class="n">services</span><span class="p">.</span><span class="n">BuildServiceProvider</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="n">T</span> <span class="n">Get</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;()</span> <span class="k">where</span> <span class="n">T</span> <span class="p">:</span> <span class="k">class</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">(</span><span class="n">T</span><span class="p">)</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetService</span><span class="p">(</span><span class="k">typeof</span><span class="p">(</span><span class="n">T</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>We can call the above code from the <code>Program.cs</code> file, right before showing the main form:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">static</span> <span class="k">void</span> <span class="n">Main</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Application</span><span class="p">.</span><span class="n">EnableVisualStyles</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">Application</span><span class="p">.</span><span class="n">SetCompatibleTextRenderingDefault</span><span class="p">(</span><span class="kc">false</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">Services</span><span class="p">.</span><span class="n">RegisterServices</span><span class="p">();</span>  <span class="c1">// register the dependencies</span>
</span></span><span class="line"><span class="cl">    <span class="n">Application</span><span class="p">.</span><span class="n">Run</span><span class="p">(</span><span class="k">new</span> <span class="n">Form1</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h3 class="relative group">Using TimeProvider (directly from the Form)
    <div id="using-timeprovider-directly-from-the-form" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#using-timeprovider-directly-from-the-form" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>After requesting an instance of <code>TimeProvider</code> once in the constructor, getting the local time from anywhere is a simple one-liner. While we&rsquo;re at it, let&rsquo;s create a timer too, to update the displayed time every second.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">readonly</span> <span class="n">TimeProvider</span> <span class="n">tp</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="n">Form1</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">InitializeComponent</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">tp</span> <span class="p">=</span> <span class="n">Services</span><span class="p">.</span><span class="n">Get</span><span class="p">&lt;</span><span class="n">TimeProvider</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">timer</span> <span class="p">=</span> <span class="n">tp</span><span class="p">.</span><span class="n">CreateTimer</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">callback</span><span class="p">:</span> <span class="p">(</span><span class="n">state</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">Invoke</span><span class="p">(</span><span class="k">new</span> <span class="n">Action</span><span class="p">(()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="n">tslCurrentTimeUpdate</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="n">tp</span><span class="p">.</span><span class="n">GetLocalNow</span><span class="p">().</span><span class="n">ToString</span><span class="p">(</span><span class="s">&#34;T&#34;</span><span class="p">))),</span>
</span></span><span class="line"><span class="cl">        <span class="n">state</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">dueTime</span><span class="p">:</span> <span class="n">TimeSpan</span><span class="p">.</span><span class="n">Zero</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">period</span><span class="p">:</span> <span class="n">TimeSpan</span><span class="p">.</span><span class="n">FromSeconds</span><span class="p">(</span><span class="m">1</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">Form1_Shown</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">tslCurrentTimeOnce</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="n">tp</span><span class="p">.</span><span class="n">GetLocalNow</span><span class="p">().</span><span class="n">ToString</span><span class="p">(</span><span class="s">&#34;T&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Here&rsquo;s the <code>StatusStrip</code> area of the app while running the app. The time on the left never changes, while the time on the right is refreshed every second.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-timeprovider-and-faketimeprovider-in-winforms/currenttime.gif"
    width="466"
      height="22"></figure>

<h3 class="relative group">Using TimeProvider (from a separate class)
    <div id="using-timeprovider-from-a-separate-class" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#using-timeprovider-from-a-separate-class" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Ideally, we should try to keep business logic out of code-behind files as much as possible (using patterns like <a href="https://grantwinney.com/its-possible-to-test-a-winforms-app-using-mvp/"  target="_blank" rel="noreferrer">MVP</a>), so let&rsquo;s create a <code>DiscountLogic</code> class to play around with <code>TimeProvider</code> some more.</p>
<p>It has one property (for returning a discount percentage based on the current date) and one method (for returning a discounted price, based on the discount percentage). By accepting a <code>TimeProvider</code> in the constructor, and implementing an interface, we set things up nicely later on for testing.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">DiscountLogic</span> <span class="p">:</span> <span class="n">IDiscountLogic</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">readonly</span> <span class="n">TimeProvider</span> <span class="n">_timeProvider</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">DiscountLogic</span><span class="p">(</span><span class="n">TimeProvider</span> <span class="n">timeProvider</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">_timeProvider</span> <span class="p">=</span> <span class="n">timeProvider</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">decimal</span> <span class="n">DailyDiscount</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">get</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">now</span> <span class="p">=</span> <span class="n">_timeProvider</span><span class="p">.</span><span class="n">GetLocalNow</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">discountPercent</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">now</span><span class="p">.</span><span class="n">DayOfWeek</span> <span class="p">!=</span> <span class="n">DayOfWeek</span><span class="p">.</span><span class="n">Saturday</span> <span class="p">&amp;&amp;</span> <span class="n">now</span><span class="p">.</span><span class="n">DayOfWeek</span> <span class="p">!=</span> <span class="n">DayOfWeek</span><span class="p">.</span><span class="n">S</span>
</span></span><span class="line"><span class="cl">                <span class="n">discountPercent</span> <span class="p">+=</span> <span class="m">10</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="n">discountPercent</span> <span class="p">+=</span>
</span></span><span class="line"><span class="cl">                <span class="n">now</span><span class="p">.</span><span class="n">Month</span> <span class="p">&gt;=</span> <span class="m">11</span> <span class="p">||</span> <span class="n">now</span><span class="p">.</span><span class="n">Month</span> <span class="p">&lt;=</span> <span class="m">2</span> <span class="p">?</span> <span class="m">30</span> <span class="p">:</span>      <span class="c1">// Nov, Dec, Jan, Feb</span>
</span></span><span class="line"><span class="cl">                <span class="n">now</span><span class="p">.</span><span class="n">Month</span> <span class="p">&gt;=</span> <span class="m">3</span> <span class="p">&amp;&amp;</span> <span class="n">now</span><span class="p">.</span><span class="n">Month</span> <span class="p">&lt;=</span> <span class="m">5</span> <span class="p">?</span> <span class="m">20</span> <span class="p">:</span>       <span class="c1">// Mar, Apr, May</span>
</span></span><span class="line"><span class="cl">                <span class="n">now</span><span class="p">.</span><span class="n">Month</span> <span class="p">&gt;=</span> <span class="m">9</span> <span class="p">&amp;&amp;</span> <span class="n">now</span><span class="p">.</span><span class="n">Month</span> <span class="p">&lt;=</span> <span class="m">10</span> <span class="p">?</span> <span class="m">10</span> <span class="p">:</span> <span class="m">0</span><span class="p">;</span>   <span class="c1">// Sep, Oct</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">discountPercent</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">decimal</span> <span class="n">GetDiscountPrice</span><span class="p">(</span><span class="kt">decimal</span> <span class="n">originalPrice</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">originalPrice</span> <span class="p">*</span> <span class="p">((</span><span class="m">100</span> <span class="p">-</span> <span class="n">DailyDiscount</span><span class="p">)</span> <span class="p">/</span> <span class="m">100</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">interface</span> <span class="nc">IDiscountLogic</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">decimal</span> <span class="n">DailyDiscount</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kt">decimal</span> <span class="n">GetDiscountPrice</span><span class="p">(</span><span class="kt">decimal</span> <span class="n">originalPrice</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>One more line in the <code>Services.cs</code> class registers the new class:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">services</span><span class="p">.</span><span class="n">AddTransient</span><span class="p">&lt;</span><span class="n">IDiscountLogic</span><span class="p">,</span> <span class="n">DiscountLogic</span><span class="p">&gt;();</span></span></span></code></pre></div></div>
<p>And then back in the Form, we can get an instance of <code>DiscountLogic</code> and display a message about today&rsquo;s discount:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">Form1_Shown</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">dl</span> <span class="p">=</span> <span class="n">Services</span><span class="p">.</span><span class="n">Get</span><span class="p">&lt;</span><span class="n">IDiscountLogic</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl">    <span class="n">lblDiscount</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span>
</span></span><span class="line"><span class="cl">        <span class="s">$&#34;The discount for {tp.GetLocalNow().ToString(&#34;</span><span class="n">dddd</span><span class="p">,</span> <span class="n">MMM</span> <span class="n">d</span><span class="s">&#34;)} is {dl.DailyDiscount}%. &#34;</span> <span class="p">+</span>
</span></span><span class="line"><span class="cl">        <span class="s">$&#34;A $5.00 icecream costs ${dl.GetDiscountPrice(5):N2} today.&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-timeprovider-and-faketimeprovider-in-winforms/image-2.png"
    width="468"
      height="90"></figure>

<h2 class="relative group">Testing TimeProvider Using FakeTimeProvider
    <div id="testing-timeprovider-using-faketimeprovider" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#testing-timeprovider-using-faketimeprovider" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There&rsquo;s a <code>FakeTimeProvider</code> class that helps us test <code>TimeProvider</code>, provided by the <a href="https://www.nuget.org/packages/Microsoft.Extensions.TimeProvider.Testing#readme-body-tab"  target="_blank" rel="noreferrer">Microsoft.Extensions.TimeProvider.Testing</a> package. We&rsquo;ll add that to an NUnit test project, which happens to target .NET 6. I&rsquo;d prefer to be able to target the .NET Framework but I&rsquo;m not sure it&rsquo;s possible – more on that later.</p>

<h3 class="relative group">Fast-forwarding through time
    <div id="fast-forwarding-through-time" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#fast-forwarding-through-time" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Here&rsquo;s where the power of all this really shows. Testing code that uses time has always been difficult.. and limited.</p>
<p>We might write a test that simply calls the <code>DailyDiscount</code> property to see what the percentage is for today. Of course, based on the logic, if it&rsquo;s not a summer weekend when the following test runs, it&rsquo;ll fail. We need a way to fake out what time it is.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="na">[Test]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">GivenSummer_WhenWeekend_ThenNoDiscount</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">discountLogic</span><span class="p">.</span><span class="n">DailyDiscount</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="m">0</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-timeprovider-and-faketimeprovider-in-winforms/image-1.png"
    width="910"
      height="295"></figure>
<p>The <code>FakeTimeProvider</code> class lets us jump to a future date, like the tests below that jump forward <em>(in the US)</em> to a summer weekend and then a winter weekday, to make sure the appropriate discount (if any) is applied for different times of the year.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="na">[TestFixture]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">DiscountTests</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="n">FakeTimeProvider</span> <span class="n">fakeTimeProvider</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="n">DiscountLogic</span> <span class="n">discountLogic</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [SetUp]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">Setup</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">fakeTimeProvider</span> <span class="p">=</span> <span class="k">new</span> <span class="n">FakeTimeProvider</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">fakeTimeProvider</span><span class="p">.</span><span class="n">SetLocalTimeZone</span><span class="p">(</span><span class="n">TimeZoneInfo</span><span class="p">.</span><span class="n">Utc</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">discountLogic</span> <span class="p">=</span> <span class="k">new</span> <span class="n">DiscountLogic</span><span class="p">(</span><span class="n">fakeTimeProvider</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [Test]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">GivenSummer_WhenWeekend_ThenNoDiscount</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">fakeTimeProvider</span><span class="p">.</span><span class="n">SetUtcNow</span><span class="p">(</span><span class="n">DateTimeOffset</span><span class="p">.</span><span class="n">Parse</span><span class="p">(</span><span class="s">&#34;7/27/2024&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">discountLogic</span><span class="p">.</span><span class="n">DailyDiscount</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="m">0</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">discountLogic</span><span class="p">.</span><span class="n">GetDiscountPrice</span><span class="p">(</span><span class="m">5</span><span class="p">),</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="m">5</span><span class="n">m</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [Test]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">GivenWinter_WhenWeekday_ThenLargeDiscount</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">fakeTimeProvider</span><span class="p">.</span><span class="n">SetUtcNow</span><span class="p">(</span><span class="n">DateTimeOffset</span><span class="p">.</span><span class="n">Parse</span><span class="p">(</span><span class="s">&#34;2/2/2024&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">discountLogic</span><span class="p">.</span><span class="n">DailyDiscount</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="m">40</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">discountLogic</span><span class="p">.</span><span class="n">GetDiscountPrice</span><span class="p">(</span><span class="m">8</span><span class="p">),</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="m">4.8</span><span class="n">m</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span></span></span></code></pre></div></div>
<p>It&rsquo;s worth noting in the above code that I set the local time zone to UTC (something else <code>FakeTimeProvider</code> lets us do), to avoid any weirdness with the test suite running in different timezones on different systems and potentially failing.</p>

<h3 class="relative group">It doesn&rsquo;t work with .NET Framework&hellip; maybe
    <div id="it-doesnt-work-with-net-framework-maybe" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#it-doesnt-work-with-net-framework-maybe" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The <code>TimeProvider</code> works with .NET Framework 4.6.2, so it makes sense that <code>FakeTimeProvider</code> would too – and it claims it does on the NuGet package page. But when I created a test project using that version, it wouldn&rsquo;t run my tests.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-timeprovider-and-faketimeprovider-in-winforms/image.png"
    width="719"
      height="279"></figure>
<p>There was a warning in the Error List panel that suggested setting a flag, which seems like overkill (assuming it works at all).</p>
<blockquote><p>Microsoft.Extensions.TimeProvider.Testing 8.1.0 doesn&rsquo;t support net462 and has not been tested with it. Consider upgrading your TargetFramework to net6.0 or later. You may also set <SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings> in the project file to ignore this warning and attempt to run in this unsupported configuration at your own risk.</p>
</blockquote><p>So as far as I can tell, we can&rsquo;t use <code>FakeTimeProvider</code> in a .NET Framework test suite, although a test suite running a newer version of .NET shouldn&rsquo;t have a problem testing a legacy WinForms app. Targeting .NET 6 in the test suite works fine.</p>
<p>If someone finds a workaround, or that I missed something, I&rsquo;d love to know what it is!</p>
]]></content:encoded><media:content url="https://grantwinney.com/using-timeprovider-and-faketimeprovider-in-winforms/feature.webp" medium="image" type="image/webp"/></item><item><title>SSRS won't show the updated default value for a parameter</title><link>https://grantwinney.com/ssrs-wont-show-the-updated-default-value-for-a-parameter/</link><pubDate>Wed, 31 Jan 2024 02:40:31 +0000</pubDate><guid>https://grantwinney.com/ssrs-wont-show-the-updated-default-value-for-a-parameter/</guid><description>Changed the default value for a report parameter, but it&amp;rsquo;s not actually updating in SSRS? That&amp;rsquo;s by design. Let&amp;rsquo;s find a way around it.</description><content:encoded><![CDATA[<p>I recently had to figure out why a minor change to an SSRS report wasn&rsquo;t deploying correctly. It was just a minor change to the default value of a parameter on one report, nothing special.</p>
<p>One step of the deployment process involves uploading some <a href="https://learn.microsoft.com/en-us/sql/reporting-services/reports/report-definition-language-ssrs"  target="_blank" rel="noreferrer">RDL</a> files via SSRS&rsquo;s <a href="https://learn.microsoft.com/en-us/sql/reporting-services/report-server-web-service/accessing-the-soap-api"  target="_blank" rel="noreferrer">SOAP API</a>. I expected the <a href="https://learn.microsoft.com/en-us/dotnet/api/reportservice2010.reportingservice2010.createcatalogitem?view=sqlserver-2016#reportservice2010-reportingservice2010-createcatalogitem%5C%28system-string-system-string-system-string-system-boolean-system-byte%5C%28%5C%29-reportservice2010-property%5C%28%5C%29-reportservice2010-warning%5C%28%5C%29@%5C%29"  target="_blank" rel="noreferrer">CreateCatalogItem</a> endpoint to update the default parameter for the report in SSRS, like it would for other changes. But it didn&rsquo;t.</p>

<h2 class="relative group">Why won&rsquo;t the default value update?
    <div id="why-wont-the-default-value-update" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#why-wont-the-default-value-update" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Skip to the next section if you&rsquo;re not interested in the <em>&ldquo;why&rdquo;,</em> but I was. A few online searches turned up years-old threads with people complaining about this scenario_._ There&rsquo;s a number of suggested fixes, which I&rsquo;ll get to in a minute, but I was curious &hellip; was this a bug or &ldquo;by design&rdquo;?</p>
<p>A <a href="https://dba.stackexchange.com/a/149666"  target="_blank" rel="noreferrer">post</a> by a DBA in 2016 gave me the first hint that it might be the latter:</p>
<blockquote><p>This is a &ldquo;known&rdquo; issue/feature according to Microsoft. Descriptions, parameter defaults, subscriptions, etc. all fall under report &ldquo;meta data&rdquo; and are maintained separately on the Report Manager server. The only way to get these to change on the Report Manager is to either manipulate them manually or delete the report and upload anew.</p>
</blockquote><p>A link in that thread led to a Microsoft Connect (discontinued and deleted.. thanks Microsoft) <a href="https://web.archive.org/web/20130313052333/https://connect.microsoft.com/SQLServer/feedback/details/299372/report-parameter-defaults-not-updated-during-deployment"  target="_blank" rel="noreferrer">post from 2007</a>, detailing the same issue I was seeing:</p>
<blockquote><p>Parameter defaults do not get updated when re-deploying existing reports. These either have to be updated manually or the reports deleted and re-deployed. The latter regenerates all report ID&rsquo;s (GUID&rsquo;s) and makes traking usage from the ExecutionLog more difficult.</p>
<p>This is explained here as being by design however I can&rsquo;t envisage parameter defaults and prompts being maintaned by an administrator. An override mechanism similar to OverwriteDataSources should be added to Reporting Services projects to allow deploymnent from Visual Studio.</p>
</blockquote><p>That led to <a href="https://web.archive.org/web/20100828132548/http://social.msdn.microsoft.com/forums/en-US/sqlreportingservices/thread/c6c5b75a-fcbd-48f4-a30d-852d443d0a74/"  target="_blank" rel="noreferrer">an even older post</a> from a Microsoft Forums (also discontinued and deleted.. thanks <em>again</em> Microsoft) thread in 2005, with an answer from a SQL Server program manager at MS, which is probably the closest we&rsquo;ll get to an authoritative answer: <em>(emphasis in Brian&rsquo;s answer is mine)</em></p>
<blockquote><p>OP: For testing purposes, I had placed default values in my report parameters. I deployed the whole suite of reports once and tested them. Then I eliminated the default parameters and redeployed, but the server doesn&rsquo;t pick up the changes. Other changes are updated fine, but for some reason it&rsquo;s hanging on to my default parameter values? Why? Is there a way around this?</p>
<p>Andrew Sears: Perhaps deleting the reports and recreating them would help?</p>
<p>OP: Yes, that&rsquo;s what I&rsquo;m doing. Seems stupid, though. Which table in the ReportServer database contains the default parameter info?</p>
<p>Brian Welcker: Modifying the tables directly is not supported. However, you can see this information in the Parameter column in the Catalog table. <strong>The original intent was to allow for an admin to change the defaults, prompts, etc. and not have it overwritten by the report developer. We should provide a way to override this behavior.</strong></p>
</blockquote><p>I wish that override would&rsquo;ve happened a long time ago, but I guess I&rsquo;ve got my answer. Microsoft created separate logic for updating certain aspects of a report, and that logic seems to be preventing the API call from doing what one would expect it to do.</p>

<h2 class="relative group">Can we force the default value to update?
    <div id="can-we-force-the-default-value-to-update" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#can-we-force-the-default-value-to-update" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>As for solutions, none of the ones I saw were particularly good. One common suggestion is to go in through the web interface for SSRS and change the default in there. If you&rsquo;re into automated deploys though, then manual steps like these are a no-go.</p>
<p>Another oft-suggested workaround is to drop the report and recreate it, but that&rsquo;s a no-go too. SSRS links a lot of things together, and deleting a report will almost certainly delete other records in other tables.. including any <a href="https://learn.microsoft.com/en-us/sql/reporting-services/subscriptions/subscriptions-and-delivery-reporting-services"  target="_blank" rel="noreferrer">subscriptions</a> that have been created against the report.</p>
<p>Yet another workaround is to rename the parameter in the report, but that&rsquo;s still a problem for subscriptions. If one parameter disappears, and a new one materializes out of thin air, how could that <em>not</em> break subscriptions? You and I may know it&rsquo;s a direct replacement, but SSRS won&rsquo;t.</p>
<p>Since the problem seems to be that SSRS ignores default values when a report is updated, it&rsquo;s worth asking the question – what if SSRS didn&rsquo;t have parameters to compare to? That seemed to be the thinking of user JonoB in <a href="https://stackoverflow.com/a/76174818"  target="_blank" rel="noreferrer">another thread</a>:</p>
<blockquote><p>Instead you can clear the parameters stored in the Database with the following sql, and then re-upload the report in place and it will regenerate the parameters.</p>
<p><code>EXEC [ReportServer].[dbo].[SetParameters] '/MyReportPath/MyReport', NULL</code></p>
</blockquote><p>I tried it out, and as far as I can see, it has no adverse effects, despite Brian&rsquo;s admonition in 2005 that <em>&ldquo;modifying the tables directly is not supported&rdquo;.</em> There&rsquo;s a lot of built-in stored procs in SSRS, and this one just sets the <code>dbo.Catalog.Parameter</code> column for a given report. You can even, as JonoB suggests, pass in <code>NULL</code> to clear out the <code>Parameter</code> column.</p>
<p>After doing that, running the original API endpoint then repopulates <code>dbo.Catalog.Parameter</code> with the correct values.. including any changes to default values! It doesn&rsquo;t seem to touch anything else, other than making a call to the <code>dbo.FlushReportFromCache</code> stored proc that updates some cache in the <a href="https://learn.microsoft.com/en-us/previous-versions/sql/sql-server-2008/ms156016%28v=sql.100%29#report-server-temporary-database"  target="_blank" rel="noreferrer">report server temp database</a>.</p>
<p>You might be able to just update the column directly with a simple <code>UPDATE</code> statement, but if someone thought it was important to update the cache in the temp db after updating the <code>Parameter</code> column, it probably is.</p>

<h2 class="relative group">Final thoughts
    <div id="final-thoughts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#final-thoughts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I&rsquo;m annoyed the answer isn&rsquo;t in the API docs, at least not that I found. Instead, the answer is buried in old (thankfully archived) posts on two separate doc sites that Microsoft killed off. I <em>really</em> wish they&rsquo;d leave this stuff around, or migrate it in a way that forwards the old url to whatever new site they decide to create each year, especially considering how many companies are running legacy software running on their old technologies.</p>
<p>Since it sounds like the differing logic is role-based, I wonder if it&rsquo;s possible to change the role or privileges of the user used to call the API? Or just call the API as an &ldquo;admin&rdquo;, if possible? For now, calling the <code>SetParameters</code> proc before the <code>CreateCatalogItem</code> endpoint seems to be enough.</p>
<p>If you find better documentation, or a better solution, feel free to share it below.. I&rsquo;d love to hear about it!</p>
]]></content:encoded><media:content url="https://grantwinney.com/ssrs-wont-show-the-updated-default-value-for-a-parameter/feature.webp" medium="image" type="image/webp"/></item><item><title>How to use (and test) TimeProvider timers in .NET</title><link>https://grantwinney.com/how-to-use-timeprovider-and-faketimeprovider-to-test-timers/</link><pubDate>Thu, 11 Jan 2024 04:34:20 +0000</pubDate><guid>https://grantwinney.com/how-to-use-timeprovider-and-faketimeprovider-to-test-timers/</guid><description>Testing timers in C# is difficult, but .NET 8 (C# 12) adds an abstract TimeProvider class that makes it easier. Let&amp;rsquo;s take a closer look.</description><content:encoded><![CDATA[<p>The .NET 8 (C# 12) <a href="https://devblogs.microsoft.com/dotnet/announcing-dotnet-8/"  target="_blank" rel="noreferrer">release</a> included new constructs for abstracting time and timers, two things that have traditionally been a pain when it comes to testing. A few days ago, <a href="https://grantwinney.com/how-to-use-timeprovider-and-faketimeprovider/"  target="_blank" rel="noreferrer">I took a first look at time abstraction</a> using the new <a href="https://learn.microsoft.com/en-us/dotnet/api/system.timeprovider"  target="_blank" rel="noreferrer">TimeProvider</a> abstract class, and then wrote some tests using the new <a href="https://github.com/dotnet/extensions/blob/d5d15f9fb777ff5572dc3fa1673c2e2704da0193/src/Libraries/Microsoft.Extensions.TimeProvider.Testing/FakeTimeProvider.cs#L16"  target="_blank" rel="noreferrer">FakeTimeProvider</a> class provided by the <a href="https://www.nuget.org/packages/Microsoft.Extensions.TimeProvider.Testing"  target="_blank" rel="noreferrer">Microsoft.Extensions.TimeProvider.Testing</a> NuGet package.</p>
<p>If you&rsquo;re brand new to <code>TimeProvider</code>, it might be worth checking out my other post first. In this one though, I&rsquo;d like to check out a different aspect of <code>TimeProvider</code>, involving timers.</p>
<blockquote><p>The code in this post is available on <a href="https://github.com/grantwinney/CSharpDotNetExamples/tree/master/C%23%2012/TimeAbstraction_Timers"  target="_blank" rel="noreferrer">GitHub</a>, for you to use, expand upon, or just follow along while you read&hellip; and hopefully discover something new!</p>
</blockquote><p>If you don&rsquo;t have .NET 8 yet, <a href="https://dotnet.microsoft.com/en-us/download/visual-studio-sdks"  target="_blank" rel="noreferrer">download the SDK</a>.</p>
<p>A developer might use a timer for all kinds of things, like periodically reminding a user to take some action, checking a database table to see if there&rsquo;s any orders to process, or hitting an API endpoint to update some widget in the UI. One of the things a developer can&rsquo;t easily do with a timer, though, is <em>test</em> it.</p>
<p>You <em>could</em> move whatever the timer&rsquo;s doing into another method and then test that method directly, but then you&rsquo;re rearranging your code to support testing. It&rsquo;s not the end of the world, but it&rsquo;s not ideal either&hellip; and in this case (thanks to .NET 8), it&rsquo;s not even necessary.</p>

<h2 class="relative group">A sample timer
    <div id="a-sample-timer" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#a-sample-timer" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Let&rsquo;s start with a little class that creates one timer, which prints the current time to the screen every second. Most of the time, it prints one particular message, but on Friday evenings it prints a different one.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">TimerSamples</span> <span class="p">:</span> <span class="n">ITimerSamples</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">readonly</span> <span class="n">TimeProvider</span> <span class="n">_timeProvider</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">readonly</span> <span class="n">IConsole</span> <span class="n">_console</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">TimerSamples</span><span class="p">(</span><span class="n">TimeProvider</span> <span class="n">timeProvider</span><span class="p">,</span> <span class="n">IConsole</span> <span class="n">console</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">_timeProvider</span> <span class="p">=</span> <span class="n">timeProvider</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">_console</span> <span class="p">=</span> <span class="n">console</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">StartTimers</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">_timeProvider</span><span class="p">.</span><span class="n">CreateTimer</span><span class="p">(</span><span class="n">PrintTime</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="n">TimeSpan</span><span class="p">.</span><span class="n">Zero</span><span class="p">,</span> <span class="n">TimeSpan</span><span class="p">.</span><span class="n">FromSeconds</span><span class="p">(</span><span class="m">1</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">PrintTime</span><span class="p">(</span><span class="kt">object?</span> <span class="n">_</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">today</span> <span class="p">=</span> <span class="n">_timeProvider</span><span class="p">.</span><span class="n">GetLocalNow</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">message</span> <span class="p">=</span> <span class="p">(</span><span class="n">today</span><span class="p">.</span><span class="n">DayOfWeek</span> <span class="p">==</span> <span class="n">DayOfWeek</span><span class="p">.</span><span class="n">Friday</span> <span class="p">&amp;&amp;</span> <span class="n">today</span><span class="p">.</span><span class="n">Hour</span> <span class="p">&gt;=</span> <span class="m">17</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="p">?</span>
</span></span><span class="line"><span class="cl">                <span class="s">$&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s">                TGIF!!!
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">                It&#39;s {_timeProvider.GetLocalNow():hh:mm tt}... go home!
</span></span></span><span class="line"><span class="cl"><span class="s">                &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="s">$&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s">                THE CURRENT TIME:
</span></span></span><span class="line"><span class="cl"><span class="s">                ================
</span></span></span><span class="line"><span class="cl"><span class="s">                🕒 {_timeProvider.GetLocalNow():hh:mm:ss tt}
</span></span></span><span class="line"><span class="cl"><span class="s">                ================
</span></span></span><span class="line"><span class="cl"><span class="s">                &#34;&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">_console</span><span class="p">.</span><span class="n">Clear</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">_console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">message</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">interface</span> <span class="nc">ITimerSample</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">void</span> <span class="n">StartTimers</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Creating a timer that prints the time to the console every second</p>
<p>There&rsquo;s a few things to note here, one of which is the call to <code>CreateTimer</code>. When the <code>StartTimers</code> method is called, we use <code>TimeProvider</code> to create a new timer, and pass it several values:</p>
<ul>
<li>Which method to run (in this case, it&rsquo;s <code>PrintTime</code>)</li>
<li>What value to pass to the method (don&rsquo;t need it, so <code>null</code> is fine)</li>
<li>When to start it (the <code>TimeSpan.Zero</code> value says to start it immediately)</li>
<li>How often to tick the event (in this case, every second)</li>
</ul>
<p>The constructor is being passed two parameters to support <a href="https://phoenixnap.com/kb/dependency-injection"  target="_blank" rel="noreferrer">dependency injection</a> and testing. The first one is the abstract <code>TimeProvider</code> class, while the second is an <code>IConsole</code> interface to mock the call to <code>Console.WriteLine</code>. This is a console app, and I don&rsquo;t want the tests attempting to write to the console, so I created a tiny wrapper class around it.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">ConsoleWrapper</span> <span class="p">:</span> <span class="n">IConsole</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">Clear</span><span class="p">()</span> <span class="p">=&gt;</span> <span class="n">Console</span><span class="p">.</span><span class="n">Clear</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">WriteLine</span><span class="p">(</span><span class="kt">string</span> <span class="k">value</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="k">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">interface</span> <span class="nc">IConsole</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">void</span> <span class="n">Clear</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="k">void</span> <span class="n">WriteLine</span><span class="p">(</span><span class="kt">string</span> <span class="k">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>A wrapper around Console to help with mocking from the tests</p>
<p>Being that this is a console app, everything starts in the <code>program.cs</code> file. And being that we&rsquo;re using DI, we want to add a list of services as well, so that the app knows what to hand our <code>TimerSamples</code> class when it asks for an <code>IConsole</code>.</p>
<p>One magic line below tells the app what to pass in when a <code>TimeProvider</code> is requested, and that&rsquo;s the one with <code>TimeProvider.System</code> on it. The .NET team defined an implementation of <code>TimeProvider</code> called <code>SystemTimeProvider</code>, and we can access it using <code>TimeProvider.System</code>. That&rsquo;s what the app will use at runtime, although we&rsquo;ll use a different class in our tests&hellip;</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">Microsoft.Extensions.DependencyInjection</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">System.Text</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">TimeAbstractionTimers</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">TimeAbstractionTimers.Wrappers</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">OutputEncoding</span> <span class="p">=</span> <span class="n">Encoding</span><span class="p">.</span><span class="n">UTF8</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">services</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ServiceCollection</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">provider</span> <span class="p">=</span> <span class="n">services</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">AddScoped</span><span class="p">&lt;</span><span class="n">ITimerSamples</span><span class="p">,</span> <span class="n">TimerSamples</span><span class="p">&gt;()</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">AddScoped</span><span class="p">&lt;</span><span class="n">IConsole</span><span class="p">,</span> <span class="n">ConsoleWrapper</span><span class="p">&gt;()</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">AddSingleton</span><span class="p">(</span><span class="n">TimeProvider</span><span class="p">.</span><span class="n">System</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">BuildServiceProvider</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">ts</span> <span class="p">=</span> <span class="n">provider</span><span class="p">.</span><span class="n">GetService</span><span class="p">&lt;</span><span class="n">ITimerSamples</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl"><span class="n">ts</span><span class="p">.</span><span class="n">StartTimers</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">Clear</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">ReadLine</span><span class="p">();</span></span></span></code></pre></div></div>
<p>That&rsquo;s all we need to run the app, which does just absolutely <em>amazing</em> things all by itself. Really, what a work of beauty. A true labor of love. If you run it on a Friday evening, the message will be different, but it isn&rsquo;t Friday right now so we have to wait. Or do we..?</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-timeprovider-and-faketimeprovider-to-test-timers/currenttime.gif"
    width="621"
      height="315"></figure>

<h2 class="relative group">A sample timer test (or several)
    <div id="a-sample-timer-test-or-several" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#a-sample-timer-test-or-several" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>In order to test the abstract <code>TimeProvider</code> class, you&rsquo;ll need to write your own class that overrides some things, and then adds some other methods that make the whole thing easily testable, in a flexible sort of way. Or you could just grab the <code>FakeTimeProvider</code> class that the .NET team wrote. 😅</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-timeprovider-and-faketimeprovider-to-test-timers/image-7.png"
    width="1063"
      height="380"></figure>
<p>You can see more examples of it being used in <a href="https://grantwinney.com/how-to-use-timeprovider-and-faketimeprovider/"  target="_blank" rel="noreferrer">my previous post</a>, but basically you just pass it in place of <code>TimeProvider.System</code>. It&rsquo;s just another implementation of the abstract <code>TimeProvider</code> class, and isn&rsquo;t tied down to any particular test suite, so you can use it with NUnit, xUnit, or anything else you&rsquo;d like.</p>
<p>Here&rsquo;s how I started an NUnit test suite, with a <code>Setup</code> method that gives us a fresh <code>FakeTimeProvider</code>, a mocked <code>IConsole</code> (courtesy of <a href="https://www.nuget.org/packages/moq/"  target="_blank" rel="noreferrer">Moq</a>), and a <code>TimerSample</code> instance for each test. We don&rsquo;t want two tests accidentally interacting somehow.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="n">TimerSamples</span> <span class="n">timerSamples</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="n">FakeTimeProvider</span> <span class="n">fakeTimeProvider</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="n">Mock</span><span class="p">&lt;</span><span class="n">IConsole</span><span class="p">&gt;</span> <span class="n">consoleMock</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">[SetUp]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">Setup</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">fakeTimeProvider</span> <span class="p">=</span> <span class="k">new</span> <span class="n">FakeTimeProvider</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">consoleMock</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Mock</span><span class="p">&lt;</span><span class="n">IConsole</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl">    <span class="n">timerSamples</span> <span class="p">=</span> <span class="k">new</span> <span class="n">TimerSamples</span><span class="p">(</span><span class="n">fakeTimeProvider</span><span class="p">,</span> <span class="n">consoleMock</span><span class="p">.</span><span class="n">Object</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Setting up an NUnit test suite to test timers</p>
<p>Now let&rsquo;s write some tests, and see what kind of things we can do!</p>
<p>As I said, the app writes out a certain message most of the week, only switching it up on Friday evenings. We can set the current time to the day we want, start the timer, and then let the test just sit for awhile until the interval passes&hellip;&hellip;&hellip; lol, no. One of the powerhouse capabilities of the <code>FakeTimeProvider</code> is that we can immediately <code>Advance</code> into the future.</p>
<p>In this first test, let&rsquo;s set the day to Tuesday, start the timer, jump forward in time a few seconds, and make sure the message we expected was printed (and that the message we <em>don&rsquo;t</em> expect <em>wasn&rsquo;t</em> printed).</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="na">[Test]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">PrintTimeTimer_NoTGIFMessage_WhenNotFriday</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">fakeTimeProvider</span><span class="p">.</span><span class="n">SetUtcNow</span><span class="p">(</span><span class="k">new</span> <span class="n">DateTimeOffset</span><span class="p">(</span><span class="m">2024</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="m">9</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">TimeSpan</span><span class="p">.</span><span class="n">Zero</span><span class="p">));</span> <span class="c1">// Tues</span>
</span></span><span class="line"><span class="cl">    <span class="n">fakeTimeProvider</span><span class="p">.</span><span class="n">SetLocalTimeZone</span><span class="p">(</span><span class="n">TimeZoneInfo</span><span class="p">.</span><span class="n">Utc</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Start the timer, then teleport 5 seconds into the future</span>
</span></span><span class="line"><span class="cl">    <span class="n">timerSamples</span><span class="p">.</span><span class="n">StartTimers</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">fakeTimeProvider</span><span class="p">.</span><span class="n">Advance</span><span class="p">(</span><span class="n">TimeSpan</span><span class="p">.</span><span class="n">FromSeconds</span><span class="p">(</span><span class="m">5</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">consoleMock</span><span class="p">.</span><span class="n">Verify</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">It</span><span class="p">.</span><span class="n">Is</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">StartsWith</span><span class="p">(</span><span class="s">&#34;THE CURRENT TIME&#34;</span><span class="p">))),</span> <span class="n">Times</span><span class="p">.</span><span class="n">AtLeastOnce</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">    <span class="n">consoleMock</span><span class="p">.</span><span class="n">Verify</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">It</span><span class="p">.</span><span class="n">Is</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">StartsWith</span><span class="p">(</span><span class="s">&#34;TGIF!!!&#34;</span><span class="p">))),</span> <span class="n">Times</span><span class="p">.</span><span class="n">Never</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Set day to Tues, and make sure the first message prints</p>
<p>You just couldn&rsquo;t do something like this before, at least that I know of, and I&rsquo;m sure nowhere near this easily. It probably would&rsquo;ve been easier to have a test suite that ran Friday evenings just to make sure the test was right rather than try to create your own version of what&rsquo;s happening here.</p>
<p>In this second test, let&rsquo;s set the date to a Friday at 5pm, when the alternate message should show. Doing the same thing as before, we start the timers, time travel a few seconds, and verify the <em>other</em> message printed (and the first one didn&rsquo;t).</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="na">[Test]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">PrintTimeTimer_TGIFMessage_WhenFridayEvening</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">fakeTimeProvider</span><span class="p">.</span><span class="n">SetUtcNow</span><span class="p">(</span><span class="k">new</span> <span class="n">DateTimeOffset</span><span class="p">(</span><span class="m">2024</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="m">12</span><span class="p">,</span> <span class="m">17</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">TimeSpan</span><span class="p">.</span><span class="n">Zero</span><span class="p">));</span> <span class="c1">// Fri @ 5pm</span>
</span></span><span class="line"><span class="cl">    <span class="n">fakeTimeProvider</span><span class="p">.</span><span class="n">SetLocalTimeZone</span><span class="p">(</span><span class="n">TimeZoneInfo</span><span class="p">.</span><span class="n">Utc</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Start the timer, then teleport 5 seconds into the future</span>
</span></span><span class="line"><span class="cl">    <span class="n">timerSamples</span><span class="p">.</span><span class="n">StartTimers</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">fakeTimeProvider</span><span class="p">.</span><span class="n">Advance</span><span class="p">(</span><span class="n">TimeSpan</span><span class="p">.</span><span class="n">FromSeconds</span><span class="p">(</span><span class="m">5</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">consoleMock</span><span class="p">.</span><span class="n">Verify</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">It</span><span class="p">.</span><span class="n">Is</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">StartsWith</span><span class="p">(</span><span class="s">&#34;THE CURRENT TIME&#34;</span><span class="p">))),</span> <span class="n">Times</span><span class="p">.</span><span class="n">Never</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">    <span class="n">consoleMock</span><span class="p">.</span><span class="n">Verify</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">It</span><span class="p">.</span><span class="n">Is</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">StartsWith</span><span class="p">(</span><span class="s">&#34;TGIF!!!&#34;</span><span class="p">))),</span> <span class="n">Times</span><span class="p">.</span><span class="n">AtLeastOnce</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Set day to Fri @ 5p, and make sure the second message prints</p>
<p>Here&rsquo;s one more, that sets the time right before the transition occurs, jumps past the transition, and then double-checks that <em>both</em> messages were printed at least once.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="na">[Test]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">PrintTimeTimer_MessageTransitionsCorrectly_WhenFridayAfternoonTurnsToEvening</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">fakeTimeProvider</span><span class="p">.</span><span class="n">SetUtcNow</span><span class="p">(</span><span class="k">new</span> <span class="n">DateTimeOffset</span><span class="p">(</span><span class="m">2024</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="m">12</span><span class="p">,</span> <span class="m">16</span><span class="p">,</span> <span class="m">59</span><span class="p">,</span> <span class="m">57</span><span class="p">,</span> <span class="n">TimeSpan</span><span class="p">.</span><span class="n">Zero</span><span class="p">));</span> <span class="c1">// Fri @ 4:59:57pm</span>
</span></span><span class="line"><span class="cl">    <span class="n">fakeTimeProvider</span><span class="p">.</span><span class="n">SetLocalTimeZone</span><span class="p">(</span><span class="n">TimeZoneInfo</span><span class="p">.</span><span class="n">Utc</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Start the timer, then teleport 5 seconds into the future</span>
</span></span><span class="line"><span class="cl">    <span class="n">timerSamples</span><span class="p">.</span><span class="n">StartTimers</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">fakeTimeProvider</span><span class="p">.</span><span class="n">Advance</span><span class="p">(</span><span class="n">TimeSpan</span><span class="p">.</span><span class="n">FromSeconds</span><span class="p">(</span><span class="m">5</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">consoleMock</span><span class="p">.</span><span class="n">Verify</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">It</span><span class="p">.</span><span class="n">Is</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">StartsWith</span><span class="p">(</span><span class="s">&#34;THE CURRENT TIME&#34;</span><span class="p">))),</span> <span class="n">Times</span><span class="p">.</span><span class="n">AtLeastOnce</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">    <span class="n">consoleMock</span><span class="p">.</span><span class="n">Verify</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">It</span><span class="p">.</span><span class="n">Is</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">StartsWith</span><span class="p">(</span><span class="s">&#34;TGIF!!!&#34;</span><span class="p">))),</span> <span class="n">Times</span><span class="p">.</span><span class="n">AtLeastOnce</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Set day to Fri, right before 5p, and make sure both messages printed</p>
<p>Everything works, and 4 tests that should&rsquo;ve taken 20 seconds to run (in realtime) ran in <em>less than half a second.</em></p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-timeprovider-and-faketimeprovider-to-test-timers/image-13.png"
    width="804"
      height="242"></figure>

<h2 class="relative group">Caveat?
    <div id="caveat" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#caveat" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Here&rsquo;s an interesting point. It makes sense when you think about it, but with all the seemingly magic things happening here, it might be easy to neglect. Even though, by advancing the timer, we don&rsquo;t have to actually wait for the timer interval to pass, all the tick events <em>are</em> firing, one after the other.</p>
<p>What I mean by that last part is, if we modified the <code>PrintTime</code> method, inserting one line of code that told it to sleep for 10 ms, all the tests will take just a hair longer to run.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">PrintTime</span><span class="p">(</span><span class="kt">object?</span> <span class="n">_</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Thread</span><span class="p">.</span><span class="n">Sleep</span><span class="p">(</span><span class="m">10</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">today</span> <span class="p">=</span> <span class="n">_timeProvider</span><span class="p">.</span><span class="n">GetLocalNow</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">message</span> <span class="p">=</span> <span class="p">(</span><span class="n">today</span><span class="p">.</span><span class="n">DayOfWeek</span> <span class="p">==</span> <span class="n">DayOfWeek</span><span class="p">.</span><span class="n">Friday</span> <span class="p">&amp;&amp;</span> <span class="n">today</span><span class="p">.</span><span class="n">Hour</span> <span class="p">&gt;=</span> <span class="m">17</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">?</span>
</span></span><span class="line"><span class="cl">            <span class="s">$&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s">            TGIF!!!
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">            It&#39;s {_timeProvider.GetLocalNow():hh:mm tt}... go home!
</span></span></span><span class="line"><span class="cl"><span class="s">            &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="s">$&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s">            THE CURRENT TIME:
</span></span></span><span class="line"><span class="cl"><span class="s">            ================
</span></span></span><span class="line"><span class="cl"><span class="s">            🕒 {_timeProvider.GetLocalNow():hh:mm:ss tt}
</span></span></span><span class="line"><span class="cl"><span class="s">            ================
</span></span></span><span class="line"><span class="cl"><span class="s">            &#34;&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">_console</span><span class="p">.</span><span class="n">Clear</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">_console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">message</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Here&rsquo;s the result of running the tests again, first with a 10 ms sleep, and then a 100 ms sleep. The durations get longer and longer, which makes sense.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-timeprovider-and-faketimeprovider-to-test-timers/image-14.png"
    width="606"
      height="246"></figure>
<p>Running the tests, with a 10 ms sleep in the PrintTime method</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-timeprovider-and-faketimeprovider-to-test-timers/image-16.png"
    width="606"
      height="241"></figure>
<p>Running the tests again, after increasing the sleep to 100 ms</p>
<p>The takeaway here, I think, is that we need to be careful what we&rsquo;re testing, because we&rsquo;re not skipping over <em>everything</em>&hellip; just over the intervals we&rsquo;d normally have to wait for between tick events. The sleep I put in there is silly, but if there&rsquo;s a time-consuming dependency, you might want to make adjustments for that.</p>
<p>In a unit test, we typically remove as many things as possible that are outside of our control or not things we want to test (like writing to the console, or to disk). But what about some kind of integration test, where we want to test a larger part of the system, maybe even make sure a file actually gets written to? That could be a reasonable test.</p>
<p>But what if the tick event hits an outside API to get some data back? In production, that API might only get hit once every minute, which is perfectly acceptable. But if that piece of code were caught up in a test like this, and you advanced several hours or days, I&rsquo;m fairly certain it would attempt to hit that API hundreds of times in rapid succession. I haven&rsquo;t actually tested it, but it seems consistent with what I&rsquo;m seeing here. If you know differently though, please share below!</p>
<p>If you&rsquo;d like to learn more about this intriguing new .NET 8 feature, I&rsquo;d encourage you to check out these excellent posts on the subject.</p>
<ul>
<li><a href="https://code-maze.com/csharp-testing-time-dependent-code-with-timeprovider/"  target="_blank" rel="noreferrer">Testing Time-Dependent Code With TimeProvider in .NET | CodeMaze</a></li>
<li><a href="https://andrewlock.net/exploring-the-dotnet-8-preview-avoiding-flaky-tests-with-timeprovider-and-itimer/"  target="_blank" rel="noreferrer">Avoiding flaky tests with TimeProvider and ITimer | Andrew Lock</a></li>
</ul>
<p>If you found this content useful, and would like to learn more about a variety of <a href="https://grantwinney.com/tags/csharp/"  target="_blank" rel="noreferrer">C#</a> features, check out the <a href="https://github.com/grantwinney/CSharpDotNetExamples"  target="_blank" rel="noreferrer">CSharpDotNetExamples repo</a>, where you&rsquo;ll find links to plenty more blog posts and practical examples!</p>
]]></content:encoded><media:content url="https://grantwinney.com/how-to-use-timeprovider-and-faketimeprovider-to-test-timers/feature.webp" medium="image" type="image/webp"/></item><item><title>How to use TimeProvider and FakeTimeProvider (time abstraction in .NET)</title><link>https://grantwinney.com/how-to-use-timeprovider-and-faketimeprovider/</link><pubDate>Sun, 07 Jan 2024 04:14:00 +0000</pubDate><guid>https://grantwinney.com/how-to-use-timeprovider-and-faketimeprovider/</guid><description>Testing time in C# is difficult, but .NET 8 (C# 12) adds an abstract TimeProvider class that makes it easier. Let&amp;rsquo;s take a closer look.</description><content:encoded><![CDATA[<p>Since it&rsquo;s the Christmas season, and .NET 8 (C# 12) was <a href="https://devblogs.microsoft.com/dotnet/announcing-dotnet-8/"  target="_blank" rel="noreferrer">recently released</a>, it seems like a good time to unwrap some of the goodies we got. A couple of the most intriguing ones, IMO, are new constructs for abstracting time and timers, two things that have traditionally been a pain when it comes to testing.</p>
<p>Let&rsquo;s take a closer look at abstracting time first, and save timers for <a href="https://grantwinney.com/how-to-use-timeprovider-and-faketimeprovider-to-test-timers/"  target="_blank" rel="noreferrer">another post</a>.</p>
<blockquote><p>The code in this post is available on <a href="https://github.com/grantwinney/CSharpDotNetExamples/tree/master/C%23%2012/TimeAbstraction"  target="_blank" rel="noreferrer">GitHub</a>, for you to use, expand upon, or just follow along while you read&hellip; and hopefully discover something new!</p>
</blockquote><p>If you don&rsquo;t have .NET 8 yet, <a href="https://dotnet.microsoft.com/en-us/download/visual-studio-sdks"  target="_blank" rel="noreferrer">download the SDK</a> as well.</p>

<h2 class="relative group">How do we deal with time today?
    <div id="how-do-we-deal-with-time-today" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#how-do-we-deal-with-time-today" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Let&rsquo;s take a look at a few ways to work with time in a C# app, starting with the one that&rsquo;s the least work &hellip; and also the least flexible.</p>

<h3 class="relative group">Access the static date/time right when you need it
    <div id="access-the-static-datetime-right-when-you-need-it" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#access-the-static-datetime-right-when-you-need-it" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Most of the time, in my experience anyway, when someone needs the current time, they just grab it at that moment. The .NET framework makes it incredibly easy with static properties like <code>DateTime.Now</code>, <code>DateTime.UtcNow</code>, <code>DateTimeOffset.UtcNow</code>, etc.</p>
<p>It&rsquo;s easy to define a method that grabs the current time and uses that to determine whether, for example, a business is currently open or not. Ignore the fact that my example is incredibly naive and only works for a single business.. keepin&rsquo; it simple!</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kt">bool</span> <span class="n">IsOpenHours</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">now</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Open 10a - 6p on Sundays</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">now</span><span class="p">.</span><span class="n">DayOfWeek</span> <span class="p">==</span> <span class="n">DayOfWeek</span><span class="p">.</span><span class="n">Sunday</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">now</span><span class="p">.</span><span class="n">Hour</span> <span class="p">&gt;=</span> <span class="m">10</span> <span class="p">&amp;&amp;</span> <span class="n">now</span><span class="p">.</span><span class="n">Hour</span> <span class="p">&lt;=</span> <span class="m">18</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Open 8a - 8p the rest of the week</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">now</span><span class="p">.</span><span class="n">Hour</span> <span class="p">&gt;=</span> <span class="m">8</span> <span class="p">&amp;&amp;</span> <span class="n">now</span><span class="p">.</span><span class="n">Hour</span> <span class="p">&lt;=</span> <span class="m">20</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Any attempt to test the above code, whether it&rsquo;s a tiny unit test or some larger integration test that happens to hit this code along the way, is restricted by the fact that <code>DateTime.UtcNow</code> will return whatever the current time is when the test runs. If the test is running on a Wednesday, the &ldquo;Sunday&rdquo; condition won&rsquo;t be hit. If the test suite runs automatically at midnight every night, the method <em>always</em> return &ldquo;false&rdquo;. If we can&rsquo;t control the current time, we can&rsquo;t control what the method returns - it&rsquo;s untestable.</p>

<h3 class="relative group">Pass a date/time value to the method
    <div id="pass-a-datetime-value-to-the-method" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#pass-a-datetime-value-to-the-method" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>There&rsquo;s ways around that, of course. You could pass a <code>DateTime</code> or <code>DateTimeOffset</code> in as a parameter, and then at least a unit test could pass it different values for different edge cases.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kt">bool</span> <span class="n">IsOpenHours</span><span class="p">(</span><span class="n">DateTime</span> <span class="n">now</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Open 10a - 6p on Sundays</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">now</span><span class="p">.</span><span class="n">DayOfWeek</span> <span class="p">==</span> <span class="n">DayOfWeek</span><span class="p">.</span><span class="n">Sunday</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">now</span><span class="p">.</span><span class="n">Hour</span> <span class="p">&gt;=</span> <span class="m">10</span> <span class="p">&amp;&amp;</span> <span class="n">now</span><span class="p">.</span><span class="n">Hour</span> <span class="p">&lt;=</span> <span class="m">18</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Open 8a - 8p the rest of the week</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">now</span><span class="p">.</span><span class="n">Hour</span> <span class="p">&gt;=</span> <span class="m">8</span> <span class="p">&amp;&amp;</span> <span class="n">now</span><span class="p">.</span><span class="n">Hour</span> <span class="p">&lt;=</span> <span class="m">20</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>But I&rsquo;d argue that this seems wrong somehow, allowing callers to pass in a <code>DateTime</code> value that should only ever represent <em>now</em>, just to support testing. And since it only bumps the concern a level up, any integration tests that run against larger areas of the system will still run into the problem of not being able to change the date/time value that this method uses.</p>

<h3 class="relative group">Inject a dependency that provides the date/time
    <div id="inject-a-dependency-that-provides-the-datetime" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#inject-a-dependency-that-provides-the-datetime" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Taking things a step further.. by wrapping the <code>DateTime</code> or <code>DateTimeOffset</code> class as in the <code>BusDateTime</code> class below, then injecting the dependency into the class (like with <code>AddScoped</code> or <code>AddSingleton</code> in an ASP.NET Core Web API), we can <a href="https://grantwinney.com/what-is-mocking-a-dependency"  target="_blank" rel="noreferrer">mock the dependency</a> in a test suite.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">BusinessOperations</span> <span class="p">:</span> <span class="n">IBusinessOperations</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">readonly</span> <span class="n">DateTime</span> <span class="n">_now</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">BusinessOperations</span><span class="p">(</span><span class="n">IDateTime</span> <span class="n">now</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">_now</span> <span class="p">=</span> <span class="n">now</span><span class="p">.</span><span class="n">Now</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">bool</span> <span class="n">IsOpenHours</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Open 10a - 6p on Sundays</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">_now</span><span class="p">.</span><span class="n">DayOfWeek</span> <span class="p">==</span> <span class="n">DayOfWeek</span><span class="p">.</span><span class="n">Sunday</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">_now</span><span class="p">.</span><span class="n">Hour</span> <span class="p">&gt;=</span> <span class="m">10</span> <span class="p">&amp;&amp;</span> <span class="n">_now</span><span class="p">.</span><span class="n">Hour</span> <span class="p">&lt;=</span> <span class="m">18</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Open 8a - 8p the rest of the week</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">_now</span><span class="p">.</span><span class="n">Hour</span> <span class="p">&gt;=</span> <span class="m">8</span> <span class="p">&amp;&amp;</span> <span class="n">_now</span><span class="p">.</span><span class="n">Hour</span> <span class="p">&lt;=</span> <span class="m">20</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">interface</span> <span class="nc">IBusinessOperations</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">IsOpenHours</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">BusDateTime</span> <span class="p">:</span> <span class="n">IDateTime</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">DateTime</span> <span class="n">Now</span> <span class="p">=&gt;</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">interface</span> <span class="nc">IDateTime</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">DateTime</span> <span class="n">Now</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddScoped</span><span class="p">&lt;</span><span class="n">IDateTime</span><span class="p">,</span> <span class="n">BusDateTime</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl"><span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddScoped</span><span class="p">&lt;</span><span class="n">IBusinessOperations</span><span class="p">,</span> <span class="n">BusinessOperations</span><span class="p">&gt;();</span></span></span></code></pre></div></div>
<p>The downside of this is needing to define a class that redefines all the same properties we get in the .NET classes (the ones our app needs, anyway), and then creating an interface that defines the properties yet again. Its repetitive. Wouldn&rsquo;t it be nice if there was an official .NET way of doing this?</p>
<p><em>Drum roll please&hellip;</em></p>

<h2 class="relative group">Abstraction with TimeProvider
    <div id="abstraction-with-timeprovider" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#abstraction-with-timeprovider" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Finally, it&rsquo;s time to check out the new <a href="https://learn.microsoft.com/en-us/dotnet/api/system.timeprovider?view=net-8.0"  target="_blank" rel="noreferrer">TimeProvider</a> class. It&rsquo;s <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/abstract"  target="_blank" rel="noreferrer">abstract</a>, so it can&rsquo;t be used directly - it needs to be inherited by other classes. We could define our own, but we&rsquo;re already given one (shown below), called <code>SystemTimeProvider</code>. It doesn&rsquo;t get easier than that! It&rsquo;s super tiny, since all the properties and methods in <code>TimeProvider</code> have default values and implementations. The only thing abstract about <code>TimeProvider</code> is the class itself.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="cs">/// &lt;summary&gt;</span>
</span></span><span class="line"><span class="cl"><span class="cs">/// Used to create a TimeProvider instance returned from System and uses the</span>
</span></span><span class="line"><span class="cl"><span class="cs">/// default implementation, provided by TimeProvider which uses</span>
</span></span><span class="line"><span class="cl"><span class="cs">/// DateTimeOffset.UtcNow, TimeZoneInfo.Local, Stopwatch, and Timer.</span>
</span></span><span class="line"><span class="cl"><span class="cs">/// &lt;/summary&gt;</span>
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">sealed</span> <span class="k">class</span> <span class="nc">SystemTimeProvider</span> <span class="p">:</span> <span class="n">TimeProvider</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;summary&gt;Initializes the instance.&lt;/summary&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">internal</span> <span class="n">SystemTimeProvider</span><span class="p">()</span> <span class="p">:</span> <span class="k">base</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Accessing the new class is super easy too - just use the static accessor in the <code>TimeProvider</code> class that passes back a single instance of <code>SystemTimeProvider</code>. In fact, that&rsquo;s the only way to get a new instance, since it&rsquo;s marked <code>private</code> and actually lives <em>inside</em> the abstract <code>TimeProvider</code> class. What that means is, anywhere you access that property from, you get the same (singleton) instance.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="cs">/// &lt;summary&gt;Provides an abstraction for time.&lt;/summary&gt;</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">abstract</span> <span class="k">class</span> <span class="nc">TimeProvider</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="cs">/// &lt;summary&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="cs">/// Gets a TimeProvider that provides a clock based on DateTimeOffset.UtcNow,</span>
</span></span><span class="line"><span class="cl">  <span class="cs">/// a time zone based on TimeZoneInfo.Local, a high-performance time stamp</span>
</span></span><span class="line"><span class="cl">  <span class="cs">/// based on Stopwatch, and a timer based on Timer.</span>
</span></span><span class="line"><span class="cl">  <span class="cs">/// &lt;/summary&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="cs">/// &lt;remarks&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="cs">/// If the TimeZoneInfo.Local changes after the object is returned, the change</span>
</span></span><span class="line"><span class="cl">  <span class="cs">/// will be reflected in any subsequent operations that retrieve GetLocalNow.</span>
</span></span><span class="line"><span class="cl">  <span class="cs">/// &lt;/remarks&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kd">public</span> <span class="kd">static</span> <span class="n">TimeProvider</span> <span class="n">System</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="k">new</span> <span class="n">SystemTimeProvider</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">...</span>
</span></span><span class="line"><span class="cl">  <span class="p">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Accessing that static property directly in our own code, though, will land us right back where we started in the very first example - with methods that we can&rsquo;t test, because we can&rsquo;t control them. This code will work, but it&rsquo;s untestable as-is:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kt">bool</span> <span class="n">IsOpenHours</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">utc</span> <span class="p">=</span> <span class="n">TimeProvider</span><span class="p">.</span><span class="n">System</span><span class="p">.</span><span class="n">GetUtcNow</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Open 10a - 6p on Sundays</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">utc</span><span class="p">.</span><span class="n">DayOfWeek</span> <span class="p">==</span> <span class="n">DayOfWeek</span><span class="p">.</span><span class="n">Sunday</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">utc</span><span class="p">.</span><span class="n">Hour</span> <span class="p">&gt;=</span> <span class="m">10</span> <span class="p">&amp;&amp;</span> <span class="n">utc</span><span class="p">.</span><span class="n">Hour</span> <span class="p">&lt;=</span> <span class="m">18</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Open 8a - 8p the rest of the week</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">utc</span><span class="p">.</span><span class="n">Hour</span> <span class="p">&gt;=</span> <span class="m">8</span> <span class="p">&amp;&amp;</span> <span class="n">utc</span><span class="p">.</span><span class="n">Hour</span> <span class="p">&lt;=</span> <span class="m">20</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>No better, from a testing standpoint, than using DateTime.Now or DateTimeOffset.Now</p>
<p>Instead of calling <code>TimeProvider.System.GetUtcNow()</code> directly, we can set things up for dependency injection by accepting a <code>TimeProvider</code> in the constructor and then configuring the DI system to inject the <code>SystemTimeProvider</code> as needed.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">BusinessOperations</span> <span class="p">:</span> <span class="n">IBusinessOperations</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">readonly</span> <span class="n">TimeProvider</span> <span class="n">_now</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">BusinessOperations</span><span class="p">(</span><span class="n">TimeProvider</span> <span class="n">now</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">_now</span> <span class="p">=</span> <span class="n">now</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">bool</span> <span class="n">IsOpenHours</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">utc</span> <span class="p">=</span> <span class="n">_now</span><span class="p">.</span><span class="n">GetUtcNow</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Open 10a - 6p on Sundays</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">utc</span><span class="p">.</span><span class="n">DayOfWeek</span> <span class="p">==</span> <span class="n">DayOfWeek</span><span class="p">.</span><span class="n">Sunday</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">utc</span><span class="p">.</span><span class="n">Hour</span> <span class="p">&gt;=</span> <span class="m">10</span> <span class="p">&amp;&amp;</span> <span class="n">utc</span><span class="p">.</span><span class="n">Hour</span> <span class="p">&lt;=</span> <span class="m">18</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Open 8a - 8p the rest of the week</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">utc</span><span class="p">.</span><span class="n">Hour</span> <span class="p">&gt;=</span> <span class="m">8</span> <span class="p">&amp;&amp;</span> <span class="n">utc</span><span class="p">.</span><span class="n">Hour</span> <span class="p">&lt;=</span> <span class="m">20</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">interface</span> <span class="nc">IBusinessOperations</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">IsOpenHours</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Using ASP .NET Core as an example again, we&rsquo;d just add a <a href="https://grantwinney.com/difference-between-singleton-scoped-transient"  target="_blank" rel="noreferrer">singleton</a> like this (below). Now anytime some class expects an instance of <code>IBusinessOperations</code> to be injected, the singleton instance of <code>SystemTimeProvider</code> will get injected too.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddScoped</span><span class="p">&lt;</span><span class="n">IBusinessOperations</span><span class="p">,</span> <span class="n">BusinessOperations</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl"><span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddSingleton</span><span class="p">(</span><span class="n">TimeProvider</span><span class="p">.</span><span class="n">System</span><span class="p">);</span></span></span></code></pre></div></div>

<h2 class="relative group">Testing with FakeTimeProvider
    <div id="testing-with-faketimeprovider" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#testing-with-faketimeprovider" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>All of that&rsquo;s well and good, and possibly even interesting, but how does it help us with testing? Well, .NET 8 provides us with one more new class, via NuGet package, and that&rsquo;s the <code>FakeTimeProvider</code>.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-timeprovider-and-faketimeprovider/image.png"
    width="863"
      height="310"></figure>
<p>It&rsquo;s another implementation of the abstract <code>TimeProvider</code> class, with additional methods for making us the masters of time. Here&rsquo;s part of it, with everything cut out except what I think is relevant at the moment. Things to note:</p>
<ul>
<li>It defaults to Jan 1, 2000 at midnight UTC.</li>
<li>The <code>SetUtcNow</code> method lets you move forward in time (but never backwards).</li>
<li>The <code>Advance</code> method does the same, although it has effects on timers, another part of the <code>TimeProvider</code> that I&rsquo;ll cover in a separate post.</li>
<li>The <code>SetLocalTimeZone</code> method lets you change time zones.</li>
</ul>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// Represents a synthetic time provider that can be used to enable</span>
</span></span><span class="line"><span class="cl"><span class="c1">// deterministic behavior in tests.</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">FakeTimeProvider</span> <span class="p">:</span> <span class="n">TimeProvider</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="n">DateTimeOffset</span> <span class="n">_now</span> <span class="p">=</span> <span class="k">new</span> <span class="n">DateTimeOffset</span><span class="p">(</span><span class="m">2000</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">TimeSpan</span><span class="p">.</span><span class="n">Zero</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="n">TimeZoneInfo</span> <span class="n">_localTimeZone</span> <span class="p">=</span> <span class="n">TimeZoneInfo</span><span class="p">.</span><span class="n">Utc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">FakeTimeProvider</span><span class="p">(</span><span class="n">DateTimeOffset</span> <span class="n">startDateTime</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Throw</span><span class="p">.</span><span class="n">IfLessThan</span><span class="p">(</span><span class="n">startDateTime</span><span class="p">.</span><span class="n">Ticks</span><span class="p">,</span> <span class="m">0L</span><span class="p">,</span> <span class="s">&#34;startDateTime.Ticks&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">_now</span> <span class="p">=</span> <span class="n">startDateTime</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Start</span> <span class="p">=</span> <span class="n">_now</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Sets the date and time in the UTC time zone.</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">SetUtcNow</span><span class="p">(</span><span class="n">DateTimeOffset</span> <span class="k">value</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="p">...</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="k">value</span> <span class="p">&lt;</span> <span class="n">_now</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">DefaultInterpolatedStringHandler</span> <span class="n">defaultInterpolatedStringHandler</span> <span class="p">=</span> <span class="k">new</span> <span class="n">DefaultInterpolatedStringHandler</span><span class="p">(</span><span class="m">41</span><span class="p">,</span> <span class="m">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">defaultInterpolatedStringHandler</span><span class="p">.</span><span class="n">AppendLiteral</span><span class="p">(</span><span class="s">&#34;Cannot go back in time. Current time is &#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">defaultInterpolatedStringHandler</span><span class="p">.</span><span class="n">AppendFormatted</span><span class="p">(</span><span class="n">_now</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">defaultInterpolatedStringHandler</span><span class="p">.</span><span class="n">AppendLiteral</span><span class="p">(</span><span class="s">&#34;.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">Throw</span><span class="p">.</span><span class="n">ArgumentOutOfRangeException</span><span class="p">(</span><span class="s">&#34;value&#34;</span><span class="p">,</span> <span class="n">defaultInterpolatedStringHandler</span><span class="p">.</span><span class="n">ToStringAndClear</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">_now</span> <span class="p">=</span> <span class="k">value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">...</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Advances time by a specific amount.</span>
</span></span><span class="line"><span class="cl">    <span class="c1">//</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Advancing time affects the timers created from this provider, and all other operations</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// that are directly or indirectly using this provider as a time source. Whereas</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// when using System.TimeProvider.System, time marches forward automatically in</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// hardware, for the fake time provider the application is responsible for doing</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// this explicitly by calling this method.</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">Advance</span><span class="p">(</span><span class="n">TimeSpan</span> <span class="n">delta</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="p">...</span>
</span></span><span class="line"><span class="cl">        <span class="n">_now</span> <span class="p">+=</span> <span class="n">delta</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">...</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Sets the local time zone.</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">SetLocalTimeZone</span><span class="p">(</span><span class="n">TimeZoneInfo</span> <span class="n">localTimeZone</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">_localTimeZone</span> <span class="p">=</span> <span class="n">Throw</span><span class="p">.</span><span class="n">IfNull</span><span class="p">(</span><span class="n">localTimeZone</span><span class="p">,</span> <span class="s">&#34;localTimeZone&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>If you try to move backwards, or set the time to something less than Jan 1, 2000, it throws an exception like the one below. So no testing like it&rsquo;s 1999.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-timeprovider-and-faketimeprovider/image-2.png"
    width="1033"
      height="251"></figure>

<h3 class="relative group">Using FakeTimeProvider with xUnit
    <div id="using-faketimeprovider-with-xunit" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#using-faketimeprovider-with-xunit" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Using the FakeTimeProvider just involves setting the date to whatever date and time you want to test. Here&rsquo;s a couple of xUnit tests that make sure the store shows as open or closed as expected, at different times on different days.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">BusinessOperationsTests</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">readonly</span> <span class="n">FakeTimeProvider</span> <span class="n">fake</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">readonly</span> <span class="n">BusinessOperations</span> <span class="n">busOp</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">BusinessOperationsTests</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">fake</span> <span class="p">=</span> <span class="k">new</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">busOp</span> <span class="p">=</span> <span class="k">new</span><span class="p">(</span><span class="n">fake</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [Theory]</span>
</span></span><span class="line"><span class="cl"><span class="na">    [Trait(&#34;Category&#34;, &#34;Monday Hours&#34;)]</span>
</span></span><span class="line"><span class="cl"><span class="na">    [InlineData(7, false)]</span>   <span class="c1">// 7a</span>
</span></span><span class="line"><span class="cl"><span class="na">    [InlineData(8, true)]</span>    <span class="c1">// 8a</span>
</span></span><span class="line"><span class="cl"><span class="na">    [InlineData(20, true)]</span>   <span class="c1">// 8p</span>
</span></span><span class="line"><span class="cl"><span class="na">    [InlineData(21, false)]</span>  <span class="c1">// 9p</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">BusinessOpenTimeIsCorrect_WhenDayIsNotSunday</span><span class="p">(</span><span class="kt">int</span> <span class="n">hour</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">isOpen</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">fake</span><span class="p">.</span><span class="n">SetUtcNow</span><span class="p">(</span><span class="k">new</span> <span class="n">DateTimeOffset</span><span class="p">(</span><span class="m">2024</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="n">hour</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="n">TimeSpan</span><span class="p">.</span><span class="n">Zero</span><span class="p">));</span>  <span class="c1">// Monday</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">Equal</span><span class="p">(</span><span class="n">isOpen</span><span class="p">,</span> <span class="n">busOp</span><span class="p">.</span><span class="n">IsOpenHours</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [Theory]</span>
</span></span><span class="line"><span class="cl"><span class="na">    [Trait(&#34;Category&#34;, &#34;Sunday Hours&#34;)]</span>
</span></span><span class="line"><span class="cl"><span class="na">    [InlineData(9, false)]</span>   <span class="c1">// 9a</span>
</span></span><span class="line"><span class="cl"><span class="na">    [InlineData(10, true)]</span>   <span class="c1">// 10a</span>
</span></span><span class="line"><span class="cl"><span class="na">    [InlineData(18, true)]</span>   <span class="c1">// 6p</span>
</span></span><span class="line"><span class="cl"><span class="na">    [InlineData(19, false)]</span>  <span class="c1">// 7p</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">BusinessOpenTimeIsCorrect_WhenDayIsSunday</span><span class="p">(</span><span class="kt">int</span> <span class="n">hour</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">isOpen</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">fake</span><span class="p">.</span><span class="n">SetUtcNow</span><span class="p">(</span><span class="k">new</span> <span class="n">DateTimeOffset</span><span class="p">(</span><span class="m">2023</span><span class="p">,</span> <span class="m">12</span><span class="p">,</span> <span class="m">31</span><span class="p">,</span> <span class="n">hour</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="n">TimeSpan</span><span class="p">.</span><span class="n">Zero</span><span class="p">));</span>  <span class="c1">// Sunday</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">Equal</span><span class="p">(</span><span class="n">isOpen</span><span class="p">,</span> <span class="n">busOp</span><span class="p">.</span><span class="n">IsOpenHours</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-timeprovider-and-faketimeprovider/image-4.png"
    width="839"
      height="457"></figure>
<p>Results in the Test Explorer pane</p>
<p><em>Unrelated note:</em> The eagle-eyed reader might&rsquo;ve noticed the above tests are grouped by category. You can set category names (aka &ldquo;traits&rdquo;) on your xUnit tests, and then choose to &ldquo;Group By&rdquo; those traits in the test explorer pane. It&rsquo;s a nice way of organizing things a bit.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-timeprovider-and-faketimeprovider/image-5.png"
    width="603"
      height="602"></figure>

<h3 class="relative group">Using FakeTimeProvider with NUnit
    <div id="using-faketimeprovider-with-nunit" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#using-faketimeprovider-with-nunit" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Personally I prefer NUnit, although really it&rsquo;s only because I&rsquo;ve used it a lot more. Fortunately, the <code>FakeTimeProvider</code> class isn&rsquo;t tied to any particular testing suite, so you can use it with anything. Here&rsquo;s the same tests, using NUnit:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="na">[TestFixture]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">BusinessOperationsTests</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="n">FakeTimeProvider</span> <span class="n">fake</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="n">BusinessOperations</span> <span class="n">busOp</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [SetUp]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">Setup</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">fake</span> <span class="p">=</span> <span class="k">new</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">busOp</span> <span class="p">=</span> <span class="k">new</span><span class="p">(</span><span class="n">fake</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [Category(&#34;Monday Hours&#34;)]</span>
</span></span><span class="line"><span class="cl"><span class="na">    [TestCase(7, false, TestName = &#34;Closed at 7am&#34;)]</span>
</span></span><span class="line"><span class="cl"><span class="na">    [TestCase(8, true, TestName = &#34;Open at 8am&#34;)]</span>
</span></span><span class="line"><span class="cl"><span class="na">    [TestCase(20, true, TestName = &#34;Open at 8pm&#34;)]</span>
</span></span><span class="line"><span class="cl"><span class="na">    [TestCase(21, false, TestName = &#34;Closed at 9pm&#34;)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">BusinessOpenTimeIsCorrect_WhenDayIsNotSunday</span><span class="p">(</span><span class="kt">int</span> <span class="n">hour</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">isOpen</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">fake</span><span class="p">.</span><span class="n">SetUtcNow</span><span class="p">(</span><span class="k">new</span> <span class="n">DateTimeOffset</span><span class="p">(</span><span class="m">2024</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="n">hour</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="n">TimeSpan</span><span class="p">.</span><span class="n">Zero</span><span class="p">));</span>  <span class="c1">// Monday</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">busOp</span><span class="p">.</span><span class="n">IsOpenHours</span><span class="p">(),</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="n">isOpen</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [Category(&#34;Sunday Hours&#34;)]</span>
</span></span><span class="line"><span class="cl"><span class="na">    [TestCase(9, false, TestName = &#34;Closed at 9am&#34;)]</span>
</span></span><span class="line"><span class="cl"><span class="na">    [TestCase(10, true, TestName = &#34;Open at 10am&#34;)]</span>
</span></span><span class="line"><span class="cl"><span class="na">    [TestCase(18, true, TestName = &#34;Open at 6pm&#34;)]</span>
</span></span><span class="line"><span class="cl"><span class="na">    [TestCase(19, false, TestName = &#34;Closed at 7pm&#34;)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">BusinessOpenTimeIsCorrect_WhenDayIsSunday</span><span class="p">(</span><span class="kt">int</span> <span class="n">hour</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">isOpen</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">fake</span><span class="p">.</span><span class="n">SetUtcNow</span><span class="p">(</span><span class="k">new</span> <span class="n">DateTimeOffset</span><span class="p">(</span><span class="m">2023</span><span class="p">,</span> <span class="m">12</span><span class="p">,</span> <span class="m">31</span><span class="p">,</span> <span class="n">hour</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="n">TimeSpan</span><span class="p">.</span><span class="n">Zero</span><span class="p">));</span>  <span class="c1">// Sunday</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">busOp</span><span class="p">.</span><span class="n">IsOpenHours</span><span class="p">(),</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="n">isOpen</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>One more side note.. you can categorize tests in NUnit too, with a slightly different syntax, by decorating the test methods with a <code>CategoryAttribute</code>.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-timeprovider-and-faketimeprovider/image-6.png"
    width="983"
      height="354"></figure>
<p>Results in the Test Explorer pane</p>
<p>That&rsquo;s it for now! Will this be a game-changer? Who knows. I hope it gets adopted over time. There&rsquo;s even <a href="https://grantwinney.com/using-timeprovider-and-faketimeprovider-in-winforms/"  target="_blank" rel="noreferrer">a way to use this in legacy code</a>, thanks to the .NET team developing a NuGet package that retrofits it to the .NET Framework.</p>
<p>If you&rsquo;d like to learn more about this intriguing new .NET 8 feature, I&rsquo;d encourage you to check out these excellent posts on the subject.</p>
<ul>
<li><a href="https://code-maze.com/csharp-testing-time-dependent-code-with-timeprovider/"  target="_blank" rel="noreferrer">Testing Time-Dependent Code With TimeProvider in .NET | CodeMaze</a></li>
<li><a href="https://andrewlock.net/exploring-the-dotnet-8-preview-avoiding-flaky-tests-with-timeprovider-and-itimer/"  target="_blank" rel="noreferrer">Avoiding flaky tests with TimeProvider and ITimer | Andrew Lock</a></li>
</ul>
<p>If you found this content useful, and would like to learn more about a variety of <a href="https://grantwinney.com/tags/csharp/"  target="_blank" rel="noreferrer">C#</a> features, check out the <a href="https://github.com/grantwinney/CSharpDotNetExamples"  target="_blank" rel="noreferrer">CSharpDotNetExamples repo</a>, where you&rsquo;ll find links to plenty more blog posts and practical examples!</p>
]]></content:encoded><media:content url="https://grantwinney.com/how-to-use-timeprovider-and-faketimeprovider/feature.webp" medium="image" type="image/webp"/></item><item><title>Diligence, laziness.. or both?</title><link>https://grantwinney.com/diligence-laziness-or-both/</link><pubDate>Mon, 04 Dec 2023 22:22:49 +0000</pubDate><guid>https://grantwinney.com/diligence-laziness-or-both/</guid><description>Funny how a little due diligence mixes so well with a healthy interest in avoiding unnecessary future work.</description><content:encoded><![CDATA[<p>Over the last few years, I&rsquo;ve noticed a change in how I approach my work as a developer. When the team gets together to review upcoming work, I&rsquo;m more willing to ask questions when something doesn&rsquo;t sound right, instead of just convincing myself that everyone else &ldquo;gets it&rdquo;. When someone <a href="https://grantwinney.com/what-is-a-code-review/"  target="_blank" rel="noreferrer">opens a pull request</a>, I spend more time reviewing the original requirements and looking at every change. When I learn something new about a project I&rsquo;m involved with, like where the code will be deployed, or why certain requirements exist, <a href="https://grantwinney.com/avoiding-tribal-knowledge-in-programming/"  target="_blank" rel="noreferrer">I document it</a>.</p>
<p>It sounds diligent, or at least I hope it does. Maybe it sounds pedantic, or a little OCD? But I&rsquo;d suggest it&rsquo;s something else too - laziness. And maybe self-preservation, lol.</p>
<p>With any development project, there&rsquo;s a somewhat linear (and somewhat downhill) progression from start to finish, especially on moderately sized teams with a concept of planning, coding, merging, testing, releasing, etc. It&rsquo;s easier to plan before any code is written, to fix the code before it&rsquo;s deployed for testing, and to test it before going to production. The further things travel down the hill, the tougher it is to haul them back up, especially when it&rsquo;s due to something missing or unclear that should&rsquo;ve been called out ages ago. Do that a few (or a few dozen) times, and you start to feel like <a href="https://mythopedia.com/topics/sisyphus"  target="_blank" rel="noreferrer">Sisyphus</a>, rolling a big ball of code uphill for all eternity.</p>
<p>So sure, my (and anyone else&rsquo;s) attention to requirements, PRs, and whatever else is diligence.. but it&rsquo;s more than that too. Once you&rsquo;ve run around the hamster wheel a few times, and seen others run it too, you learn that laziness isn&rsquo;t <em>all</em> bad, and that a little more work now means far less work later.</p>
]]></content:encoded><media:content url="https://grantwinney.com/diligence-laziness-or-both/feature.jpg" medium="image" type="image/webp"/></item><item><title>A swirly mass of shared code</title><link>https://grantwinney.com/swirly-mass-of-code/</link><pubDate>Tue, 14 Nov 2023 04:31:52 +0000</pubDate><guid>https://grantwinney.com/swirly-mass-of-code/</guid><description/><content:encoded><![CDATA[<p>I heard a story recently, where a team was asked, after spending months adding a set of features to a codebase, to remove a specific feature from very early on in the project, right before the release date. Other features had been built around it and on top of it. Without necessarily even intending too, the devs that came after that code was written would&rsquo;ve had to understand it in order to add to it. I don&rsquo;t know what the outcome was, but that&rsquo;s not an easy ask.</p>
<p>If you&rsquo;re a fan of Harry Potter (the older stuff, not <em>The Cursed Playscript</em> or <em>The 50 Incarnations of Grindelwald</em> trilogy), you&rsquo;ve heard of pensieves. Those little dishes of weird, smoky, flowy &ldquo;thoughts&rdquo;. A person could offload new thoughts into them, slosh the contents around like a fine wine, let them sit there mixing and simmering for awhile, and then extract them later to gain new insights.</p>
<p>Like everything in the wizarding world, the rules around pensieves and extracting thoughts were pretty loose. Harry jumps into them and experiences other people&rsquo;s thoughts, and Snape ends up handing some of his own over to Harry, after which Harry would remember them so&hellip; are those <em>new</em> thoughts or did he just soak in someone else&rsquo;s? And don&rsquo;t get me started on why Mrs Weasley could make the dishes wash themselves, but not accio some bricks to make a bigger house.</p>
<p>A codebase is a bit like a pensieve.. one that an entire team is sharing. Each dev adds some code and it&rsquo;s mixed in with everything else - all the thoughts, ideas, and goals that came before. Each new thing builds on, and touches, and affects the rest, and what you end up with is a <em>new</em> thing that&rsquo;s not exactly what it was before. Ever changing, ever mutating.</p>
<p>Devs that come even later dip their faces right into that swirly mass of code <em>(quite the image)</em> and, combined with their own experience, come away with a new insight into how the code works. Later on they&rsquo;ll add their own thoughts and code to the swirling mass, and the codebase will look different, again. For a short time, you could extract those new thoughts from the codebase, but that gets pretty tough pretty quickly.</p>
<p>A lot of things are out of our control when we&rsquo;re writing code though. That request to remove a feature.. stuff like that happens. Some feature depends on a third-party integration that&rsquo;s not done yet, and there&rsquo;s nothing anyone can do about it. Before trying to pull out every trace of code related to a feature though, it&rsquo;s certainly worth thinking about how to do the <em>least</em> impactful thing, like commenting out a few lines that affect some important calculations, or the single line that calls the rest of the code in question.</p>
<p>After all, safely removing code can take as long as adding it in the first place. 😬</p>
]]></content:encoded><media:content url="https://grantwinney.com/swirly-mass-of-code/feature.png" medium="image" type="image/webp"/></item><item><title>If/else vs switch/case pattern matching</title><link>https://grantwinney.com/if-else-vs-switch-case-pattern-matching/</link><pubDate>Fri, 03 Nov 2023 16:35:50 +0000</pubDate><guid>https://grantwinney.com/if-else-vs-switch-case-pattern-matching/</guid><description>A look at if/else, switch/case, pattern matching, other options &amp;hellip; and which is best. (spoiler: none ;) )</description><content:encoded><![CDATA[<p>I stumbled on a pull request recently, in which the suggestion was made to replace an <code>if/else</code> block with a <code>switch/case</code>. The reviewer seemed to feel it was &ldquo;better&rdquo;. In reality, these are just two approaches to organizing conditional logic, and using one or the other mostly comes down to a matter of taste.. especially with changes to C# in the last few years, but more on that below.</p>
<blockquote><p>The code in this post is available on <a href="https://github.com/grantwinney/CSharpDotNetExamples/tree/master/C%23%2009/SwitchPatternMatchingVsIfElse"  target="_blank" rel="noreferrer">GitHub</a>, for you to use, expand upon, or just follow along while you read&hellip; and hopefully discover something new!</p>
</blockquote><p>Thinking back to when <em>I&rsquo;ve</em> used one over the other, I&rsquo;d say I reserve <code>switch/case</code> for sets with a distinct, finite number of values. Say you want to set a couple flags, based on the current set piece being manipulated in a game of chess. You might use code like this:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">enum</span> <span class="n">ChessPiece</span> <span class="p">{</span> <span class="n">Rook</span><span class="p">,</span> <span class="n">Knight</span><span class="p">,</span> <span class="n">Bishop</span><span class="p">,</span> <span class="n">King</span><span class="p">,</span> <span class="n">Queen</span><span class="p">,</span> <span class="n">Pawn</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">canMoveMultiple</span> <span class="p">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">canMoveStraight</span> <span class="p">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">switch</span> <span class="p">(</span><span class="n">currentChessPiece</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">case</span> <span class="n">ChessPiece</span><span class="p">.</span><span class="n">Rook</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">canMoveMultiple</span> <span class="p">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">canMoveStraight</span> <span class="p">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">case</span> <span class="n">ChessPiece</span><span class="p">.</span><span class="n">Bishop</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">canMoveMultiple</span> <span class="p">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">canMoveStraight</span> <span class="p">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// etc, etc.</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The above code could easily be replaced with an <code>if/else</code>; however, I (subjectively) think the first one is &ldquo;better&rdquo;.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-charp" data-lang="charp">enum ChessPiece { Rook, Knight, Bishop, King, Queen, Pawn }

var canMoveMultiple = false;
var canMoveStraight = false;

if (currentChessPiece == ChessPiece.Rook)
{
  canMoveMultiple = true;
  canMoveStraight = true;
}
else if (currentChessPiece == ChessPiece.Bishop)
{
  canMoveMultiple = true;
  canMoveStraight = false;
}
else if ( // etc, etc...</code></pre></div>
<p>We usually have quite a bit of freedom in how we write our code though, so maybe the &ldquo;best&rdquo; option is to do something else entirely. One might be more readable, or more testable, or just more consistent with the rest of the code in the application.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">enum</span> <span class="n">ChessPiece</span> <span class="p">{</span> <span class="n">Rook</span><span class="p">,</span> <span class="n">Knight</span><span class="p">,</span> <span class="n">Bishop</span><span class="p">,</span> <span class="n">King</span><span class="p">,</span> <span class="n">Queen</span><span class="p">,</span> <span class="n">Pawn</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">canMoveMultiple</span> <span class="p">=</span> <span class="n">currentChessPiece</span> <span class="p">==</span> <span class="n">ChessPiece</span><span class="p">.</span><span class="n">Rook</span>
</span></span><span class="line"><span class="cl">  <span class="p">||</span> <span class="n">currentChessPiece</span> <span class="p">==</span> <span class="n">ChessPiece</span><span class="p">.</span><span class="n">Knight</span>
</span></span><span class="line"><span class="cl">  <span class="p">||</span> <span class="n">currentChessPiece</span> <span class="p">==</span> <span class="n">ChessPiece</span><span class="p">.</span><span class="n">Bishop</span>
</span></span><span class="line"><span class="cl">  <span class="p">||</span> <span class="n">currentChessPiece</span> <span class="p">==</span> <span class="n">ChessPiece</span><span class="p">.</span><span class="n">Queen</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">canMoveStraight</span> <span class="p">=</span> <span class="n">currentChessPiece</span> <span class="p">==</span> <span class="n">ChessPiece</span><span class="p">.</span><span class="n">Rook</span>
</span></span><span class="line"><span class="cl">  <span class="p">||</span> <span class="n">currentChessPiece</span> <span class="p">==</span> <span class="n">ChessPiece</span><span class="p">.</span><span class="n">King</span>
</span></span><span class="line"><span class="cl">  <span class="p">||</span> <span class="n">currentChessPiece</span> <span class="p">==</span> <span class="n">ChessPiece</span><span class="p">.</span><span class="n">Queen</span>
</span></span><span class="line"><span class="cl">  <span class="p">||</span> <span class="n">currentChessPiece</span> <span class="p">==</span> <span class="n">ChessPiece</span><span class="p">.</span><span class="n">Pawn</span><span class="p">;</span></span></span></code></pre></div></div>

<h2 class="relative group">Traditional switch/case can&rsquo;t match if/else
    <div id="traditional-switchcase-cant-match-ifelse" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#traditional-switchcase-cant-match-ifelse" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>One of the drawbacks to <code>switch/case</code> has traditionally been that it could only test <em>distinct</em> values - not a range of values like greater or less than. A particular &ldquo;case&rdquo; could test a single value, but it couldn&rsquo;t do anything too fancy.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">switch</span> <span class="p">(</span><span class="n">bankBalance</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">case</span> <span class="p">&gt;</span> <span class="m">1000000</span><span class="p">:</span>  <span class="c1">// can&#39;t do that...</span>
</span></span><span class="line"><span class="cl">    <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;You&#39;re getting a yacht!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">case</span> <span class="m">5</span> <span class="n">to</span> <span class="m">10</span><span class="p">:</span>  <span class="c1">// can&#39;t do that either...</span>
</span></span><span class="line"><span class="cl">    <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;You&#39;re getting a coffee!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">case</span> <span class="m">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;You&#39;re getting a job!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>You could use the &ldquo;default&rdquo; case to catch a single range of values, I suppose, but that&rsquo;s incredibly limited.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">remainingBal</span> <span class="p">=</span> <span class="n">currentBal</span> <span class="p">-</span> <span class="n">purchPrice</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">switch</span> <span class="p">(</span><span class="n">remainingBal</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">case</span> <span class="m">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;Insufficient funds.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">default</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;You&#39;re all good!&#34;</span><span class="p">);</span>  <span class="c1">// oh wait, negative values are a thing</span>
</span></span><span class="line"><span class="cl">    <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h2 class="relative group">Pattern matching to the rescue
    <div id="pattern-matching-to-the-rescue" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#pattern-matching-to-the-rescue" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Things changed in C# 9 though, with all kinds of <a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9#pattern-matching-enhancements"  target="_blank" rel="noreferrer">pattern matching enhancements</a>. It probably started before that, but whenever the additional pattern matching / guard clause / case guard / whatever you want to call it was tacked on, the <code>switch/case</code> construct supports all kinds of fancy conditions now. It&rsquo;s thumbing its nose at <code>if/else</code>, singing &ldquo;anything you can do, I can do &hellip; just as well&rdquo;.</p>
<p>There&rsquo;s a lot to check out in the <a href="https://learn.microsoft.com/en-US/dotnet/csharp/language-reference/operators/switch-expression"  target="_blank" rel="noreferrer">switch expression</a> docs too, especially in the section on &ldquo;case guards&rdquo;, but you&rsquo;re here so let&rsquo;s play around with a few examples just to see what it looks like.</p>

<h2 class="relative group">Examples, using the Open Notify API
    <div id="examples-using-the-open-notify-api" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#examples-using-the-open-notify-api" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>A few years back, <a href="https://grantwinney.com/what-is-iss-notify-api/"  target="_blank" rel="noreferrer">I wrote about the Open Notify API</a>. It&rsquo;s as straight-forward an API as you could possibly have. One of the endpoints returns some data about the current location of the ISS, and the JSON response looks like this:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span><span class="nt">&#34;timestamp&#34;</span><span class="p">:</span> <span class="mi">1698171977</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;message&#34;</span><span class="p">:</span> <span class="s2">&#34;success&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;iss_position&#34;</span><span class="p">:</span> <span class="p">{</span><span class="nt">&#34;latitude&#34;</span><span class="p">:</span> <span class="s2">&#34;-50.8709&#34;</span><span class="p">,</span> <span class="nt">&#34;longitude&#34;</span><span class="p">:</span> <span class="s2">&#34;-6.0132&#34;</span><span class="p">}}</span></span></span></code></pre></div></div>
<p>First, we need a couple classes to hold the response. The &ldquo;message&rdquo; property contains the status of the API call, and the &ldquo;iss_position&rdquo; is, well.. I&rsquo;ll leave it to your imagination. <em>(A</em> <a href="https://www.thoughtco.com/degree-of-latitude-and-longitude-distance-4070616"  target="_blank" rel="noreferrer"><em>review of latitude and longitude</em></a><em>, for those who want extra credit, lol.)</em></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">IssResponse</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="na">    [JsonPropertyName(&#34;timestamp&#34;)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">Timestamp</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [JsonPropertyName(&#34;message&#34;)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Message</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [JsonPropertyName(&#34;iss_position&#34;)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">IssPosition</span> <span class="n">Position</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">IssPosition</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="na">    [JsonPropertyName(&#34;longitude&#34;)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Longitude</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [JsonPropertyName(&#34;latitude&#34;)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Latitude</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Then we&rsquo;ll hit the API endpoint using <a href="https://restsharp.dev/"  target="_blank" rel="noreferrer">RestSharp</a> and parse out the response into a couple variables for latitude and longitude. <em>(Normally I&rsquo;d test the bool value that</em> <em><code>_decimal.TryParse_</code></em> <em>returns, but I&rsquo;m assuming, perhaps incorrectly, that if the API were going to return invalid long/lat values, then the &ldquo;message&rdquo; would not be &ldquo;success&rdquo;.)</em></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">client</span> <span class="p">=</span> <span class="k">new</span> <span class="n">RestClient</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">request</span> <span class="p">=</span> <span class="k">new</span> <span class="n">RestRequest</span><span class="p">(</span><span class="s">&#34;http://api.open-notify.org/iss-now.json&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">response</span> <span class="p">=</span> <span class="k">await</span> <span class="n">client</span><span class="p">.</span><span class="n">GetAsync</span><span class="p">&lt;</span><span class="n">IssResponse</span><span class="p">&gt;(</span><span class="n">request</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">decimal</span><span class="p">.</span><span class="n">TryParse</span><span class="p">(</span><span class="n">response</span><span class="p">?.</span><span class="n">Position</span><span class="p">.</span><span class="n">Latitude</span><span class="p">,</span> <span class="k">out</span> <span class="kt">var</span> <span class="n">latitude</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kt">decimal</span><span class="p">.</span><span class="n">TryParse</span><span class="p">(</span><span class="n">response</span><span class="p">?.</span><span class="n">Position</span><span class="p">.</span><span class="n">Longitude</span><span class="p">,</span> <span class="k">out</span> <span class="kt">var</span> <span class="n">longitude</span><span class="p">);</span></span></span></code></pre></div></div>

<h3 class="relative group">If/else
    <div id="ifelse" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#ifelse" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Alrighty then. If we wanted to print out a message to the user about the location of the ISS, we <em>could</em> use a bunch of nested <code>if/else</code> statements like this:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">switch</span> <span class="p">(</span><span class="n">response</span><span class="p">?.</span><span class="n">Message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">case</span> <span class="s">&#34;success&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">latitude</span> <span class="p">&gt;</span> <span class="m">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="n">longitude</span> <span class="p">&gt;</span> <span class="m">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;The ISS is over the northern hemisphere, east of the prime meredian.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">longitude</span> <span class="p">==</span> <span class="m">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;The ISS is over the northern hemisphere, on the prime meredian.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="k">else</span>
</span></span><span class="line"><span class="cl">        <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;The ISS is over the northern hemisphere, west of the prime meredian.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">latitude</span> <span class="p">==</span> <span class="m">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="n">longitude</span> <span class="p">&gt;</span> <span class="m">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;The ISS is over the equator, east of the prime meredian.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">longitude</span> <span class="p">==</span> <span class="m">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;The ISS is over the equator, on the prime meredian.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="k">else</span>
</span></span><span class="line"><span class="cl">        <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;The ISS is over the equator, west of the prime meredian.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="n">longitude</span> <span class="p">&gt;</span> <span class="m">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;The ISS is over the southern hemisphere, east of the prime meredian.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">longitude</span> <span class="p">==</span> <span class="m">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;The ISS is over the southern hemisphere, on the prime meredian.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="k">else</span>
</span></span><span class="line"><span class="cl">        <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;The ISS is over the southern hemisphere, west of the prime meredian.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">default</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">@&#34; ¯\_(ツ)_/¯ &#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h3 class="relative group">Switch/case with no pattern matching
    <div id="switchcase-with-no-pattern-matching" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#switchcase-with-no-pattern-matching" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The traditional <code>switch/case</code> couldn&rsquo;t handle any complex logic beyond a simple pattern match, so the above would&rsquo;ve probably been our only option.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">switch</span> <span class="p">(</span><span class="n">response</span><span class="p">?.</span><span class="n">Message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">case</span> <span class="s">&#34;success&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">switch</span> <span class="p">(</span><span class="n">latitude</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="m">0</span><span class="p">:</span>   <span class="c1">// What about greater or less than 0?</span>
</span></span><span class="line"><span class="cl">          <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">default</span><span class="p">:</span>  <span class="c1">// This would catch greater AND less than 0...</span>
</span></span><span class="line"><span class="cl">          <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// etc, etc...</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">default</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">@&#34; ¯\_(ツ)_/¯ &#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h3 class="relative group">Switch case WITH pattern matching
    <div id="switch-case-with-pattern-matching" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#switch-case-with-pattern-matching" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>But with pattern matching (guard clauses / whatever), the above is possible to fully convert. We can check that when the message is &ldquo;success&rdquo; and lat/long are both greater than 0, we hit one case, but if the message is &ldquo;success&rdquo; and lat/long are both <em>less</em> than 0, we hit another case&hellip; and on and on.</p>
<p>There are <em>tons</em> of different patterns too, and even shorthand ways of writing it that save some keystrokes. If you&rsquo;re interested in more, go check out the <a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9#pattern-matching-enhancements"  target="_blank" rel="noreferrer">pattern matching enhancements</a> in C# 9 and the <a href="https://learn.microsoft.com/en-US/dotnet/csharp/language-reference/operators/switch-expression"  target="_blank" rel="noreferrer">switch expression</a> docs, and then whatever those link to. Or just keep in mind that this is available as another tool, and the next time you&rsquo;re thinking about using a switch/case, give them the once over.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">switch</span> <span class="p">(</span><span class="n">response</span><span class="p">?.</span><span class="n">Message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="s">&#34;success&#34;</span> <span class="n">when</span> <span class="n">latitude</span> <span class="p">&gt;</span> <span class="m">0</span> <span class="p">&amp;&amp;</span> <span class="n">longitude</span> <span class="p">&gt;</span> <span class="m">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;The ISS is over the northern hemisphere, east of the prime meredian.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="s">&#34;success&#34;</span> <span class="n">when</span> <span class="n">latitude</span> <span class="p">&gt;</span> <span class="m">0</span> <span class="p">&amp;&amp;</span> <span class="n">longitude</span> <span class="p">==</span> <span class="m">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;The ISS is over the northern hemisphere, on the prime meredian.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="s">&#34;success&#34;</span> <span class="n">when</span> <span class="n">latitude</span> <span class="p">&gt;</span> <span class="m">0</span> <span class="p">&amp;&amp;</span> <span class="n">longitude</span> <span class="p">&lt;</span> <span class="m">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;The ISS is over the northern hemisphere, west of the prime meredian.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// etc, etc...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">default</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">@&#34; ¯\_(ツ)_/¯ &#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Don&rsquo;t forget about switch expressions too, which can be quicker to read but only apply when you&rsquo;re returning a single value.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">message</span> <span class="p">=</span> <span class="n">response</span><span class="p">?.</span><span class="n">Message</span> <span class="k">switch</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="s">&#34;success&#34;</span> <span class="n">when</span> <span class="n">latitude</span> <span class="p">&gt;</span> <span class="m">0</span> <span class="p">&amp;&amp;</span> <span class="n">longitude</span> <span class="p">&gt;</span> <span class="m">0</span> <span class="p">=&gt;</span> <span class="s">&#34;The ISS is over the northern hemisphere, east of the prime meredian.&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="s">&#34;success&#34;</span> <span class="n">when</span> <span class="n">latitude</span> <span class="p">&gt;</span> <span class="m">0</span> <span class="p">&amp;&amp;</span> <span class="n">longitude</span> <span class="p">==</span> <span class="m">0</span> <span class="p">=&gt;</span> <span class="s">&#34;The ISS is over the northern hemisphere, on the prime meredian.&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="s">&#34;success&#34;</span> <span class="n">when</span> <span class="n">latitude</span> <span class="p">&gt;</span> <span class="m">0</span> <span class="p">&amp;&amp;</span> <span class="n">longitude</span> <span class="p">&lt;</span> <span class="m">0</span> <span class="p">=&gt;</span> <span class="s">&#34;The ISS is over the northern hemisphere, west of the prime meredian.&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// and on and on...</span>
</span></span><span class="line"><span class="cl">  <span class="n">_</span> <span class="p">=&gt;</span> <span class="s">@&#34; ¯\_(ツ)_/¯ &#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">message</span><span class="p">);</span></span></span></code></pre></div></div>
<p>Here&rsquo;s another example using switch expressions, just because I think they&rsquo;re neat and that stepping away from the API example for a minute might be good. Imagine this is for some business app, where the user action and other factors (like the current date) determine which report is run.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">void</span> <span class="n">RunReport</span><span class="p">(</span><span class="kt">string</span> <span class="n">userAction</span><span class="p">,</span> <span class="n">User</span> <span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kt">var</span> <span class="n">d</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kt">var</span> <span class="n">reportToRun</span> <span class="p">=</span> <span class="n">userAction</span><span class="p">.</span><span class="n">ToLower</span><span class="p">()</span> <span class="k">switch</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;sale&#34;</span> <span class="p">=&gt;</span> <span class="s">&#34;CustomerPurchase&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="s">&#34;audit&#34;</span> <span class="n">when</span> <span class="n">user</span><span class="p">.</span><span class="n">Position</span> <span class="p">==</span> <span class="s">&#34;manager&#34;</span> <span class="p">||</span> <span class="n">user</span><span class="p">.</span><span class="n">Department</span> <span class="p">==</span> <span class="s">&#34;accounting&#34;</span> <span class="p">=&gt;</span> <span class="s">&#34;AuditDetail&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;audit&#34;</span> <span class="p">=&gt;</span> <span class="s">&#34;AuditPersonal&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="s">&#34;timecard&#34;</span> <span class="n">when</span> <span class="n">user</span><span class="p">.</span><span class="n">Department</span> <span class="p">==</span> <span class="s">&#34;hr&#34;</span> <span class="p">=&gt;</span> <span class="s">&#34;CorporateSchedule&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;timecard&#34;</span> <span class="n">when</span> <span class="n">d</span><span class="p">.</span><span class="n">DayOfWeek</span> <span class="p">==</span> <span class="n">DayOfWeek</span><span class="p">.</span><span class="n">Saturday</span> <span class="p">||</span> <span class="n">d</span><span class="p">.</span><span class="n">DayOfWeek</span> <span class="p">==</span> <span class="n">DayOfWeek</span><span class="p">.</span><span class="n">Sunday</span> <span class="p">=&gt;</span> <span class="s">&#34;OvertimeSchedule&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;timecard&#34;</span> <span class="p">=&gt;</span> <span class="s">&#34;Schedule&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="s">&#34;support&#34;</span> <span class="n">when</span> <span class="n">user</span><span class="p">.</span><span class="n">Position</span> <span class="p">==</span> <span class="s">&#34;lead&#34;</span> <span class="p">=&gt;</span> <span class="s">&#34;DailySupportTickets&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;support&#34;</span> <span class="n">when</span> <span class="n">user</span><span class="p">.</span><span class="n">Position</span> <span class="p">==</span> <span class="s">&#34;softwaredeveloper&#34;</span> <span class="p">=&gt;</span> <span class="s">&#34;SupportTicketDetail&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;support&#34;</span> <span class="p">=&gt;</span> <span class="s">&#34;SupportTicketSummary&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// ExecuteReport(reportToRun);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h3 class="relative group">There&rsquo;s always another way
    <div id="theres-always-another-way" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#theres-always-another-way" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>And of course, don&rsquo;t forget that there&rsquo;s always another way to do things, and sometimes neither a huge <code>if/else</code> <em>or</em> <code>switch/case</code> block result in the shortest, concisest, <a href="https://docs.getdbt.com/terms/dry"  target="_blank" rel="noreferrer">DRY</a>est code you could write.</p>
<p>Back to the API example, here&rsquo;s a <em>much</em> shorter way to do the same as all the above, using a few <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/conditional-operator"  target="_blank" rel="noreferrer">ternary conditional operators</a> and some <a href="https://grantwinney.com/using-string-interpolation-to-craft-readable-strings/"  target="_blank" rel="noreferrer">string interpolation</a>. Whether it&rsquo;s more readable is an exercise I&rsquo;ll leave to the reader. ;)</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">latMsg</span> <span class="p">=</span> <span class="n">latitude</span> <span class="p">&gt;</span> <span class="m">0</span> <span class="p">?</span> <span class="s">&#34;northern hemisphere&#34;</span> <span class="p">:</span> <span class="n">latitude</span> <span class="p">==</span> <span class="m">0</span> <span class="p">?</span> <span class="s">&#34;equator&#34;</span> <span class="p">:</span> <span class="s">&#34;southern hemisphere&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">lngMsg</span> <span class="p">=</span> <span class="n">longitude</span> <span class="p">&gt;</span> <span class="m">0</span> <span class="p">?</span> <span class="s">&#34;east of&#34;</span> <span class="p">:</span> <span class="n">longitude</span> <span class="p">==</span> <span class="m">0</span> <span class="p">?</span> <span class="s">&#34;on&#34;</span> <span class="p">:</span> <span class="s">&#34;west of&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">response</span><span class="p">?.</span><span class="n">Message</span> <span class="p">==</span> <span class="s">&#34;success&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">?</span> <span class="s">$&#34;The ISS is over the {latMsg}, {lngMsg} the prime meridian.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">:</span> <span class="s">&#34; ¯\\_(ツ)_/¯ &#34;</span><span class="p">);</span></span></span></code></pre></div></div>
<p>If you found this content useful, and want to learn more about a variety of C# features, check out <a href="https://github.com/grantwinney/CSharpDotNetExamples"  target="_blank" rel="noreferrer">this GitHub repo</a>, where you&rsquo;ll find links to plenty more blog posts and practical examples!</p>
]]></content:encoded><media:content url="https://grantwinney.com/if-else-vs-switch-case-pattern-matching/feature.webp" medium="image" type="image/webp"/></item><item><title>What are list patterns in C#?</title><link>https://grantwinney.com/whats-a-list-pattern-in-csharp/</link><pubDate>Thu, 31 Aug 2023 23:04:38 +0000</pubDate><guid>https://grantwinney.com/whats-a-list-pattern-in-csharp/</guid><description>C# has been getting a lot of pattern matching love in recent years, like with list patterns in C# 11. The problem is knowing where and how to use it.</description><content:encoded><![CDATA[<p>There&rsquo;s very little I miss from my days of Erlang programming. One of the things I do miss, though, is pattern matching. Erlang does a <em>lot</em> with it, and it&rsquo;s interesting to see C# doing more with it in the last few major releases too.</p>
<p>Imagine, for a moment, if we could do some kind of pattern matching while <a href="https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/member-overloading"  target="_blank" rel="noreferrer">overloading methods</a>. In the (completely invalid) code beow, the first method catches any call where the first parameter is &ldquo;Anne&rdquo;, the second catches any call where the year is 1999, and the last one catches everything else. It&rsquo;s not a perfect analogy, but it&rsquo;s similar to what you can do in Erlang, without needing to have a bunch of <code>IF/ELSE</code> statements.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">RunAwfulBdayRoutine</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">WishHappyBirthday</span><span class="p">(</span><span class="s">&#34;Anne&#34;</span><span class="p">,</span> <span class="k">new</span> <span class="n">DateOnly</span><span class="p">(</span><span class="m">1990</span><span class="p">,</span><span class="m">1</span><span class="p">,</span><span class="m">1</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">WishHappyBirthday</span><span class="p">(</span><span class="s">&#34;Tom&#34;</span><span class="p">,</span> <span class="k">new</span> <span class="n">DateOnly</span><span class="p">(</span><span class="m">1999</span><span class="p">,</span><span class="m">2</span><span class="p">,</span><span class="m">2</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">WishHappyBirthday</span><span class="p">(</span><span class="s">&#34;Karen&#34;</span><span class="p">,</span> <span class="k">new</span> <span class="n">DateOnly</span><span class="p">(</span><span class="m">2005</span><span class="p">,</span><span class="m">3</span><span class="p">,</span><span class="m">3</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">WishHappyBirthday</span><span class="p">(</span><span class="s">&#34;Anne&#34;</span><span class="p">,</span> <span class="n">DateOnly</span> <span class="n">birthdate</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;Wow, it&#39;s your birthday Anne-iversary!&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">WishHappyBirthday</span><span class="p">(</span><span class="kt">string</span> <span class="n">name</span><span class="p">,</span> <span class="n">MM</span><span class="p">/</span><span class="n">dd</span><span class="p">/</span><span class="m">1999</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Happy birthday {name}, party like it&#39;s 1999! So uh.. mash up some peas or something I guess. :/&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">WishHappyBirthday</span><span class="p">(</span><span class="kt">string</span> <span class="n">name</span><span class="p">,</span> <span class="n">DateOnly</span> <span class="n">birthdate</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">today</span> <span class="p">=</span> <span class="n">DateOnly</span><span class="p">.</span><span class="n">FromDateTime</span><span class="p">(</span><span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;You&#39;re {today.DayNumber - birthdate.DayNumber} days old, {name}... quite the uh, large number.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Obviously C# doesn&rsquo;t allow anything like that (yet anyway), but they <em>did</em> introduce something called <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns#list-patterns"  target="_blank" rel="noreferrer">list patterns</a> in C# 11 that&rsquo;s worth checking out. Unfortunately, although Microsoft&rsquo;s docs have improved a lot over the years, the docs aren&rsquo;t selling this feature well.. at least not to me.</p>
<p>I looked up some other examples around the web too, but they&rsquo;re all similarly unrealistic, typically showing a list of numbers being passed around and acted on. What these numbers mean is anyone&rsquo;s guess_,_ and then the new list pattern feature is used to make sure they all fall within certain ranges and whatnot.</p>
<p>Almost every time I&rsquo;ve had a collection of <em>anything,</em> it&rsquo;s coming from a db and represents a list of employees, security settings, subscriptions&hellip; things that should have their own class. If I had a list of seemingly-random numbers like in the examples I saw, I&rsquo;d find a better way to represent that data first, and then using list patterns probably wouldn&rsquo;t apply anyway.</p>
<p>So I&rsquo;m left wondering, what <em>are</em> some realistic usages for this new feature? What can we use it for, and how can it make our lives as programmers a little easier? Let&rsquo;s look at a few use cases.</p>
<blockquote><p>The code in this post is available on <a href="https://github.com/grantwinney/CSharpDotNetExamples/tree/master/C%23%2011/ListPatternMatching"  target="_blank" rel="noreferrer">GitHub</a>, for you to use, expand upon, or just follow along while you read&hellip; and hopefully discover something new!</p>
</blockquote>
<h2 class="relative group">Matching on CSV files with inconsistent formats
    <div id="matching-on-csv-files-with-inconsistent-formats" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#matching-on-csv-files-with-inconsistent-formats" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>One possibility is using it to parse CSV files with somewhat unpredictable formats. CSV files are just comma-delimited files after all, so each line/record is easily split into a collection of strings. Let&rsquo;s assume a few things for a hypothetical scenario:</p>
<ol>
<li>We&rsquo;ve got an app that needs to import a variety of CSV files.</li>
<li>All the CSV files represent data about stores, but they all look slightly different.</li>
<li>The only thing we can rely on is that the first &ldquo;column&rdquo; is the store&rsquo;s name and the last &ldquo;column&rdquo; is the store&rsquo;s total sales. Some files have just those 2 columns, but others have dozens in between, none of which we care about.</li>
</ol>
<p>Our data from the various files might look like this hodge-podge.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// Pretend we&#39;re actually loading a variety of inconsistently formatted CSV files,</span>
</span></span><span class="line"><span class="cl"><span class="c1">// which may have been exported from some third-party system</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">inconsistentCSVFileRecords</span> <span class="p">=</span> <span class="k">new</span><span class="p">[]</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;Willy Wonka&#39;s Chocolate Factory, true, false, 400000.00&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;The Shop Around The Corner, true, false, maybe, 12, 1200, M-F, 15000.00&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;Zonko&#39;s Joke Shop, 13000.00&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span></span></span></code></pre></div></div>
<p>After splitting each string, we can define a pattern that grabs the name and sales (first and last) values for further processing, while ignoring everything <em>(or nothing!)</em> in between, with the <code>..</code> slice pattern.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">stores</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">totalSales</span> <span class="p">=</span> <span class="m">0</span><span class="n">m</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="k">record</span> <span class="nc">in</span> <span class="n">inconsistentCSVFileRecords</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">record</span><span class="p">.</span><span class="n">Split</span><span class="p">(</span><span class="sc">&#39;,&#39;</span><span class="p">)</span> <span class="k">is</span> <span class="p">[</span><span class="kt">string</span> <span class="n">name</span><span class="p">,</span> <span class="p">..,</span> <span class="kt">string</span> <span class="n">sales</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">stores</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="n">name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">totalSales</span> <span class="p">+=</span> <span class="kt">decimal</span><span class="p">.</span><span class="n">Parse</span><span class="p">(</span><span class="n">sales</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;The following stores had combined sales of ${totalSales}:&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="kt">string</span><span class="p">.</span><span class="n">Join</span><span class="p">(</span><span class="s">&#34;, &#34;</span><span class="p">,</span> <span class="n">stores</span><span class="p">));</span></span></span></code></pre></div></div>
<p>The slice pattern is a special pattern that matches 0 or more elements, so we get only the values we&rsquo;re interested in, and toss out the rest.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/whats-a-list-pattern-in-csharp/console-output-1.png"
    width="719"
      height="58"></figure>

<h2 class="relative group">Matching on lists in an XML node
    <div id="matching-on-lists-in-an-xml-node" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#matching-on-lists-in-an-xml-node" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you haven&rsquo;t had to before, there may be a time when you have to create or manipulate an XML file, such as when you&rsquo;re integrating with a <a href="https://blog.postman.com/soap-api-definition/"  target="_blank" rel="noreferrer">SOAP API</a>. Or maybe not, unless you&rsquo;re dealing with a legacy app.. you never know.</p>
<p>Let&rsquo;s assume we&rsquo;re getting a response back from some API endpoint that returns student info, with a node that contains a list of grades. There&rsquo;s nothing in the XML itself that says what the grades mean, but there&rsquo;s some documentation somewhere else that tells us which subjects each grade represents.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// Pretend we&#39;re actually receiving XML like you might get from a SOAP API,</span>
</span></span><span class="line"><span class="cl"><span class="c1">// but this could just as well be JSON from a REST API...</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">xml</span> <span class="p">=</span> <span class="s">@&#34;&lt;students&gt;
</span></span></span><span class="line"><span class="cl"><span class="s">                &lt;student&gt;&lt;name&gt;Greg&lt;/name&gt;&lt;grades&gt;92,91,77,89,85&lt;/grades&gt;&lt;/student&gt;
</span></span></span><span class="line"><span class="cl"><span class="s">                &lt;student&gt;&lt;name&gt;Tina&lt;/name&gt;&lt;grades&gt;97,88,84,91,80&lt;/grades&gt;&lt;/student&gt;
</span></span></span><span class="line"><span class="cl"><span class="s">            &lt;/students&gt;&#34;</span><span class="p">;</span></span></span></code></pre></div></div>
<p>We know we want to parse them out, but don&rsquo;t want to create an entire class to store them because they won&rsquo;t be passed around or saved as-is. And we&rsquo;ve been told to only use a couple of the grades, and toss out the rest.</p>
<p>Using list patterns, we can parse out each student&rsquo;s grades. The underscore discards the second value, because we&rsquo;re not interested in it&hellip; for invent-your-own reason. The slice pattern is used to discard everything after the third grade, but what&rsquo;s the <code>{ Length: 2 }</code> mean? That makes sure that the slice pattern matches on two elements, so if the list of grades for one record has 4 numbers, or 6 or more, the code below won&rsquo;t match on it and won&rsquo;t print it out.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">xdoc</span> <span class="p">=</span> <span class="n">XDocument</span><span class="p">.</span><span class="n">Parse</span><span class="p">(</span><span class="n">xml</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">student</span> <span class="k">in</span> <span class="n">xdoc</span><span class="p">.</span><span class="n">Root</span><span class="p">.</span><span class="n">Elements</span><span class="p">(</span><span class="s">&#34;student&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">grades</span> <span class="p">=</span> <span class="n">student</span><span class="p">.</span><span class="n">Element</span><span class="p">(</span><span class="s">&#34;grades&#34;</span><span class="p">).</span><span class="n">Value</span><span class="p">.</span><span class="n">Split</span><span class="p">(</span><span class="sc">&#39;,&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">grades</span> <span class="k">is</span> <span class="p">[</span><span class="kt">var</span> <span class="n">math</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="kt">var</span> <span class="n">art</span><span class="p">,</span> <span class="p">..</span> <span class="p">{</span> <span class="n">Length</span><span class="p">:</span> <span class="m">2</span> <span class="p">}])</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">name</span> <span class="p">=</span> <span class="n">student</span><span class="p">.</span><span class="n">Element</span><span class="p">(</span><span class="s">&#34;name&#34;</span><span class="p">).</span><span class="n">Value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;{name} got a {math}% in math and a {art}% in art.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/whats-a-list-pattern-in-csharp/console-output-2.png"
    width="378"
      height="50"></figure>

<h2 class="relative group">Matching on the header in some text files
    <div id="matching-on-the-header-in-some-text-files" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#matching-on-the-header-in-some-text-files" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>It&rsquo;s also possible that you&rsquo;ll have some text files which have some pattern to them, some predictable portion, and that you&rsquo;ll need to extract that portion. The call to <code>File.ReadAllLines</code> returns (conveniently) all the lines of a file as a string array.</p>
<p>Imagine we have a series of text files, each with a different presidential speech in them, but no matter the content, there&rsquo;s always a header with the author, title, and the date of the speech.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">Author: Abraham Lincoln
</span></span><span class="line"><span class="cl">Title: Gettysburg Address
</span></span><span class="line"><span class="cl">Date: 11/19/1863
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Four score and seven years ago our fathers brought forth on this continent...
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">...</span></span></code></pre></div></div>
<p>By reading the lines of each file and then using a list pattern, we can pull out the first three lines, discard the blank line that comes next, and then store all the rest (the speech itself) in another variable. The content that&rsquo;s matched by the slice pattern can actually be stored too, and not simply discarded.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">filePath</span> <span class="k">in</span> <span class="n">Directory</span><span class="p">.</span><span class="n">GetFiles</span><span class="p">(</span><span class="s">&#34;c:\somefilepath\&#34;))
</span></span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">file</span> <span class="p">=</span> <span class="n">File</span><span class="p">.</span><span class="n">ReadAllLines</span><span class="p">(</span><span class="n">filePath</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">file</span> <span class="k">is</span> <span class="p">[</span><span class="kt">var</span> <span class="n">author</span><span class="p">,</span> <span class="kt">var</span> <span class="n">title</span><span class="p">,</span> <span class="kt">var</span> <span class="n">publishDate</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="p">..</span> <span class="kt">var</span> <span class="n">speech</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">author</span> <span class="p">=</span> <span class="n">author</span><span class="p">.</span><span class="n">Remove</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">8</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">title</span> <span class="p">=</span> <span class="n">title</span><span class="p">.</span><span class="n">Remove</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">7</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">publishDate</span> <span class="p">=</span> <span class="n">DateOnly</span><span class="p">.</span><span class="n">Parse</span><span class="p">(</span><span class="n">publishDate</span><span class="p">.</span><span class="n">Remove</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">6</span><span class="p">)).</span><span class="n">ToShortDateString</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;&#34;&#34;On {publishDate}, {author} gave the &#34;</span><span class="p">{</span><span class="n">title</span><span class="p">}</span><span class="s">&#34;. ({speech.Length} lines)&#34;&#34;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/whats-a-list-pattern-in-csharp/console-output-3.png"
    width="826"
      height="68"></figure>

<h2 class="relative group">Matching on arguments passed to a console app
    <div id="matching-on-arguments-passed-to-a-console-app" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#matching-on-arguments-passed-to-a-console-app" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Let&rsquo;s look at one more. It&rsquo;s possible for a console app to accepts a list of arguments. Maybe you wrote an app that can manipulate a file - create it, delete it, search through it, etc. You want users to specify the action with the first parameter, like &ldquo;s&rdquo; for search or &ldquo;d&rdquo; for delete.</p>
<p>Since <code>args</code> is just another string array, you can use list patterns on it as well, performing different logic depending on what that first parameter is.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">args</span> <span class="k">is</span> <span class="p">[</span><span class="s">&#34;s&#34;</span><span class="p">,</span> <span class="kt">var</span> <span class="n">fileToSearch</span><span class="p">,</span> <span class="kt">var</span> <span class="n">searchTerm</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Search for specified file</span>
</span></span><span class="line"><span class="cl">    <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Searching for &#39;{searchTerm}&#39; in {fileToSearch}...&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">args</span> <span class="k">is</span> <span class="p">[</span><span class="s">&#34;d&#34;</span><span class="p">,</span> <span class="kt">var</span> <span class="n">fileToDelete</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Delete specified file</span>
</span></span><span class="line"><span class="cl">    <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Deleting {fileToDelete}...&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">args</span> <span class="k">is</span> <span class="p">[</span><span class="s">&#34;c&#34;</span><span class="p">,</span> <span class="kt">var</span> <span class="n">fileToCreate</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Create specified file</span>
</span></span><span class="line"><span class="cl">    <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Creating {fileToCreate}...&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">args</span> <span class="k">is</span> <span class="p">[..])</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;Invalid input for args!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>By passing in different parameter values, you can see the different results.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/whats-a-list-pattern-in-csharp/console-output-4.png"
    width="451"
      height="173"></figure>
<p>Are these examples more realistic? At the very least, I hope these show off more opportunities for using the list patterns feature than just a random series of numbers.</p>
<blockquote><p>If you want to mess around with any of the code yourself, <a href="https://github.com/grantwinney/CSharpDotNetExamples/tree/master/C%23%2011/ListPatternMatching"  target="_blank" rel="noreferrer">it&rsquo;s available on GitHub</a> as usual. Get it, play with it, and see what you can do.</p>
</blockquote><p>If you found this content useful, and want to learn more about a variety of <a href="https://grantwinney.com/tags/csharp/"  target="_blank" rel="noreferrer">C#</a> features, check out <a href="https://github.com/grantwinney/CSharpDotNetExamples"  target="_blank" rel="noreferrer">this GitHub repo</a>, where you&rsquo;ll find links to plenty more blog posts and practical examples!</p>
]]></content:encoded><media:content url="https://grantwinney.com/whats-a-list-pattern-in-csharp/feature.webp" medium="image" type="image/webp"/></item><item><title>Even in failure, an increase in understanding is a win</title><link>https://grantwinney.com/even-in-failure-success/</link><pubDate>Thu, 24 Aug 2023 21:30:16 +0000</pubDate><guid>https://grantwinney.com/even-in-failure-success/</guid><description>When what we&amp;rsquo;re trying to accomplish fails, the extra knowledge and clarity we get just by making the attempt is a win all by itself.</description><content:encoded><![CDATA[<p>Adding something new to an application is kind of exciting, especially when it&rsquo;s an obvious change, and even <em>moreso</em> when it&rsquo;s something the users actually <em>want</em>. Then there&rsquo;s the behind-the-scenes kinda changes, where you solve some problem in the system, and even though (best case scenario) no one will know you were there, there&rsquo;s still a sense of accomplishment. And of course there&rsquo;s everything related to development too - implementing and figuring out issues with DevOps and git and installers and whatever programming tools you might want to use, etc.</p>
<p>Sometimes though, it&rsquo;s exciting to <em>remove</em> things too.</p>
<p>The more a system grows and ages, and the more hands that touch it and change it and leave their prints all over it, the more complex and unwieldy it becomes. Something that took a few steps to begin with, grows into something with 20 steps that no single person completely understands anymore, which makes it that much harder to ever simplify again. And for those who attempt it, success is never guaranteed. The road to hell and all that&hellip;</p>
<p>I had an opportunity recently to attack a problem like that. We have a deployment process for some reports that involves myriad steps, making updates in this file and that one, copying this over here and that over there, building multiple repos and bringing it all together into one master install. There&rsquo;s a long list of steps that&rsquo;s been built up over time, some parts automated and others definitely not, and I had the audacity - the sheer <em>arrogance</em> - to attempt to remove a couple of those steps. lol</p>
<p>Long story short, it didn&rsquo;t work. It seemed to at first, but after a complete runthrough of everything, it definitely wasn&rsquo;t. No big deal, not the end of the world, it just meant I had to undo my changes. It isn&rsquo;t the failure that I&rsquo;m thinking about though, but what I did with it. My first instinct was to say &ldquo;aw crap&rdquo;, revert all my changes, and just move on. Full stop, reverse course, nothing to see here.</p>
<p>Then I wondered, <em>but why?</em></p>
<p>I spent an evening picking apart the steps I&rsquo;d changed, and the steps around the steps I&rsquo;d changed. After digging for a few hours, I found an installation script that ran some other command line things, and discovered that something being copied from Point A wasn&rsquo;t in the correct format anymore when it arrived at Point B. After reviewing the steps with someone on the team, I learned of yet another step (unknown to me) where certain files are run through a utility and then exported, altering the format slightly&hellip; in just the way needed for the now-failing installation script.</p>
<p>Unfortunately, eliminating the need for <em>that</em> step would be complicated, involve others&rsquo; time rather than just mine, and was not the hill to die on mid-project. I was bummed at first, but then realized that the end result of my failure was understanding a process <em>much</em> better than before. In fact, I can see a time in the near future, due to some work being done by other teams, where I&rsquo;ll be able to revisit this and make it work like I&rsquo;d hoped it would, undoing some tech debt that&rsquo;s accumulated over the years.</p>
<p>Sometimes we don&rsquo;t get the chance to dig deeper.. there&rsquo;s just no time. I&rsquo;ve had quite a few of those. But when we can, even if there&rsquo;s no &ldquo;big win&rdquo; to ultimately be had, more clarity around a thing is a &ldquo;win&rdquo; by itself. Like standing too close to a pixelated image, and then moving a little further back, and a little further back, we keep getting a better and better view of how things work.</p>
]]></content:encoded><media:content url="https://grantwinney.com/even-in-failure-success/feature.webp" medium="image" type="image/webp"/></item><item><title>What are generic attributes in C# 11?</title><link>https://grantwinney.com/what-are-generic-attributes/</link><pubDate>Wed, 23 Aug 2023 15:52:21 +0000</pubDate><guid>https://grantwinney.com/what-are-generic-attributes/</guid><description>Generic attributes increase the flexibility of a very early .NET feature. Let&amp;rsquo;s try using them and see how it keeps our code DRY.</description><content:encoded><![CDATA[<p>For the uninitiated, <a href="https://learn.microsoft.com/en-us/dotnet/csharp/advanced-topics/reflection-and-attributes/"  target="_blank" rel="noreferrer">attributes</a> provide a way to attach extra metadata to a variety of C# elements. They&rsquo;re built into the .NET Framework (like <a href="https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.descriptionattribute"  target="_blank" rel="noreferrer">Description</a>), third-party libraries (like NUnit&rsquo;s <a href="https://docs.nunit.org/articles/nunit/writing-tests/attributes/testfixture.html"  target="_blank" rel="noreferrer">TestFixture</a>), and you can even <a href="https://learn.microsoft.com/en-us/dotnet/csharp/advanced-topics/reflection-and-attributes/attribute-tutorial"  target="_blank" rel="noreferrer">define your own</a>. While they don&rsquo;t directly affect your code, per se, they generally affect your application in some way, like how it compiles or what the user sees at runtime.</p>

<h2 class="relative group">A review of traditional attributes
    <div id="a-review-of-traditional-attributes" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#a-review-of-traditional-attributes" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The <a href="https://learn.microsoft.com/en-us/dotnet/api/system.obsoleteattribute"  target="_blank" rel="noreferrer">Obsolete</a> attribute, for example, is a built-in one used by the compiler to warn other developers that some element is (or soon will be) deprecated.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">BarberShopCustomer</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="na">    [Obsolete(&#34;This value is no longer tracked and will be removed in an upcoming release.&#34;, true)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">DateTime</span> <span class="n">FirstVisit</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">DateTime</span> <span class="n">LastVisit</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="kd">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [Obsolete($&#34;Recommend using {nameof(RecordNewVisitMoreAccurately)}() instead. This method will be removed in upcoming release.&#34;)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">RecordNewVisit</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">LastVisit</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">Today</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">RecordNewVisitMoreAccurately</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">LastVisit</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-are-generic-attributes/method-is-obsolete-1.png"
    width="683"
      height="329"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-are-generic-attributes/method-is-obsolete-2.png"
    width="661"
      height="336"></figure>
<p>If you want to see other examples of attribute usage, here&rsquo;s an article I wrote a few years ago, but today I want to look at a new feature we got in C# 11 called generic attributes.</p>
<p>The new <a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#generic-attributes"  target="_blank" rel="noreferrer">generic attribute</a> brings (as the name suggests) the power of generics to attributes; in other words, an Attribute that can apply to more than one type. When I read this, it wasn&rsquo;t immediately obvious to me how this would be useful. I mean, I get how assigning metadata to an element to indicate that it&rsquo;s obsolete, or is an initializer for tests, or should be serialized is beneficial&hellip; but what do we get from passing the <em>type</em> to the Attribute?</p>
<p>Well, other times we opt in for using generics, like <a href="https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1"  target="_blank" rel="noreferrer"><code>List&lt;T&gt;</code></a> or with <a href="https://grantwinney.com/csharp-generic-math-support/"  target="_blank" rel="noreferrer">Generic Math</a>, it&rsquo;s because we have a series of methods and whatever else that can be reused, and only the type of object being acted upon changes. So what&rsquo;s a case where we can use attributes in the same way? Validation comes to mind as one possibility&hellip;</p>
<blockquote><p>The code in this post is available on <a href="https://github.com/grantwinney/CSharpDotNetExamples/tree/master/C%23%2011/GenericAttributes"  target="_blank" rel="noreferrer">GitHub</a>, for you to use, expand upon, or just follow along while you read&hellip; and hopefully discover something new!</p>
</blockquote>
<h2 class="relative group">A traditional attribute with room to improve
    <div id="a-traditional-attribute-with-room-to-improve" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#a-traditional-attribute-with-room-to-improve" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Let&rsquo;s see how we&rsquo;d create our own validation attributes using what came before. We&rsquo;ll start with a class like this one, with some integer and double values in it that need to be validated.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Moon</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">DiscoveredBy</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [IntegerValidation(MaxValue = 2023)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">DiscoveryYear</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [IntegerValidation(MinValue = 0)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">AverageOrbitDistance</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [DoubleValidation(MinValue = 0)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">double</span> <span class="n">OrbitEccentricity</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Then we create a couple &ldquo;validation&rdquo; classes to act on those different types. There&rsquo;s really nothing different between the two, other than the types themselves. Looking unnecessarily repetitive&hellip;</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">IntegerValidation</span> <span class="p">:</span> <span class="n">ValidationAttribute</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">MinValue</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">int</span><span class="p">.</span><span class="n">MinValue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">MaxValue</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">int</span><span class="p">.</span><span class="n">MaxValue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">protected</span> <span class="kd">override</span> <span class="n">ValidationResult</span><span class="p">?</span> <span class="n">IsValid</span><span class="p">(</span><span class="kt">object?</span> <span class="k">value</span><span class="p">,</span> <span class="n">ValidationContext</span> <span class="n">validationContext</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">num</span> <span class="p">=</span> <span class="n">Convert</span><span class="p">.</span><span class="n">ToInt32</span><span class="p">(</span><span class="k">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">num</span> <span class="p">&gt;=</span> <span class="n">MinValue</span> <span class="p">&amp;&amp;</span> <span class="n">num</span> <span class="p">&lt;=</span> <span class="n">MaxValue</span> <span class="p">?</span> <span class="n">ValidationResult</span><span class="p">.</span><span class="n">Success</span> <span class="p">:</span> <span class="k">new</span> <span class="n">ValidationResult</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">DoubleValidation</span> <span class="p">:</span> <span class="n">ValidationAttribute</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">double</span> <span class="n">MinValue</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">double</span><span class="p">.</span><span class="n">MinValue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">double</span> <span class="n">MaxValue</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">double</span><span class="p">.</span><span class="n">MaxValue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">protected</span> <span class="kd">override</span> <span class="n">ValidationResult</span><span class="p">?</span> <span class="n">IsValid</span><span class="p">(</span><span class="kt">object?</span> <span class="k">value</span><span class="p">,</span> <span class="n">ValidationContext</span> <span class="n">validationContext</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">num</span> <span class="p">=</span> <span class="n">Convert</span><span class="p">.</span><span class="n">ToDouble</span><span class="p">(</span><span class="k">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">num</span> <span class="p">&gt;=</span> <span class="n">MinValue</span> <span class="p">&amp;&amp;</span> <span class="n">num</span> <span class="p">&lt;=</span> <span class="n">MaxValue</span> <span class="p">?</span> <span class="n">ValidationResult</span><span class="p">.</span><span class="n">Success</span> <span class="p">:</span> <span class="k">new</span> <span class="n">ValidationResult</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Finally, this is what it might look like to run the validators against a new instance of the class. I&rsquo;m using <a href="https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations.validator.tryvalidateobject"  target="_blank" rel="noreferrer">TryValidateObject</a> because this is just a console app.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">validationResults</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">ValidationResult</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">moon</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Moon</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Europa&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">DiscoveredBy</span> <span class="p">=</span> <span class="s">&#34;Galileo Galilei&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">DiscoveryYear</span> <span class="p">=</span> <span class="m">2500</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">AverageOrbitDistance</span> <span class="p">=</span> <span class="m">417002</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">OrbitEccentricity</span> <span class="p">=</span> <span class="p">-</span><span class="m">0.222</span><span class="p">,</span>  <span class="c1">// it&#39;s actually 0.0094 but validators gotta validate</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Validator</span><span class="p">.</span><span class="n">TryValidateObject</span><span class="p">(</span><span class="n">moon</span><span class="p">,</span> <span class="k">new</span> <span class="n">ValidationContext</span><span class="p">(</span><span class="n">moon</span><span class="p">),</span> <span class="n">validationResults</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Results of {nameof(ValidationAttributeExample)}:\r\n&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">vr</span> <span class="k">in</span> <span class="n">validationResults</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">vr</span><span class="p">.</span><span class="n">ErrorMessage</span><span class="p">);</span></span></span></code></pre></div></div>
<p>The output correctly reports that DiscoveryYear and OrbitEccentricity are invalid, because the former is past the current year (2023), and the latter is a negative value.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-are-generic-attributes/results-of-validationattributionexample.png"
    width="501"
      height="138"></figure>

<h2 class="relative group">A generic attribute that keeps things DRYer
    <div id="a-generic-attribute-that-keeps-things-dryer" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#a-generic-attribute-that-keeps-things-dryer" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There&rsquo;s definitely an opportunity here to DRY up some code, using the new generic attribute feature. We&rsquo;ll start with the same class as the previous example, with one significant change - the validation attribute on the 3 numeric fields is the same now.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Moon</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">DiscoveredBy</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [NumberValidation&lt;int&gt;(MaxValue = 2023)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">DiscoveryYear</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [NumberValidation&lt;int&gt;(MinValue = 0)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">AverageOrbitDistance</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [NumberValidation&lt;double&gt;(MinValue = 0.0)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">double</span> <span class="n">OrbitEccentricity</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The &ldquo;integer&rdquo; and &ldquo;double&rdquo; validators can be combined into a single validator that accepts either type. There&rsquo;s some caveats here that I&rsquo;ll point out, and you can leave a comment below if you think I&rsquo;m doing something too crazy here.. lol.</p>
<ul>
<li>This validator only makes sense with a numerical input, so I restricted it to types that implements the <a href="https://learn.microsoft.com/en-us/dotnet/api/system.numerics.inumber-1"  target="_blank" rel="noreferrer"><code>INumber&lt;T&gt;</code></a> interface, also introduced in C#11.</li>
<li>I wanted to set a default value for <code>MinValue</code> and <code>MaxValue</code>, but I can&rsquo;t access those directly since I&rsquo;m using the generic <code>T</code> type, so reflection to the rescue.</li>
</ul>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">NumberValidation</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;</span> <span class="p">:</span> <span class="n">ValidationAttribute</span> <span class="k">where</span> <span class="n">T</span> <span class="p">:</span> <span class="n">INumber</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">T</span> <span class="n">MinValue</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="p">(</span><span class="n">T</span><span class="p">)</span><span class="k">typeof</span><span class="p">(</span><span class="n">T</span><span class="p">).</span><span class="n">GetField</span><span class="p">(</span><span class="s">&#34;MinValue&#34;</span><span class="p">).</span><span class="n">GetValue</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">T</span> <span class="n">MaxValue</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="p">(</span><span class="n">T</span><span class="p">)</span><span class="k">typeof</span><span class="p">(</span><span class="n">T</span><span class="p">).</span><span class="n">GetField</span><span class="p">(</span><span class="s">&#34;MaxValue&#34;</span><span class="p">).</span><span class="n">GetValue</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">protected</span> <span class="kd">override</span> <span class="n">ValidationResult</span><span class="p">?</span> <span class="n">IsValid</span><span class="p">(</span><span class="kt">object?</span> <span class="k">value</span><span class="p">,</span> <span class="n">ValidationContext</span> <span class="n">validationContext</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">num</span> <span class="p">=</span> <span class="p">(</span><span class="n">T</span><span class="p">?)</span><span class="k">value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">num</span> <span class="p">!=</span> <span class="kc">null</span> <span class="p">&amp;&amp;</span> <span class="n">num</span> <span class="p">&gt;=</span> <span class="n">MinValue</span> <span class="p">&amp;&amp;</span> <span class="n">num</span> <span class="p">&lt;=</span> <span class="n">MaxValue</span> <span class="p">?</span> <span class="n">ValidationResult</span><span class="p">.</span><span class="n">Success</span> <span class="p">:</span> <span class="k">new</span> <span class="n">ValidationResult</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The output is the same as before, detecting that 2 of the 3 properties have values that are outside the acceptable range.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-are-generic-attributes/results-of-genericattributeexample.png"
    width="501"
      height="138"></figure>
<p>If you find your own interesting use for generic attributes, feel free to share them in the comments below! And if you found this content useful, and want to learn more about a variety of <a href="https://grantwinney.com/tags/csharp/"  target="_blank" rel="noreferrer">C#</a> features, check out my <a href="https://github.com/grantwinney/CSharpDotNetExamples"  target="_blank" rel="noreferrer">CSharpDotNetExamples repo</a>, where you&rsquo;ll find links to plenty more blog posts and practical examples.</p>
]]></content:encoded><media:content url="https://grantwinney.com/what-are-generic-attributes/feature.webp" medium="image" type="image/webp"/></item><item><title>Simple ways to notify a user without a MessageBox in WinForms</title><link>https://grantwinney.com/other-ways-to-notify-user-besides-messagebox/</link><pubDate>Wed, 16 Aug 2023 01:35:54 +0000</pubDate><guid>https://grantwinney.com/other-ways-to-notify-user-besides-messagebox/</guid><description>When sending notifications in a WinForms app, a MessageBox is the only way to go&amp;hellip; or is it? Let&amp;rsquo;s get creative and see what else we might do.</description><content:encoded><![CDATA[<p>I think all of us would agree that when we&rsquo;re notified about something, it&rsquo;s best to have it jammed in front of our faces, interrupting whatever else we&rsquo;re doing until we deal with it. The latest issue, whatever it is, is the most pressing and should demand our immediate attention. This is the way. 😑</p>
<p>Unfortunately, this really is the way for most WinForms apps I&rsquo;ve seen. Whatever a user is doing at the moment, if they run into an issue, the defacto action is to just throw a MessageBox in front of them. Since most WinForms apps tend to be single-threaded, with only one thing being done at a time, most messages that pop up <em>are</em> related to whatever the user is doing at the moment. Maybe that&rsquo;ll change if more devs adopt the <a href="https://grantwinney.com/using-async-await-and-task-to-keep-the-winforms-ui-more-responsive/"  target="_blank" rel="noreferrer">async/await pattern</a>, but that&rsquo;ll take a long time.</p>
<p>A <code>MessageBox</code> isn&rsquo;t the only way to notify a user that something has happened or needs attention though, so let&rsquo;s take a look at a few alternatives!</p>
<blockquote><p>The code in this post is available on <a href="https://github.com/grantwinney/Surviving-WinForms/tree/master/Presentation/Native/AlternativesToMessageBox"  target="_blank" rel="noreferrer">GitHub</a>, for you to use, expand upon, or just follow along while you read&hellip; and hopefully discover something new!</p>
</blockquote><p>Before continuing, keep in mind that everything below should be taken with a grain of salt. I&rsquo;m not selling anything here as the best idea for an app (ymmv and all that), but since most of the projects I&rsquo;ve ever worked on included requirements for new messages to popup in front of the user, I thought it&rsquo;d be fun to look at what <em>else</em> we could do.</p>

<h2 class="relative group">StatusStrip
    <div id="statusstrip" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#statusstrip" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I tend to forget about the <a href="https://learn.microsoft.com/en-us/dotnet/desktop/winforms/controls/statusstrip-control-overview"  target="_blank" rel="noreferrer">StatusStrip</a> control, since it&rsquo;s generally only used in <a href="https://learn.microsoft.com/en-us/dotnet/desktop/winforms/advanced/multiple-document-interface-mdi-applications"  target="_blank" rel="noreferrer">MDI forms</a>, which are present in most enterprisey type WinForms apps, but only on a single Form that&rsquo;s marked as MDI. There&rsquo;s no reason you can&rsquo;t add one in other places and positions though, as needed.</p>
<p>For this example, I used a <a href="https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.queue-1"  target="_blank" rel="noreferrer"><code>Queue&lt;T&gt;</code></a> to queue up some messages to show the user. Since the WinForms <a href="https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.timer"  target="_blank" rel="noreferrer">Timer</a> operates on the same thread as the UI, it&rsquo;s safe to use the <code>Queue&lt;T&gt;</code> here.. otherwise you&rsquo;d want a <code>ConcurrentQueue&lt;T&gt;</code> (which I use in a later example). The timer executes every 100 ms (the default), checking the queue for pending messages and displaying each for 3 seconds, one after another.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">readonly</span> <span class="n">Queue</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="n">pendingMessages</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Queue</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnUseStatusStrip_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">pendingMessages</span><span class="p">.</span><span class="n">Enqueue</span><span class="p">(</span><span class="s">$&#34;{DateTime.Now:h:mm:ss.fff tt}: {MSG_TEXT}&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">async</span> <span class="k">void</span> <span class="n">timer1_Tick</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">timer1</span><span class="p">.</span><span class="n">Stop</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="n">pendingMessages</span><span class="p">.</span><span class="n">Count</span> <span class="p">&gt;</span> <span class="m">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">toolStripStatusLabelMessage</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="n">pendingMessages</span><span class="p">.</span><span class="n">Dequeue</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="k">await</span> <span class="n">Task</span><span class="p">.</span><span class="n">Delay</span><span class="p">(</span><span class="m">3000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">toolStripStatusLabelMessage</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">timer1</span><span class="p">.</span><span class="n">Start</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The result is a simple message, out of the way yet still visible from anywhere, easily-readable yet not popping up in the user&rsquo;s face and stopping them in their tracks until they acknowledge it.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/other-ways-to-notify-user-besides-messagebox/image-4.png"
    width="809"
      height="156"></figure>

<h2 class="relative group">FlowLayoutPanel
    <div id="flowlayoutpanel" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#flowlayoutpanel" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The <a href="https://learn.microsoft.com/en-us/dotnet/desktop/winforms/controls/flowlayoutpanel-control-overview"  target="_blank" rel="noreferrer">FlowLayoutPanel</a> is a control that (I feel) doesn&rsquo;t get much love.. then again, it&rsquo;s WinForms so love is tough to come by, lol. I&rsquo;m not sure I&rsquo;ve ever actually used it at any place I worked, but I do tend to use it in examples like the one <a href="https://grantwinney.com/call-an-async-method-from-a-synchronous-one/"  target="_blank" rel="noreferrer">in this post</a> because it makes placement of multiple controls much faster and easier.</p>
<p>This one adds messages as a new <code>Label</code> to the <code>FlowLayoutPanel</code>, where the <code>FlowDirection = TopDown</code>. After 3 seconds, they self-destruct. 💣</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">async</span> <span class="k">void</span> <span class="n">btnUseFlowLayoutPanel_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="n">AddMessageToPanel</span><span class="p">(</span><span class="s">$&#34;{DateTime.Now:h:mm:ss.fff tt}: {MSG_TEXT}&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">async</span> <span class="n">Task</span> <span class="n">AddMessageToPanel</span><span class="p">(</span><span class="kt">string</span> <span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">l</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Label</span> <span class="p">{</span> <span class="n">Text</span> <span class="p">=</span> <span class="n">message</span><span class="p">,</span> <span class="n">AutoSize</span> <span class="p">=</span> <span class="kc">true</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="n">flowLayoutPanelMessages</span><span class="p">.</span><span class="n">Controls</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="n">l</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="n">Task</span><span class="p">.</span><span class="n">Delay</span><span class="p">(</span><span class="m">3000</span><span class="p">);</span>  <span class="c1">// give user a few seconds to read it</span>
</span></span><span class="line"><span class="cl">    <span class="n">l</span><span class="p">.</span><span class="n">Dispose</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The result is a stack of messages in labels, aligned for us by the <code>FlowLayoutPanel</code>. No calculations of heights and positions to align everything, or using a single TextBox with <code>Multiline = True</code>.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/other-ways-to-notify-user-besides-messagebox/image-5.png"
    width="428"
      height="147"></figure>

<h2 class="relative group">NotifyIcon / Windows Notification Area
    <div id="notifyicon--windows-notification-area" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#notifyicon--windows-notification-area" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Another good way to let the user know about something, and offload some of your own work at the same time, is to use a <a href="https://learn.microsoft.com/en-us/dotnet/desktop/winforms/controls/notifyicon-component-overview-windows-forms"  target="_blank" rel="noreferrer">NotifyIcon</a> and display notifications using the <a href="https://learn.microsoft.com/en-us/windows/win32/shell/notification-area"  target="_blank" rel="noreferrer">Windows notification area</a>. Just drop a <code>NotifyIcon</code> on the Form and make a single line call (the timeout doesn&rsquo;t matter in most circumstances, since it&rsquo;s ignored on every version of Windows in the last 15 years).</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnUseNotifyIcon_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">notifyIcon1</span><span class="p">.</span><span class="n">ShowBalloonTip</span><span class="p">(</span><span class="m">2000</span><span class="p">,</span> <span class="n">MSG_CAPTION</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s">$&#34;{MSG_TEXT} ({DateTime.Now:h:mm:ss tt})&#34;</span><span class="p">,</span> <span class="n">ToolTipIcon</span><span class="p">.</span><span class="n">Warning</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>While this is convenient, it shouldn&rsquo;t be used for anything really vital because you have so little control over it. The user (depending on how much access they have on the system) can adjust notifications and even disable them altogether for individual (or all) apps.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/other-ways-to-notify-user-besides-messagebox/image-7.png"
    width="455"
      height="349"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/other-ways-to-notify-user-besides-messagebox/image-8.png"
    width="356"
      height="222"></figure>

<h2 class="relative group">Use a separate Form
    <div id="use-a-separate-form" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#use-a-separate-form" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>It&rsquo;s even possible to use a separate Form to display your notifications.. sort of like your own notification area. The way I&rsquo;ve done it in my example, both Forms are running on the main UI thread, which may or may not be fine depending on the circumstance.</p>
<p>The Form that displays the messages has a static <code>ConcurrentQueue&lt;T&gt;</code> in it, <a href="https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentqueue-1"  target="_blank" rel="noreferrer">which is threadsafe</a>. It&rsquo;s not really necessary the way I&rsquo;m using it here, but it&rsquo;s worth knowing it&rsquo;s available, especially if you&rsquo;re pushing messages to the queue from different threads.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="k">readonly</span> <span class="n">ConcurrentQueue</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="n">Messages</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ConcurrentQueue</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="n">frmMessages</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">InitializeComponent</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">timer1_Tick</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">timer1</span><span class="p">.</span><span class="n">Stop</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="n">Messages</span><span class="p">.</span><span class="n">Any</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">Messages</span><span class="p">.</span><span class="n">TryDequeue</span><span class="p">(</span><span class="k">out</span> <span class="kt">var</span> <span class="n">message</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">l</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Label</span> <span class="p">{</span> <span class="n">Text</span> <span class="p">=</span> <span class="s">$&#34;{message} (click to dismiss)&#34;</span><span class="p">,</span> <span class="n">AutoSize</span> <span class="p">=</span> <span class="kc">true</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">            <span class="n">l</span><span class="p">.</span><span class="n">Click</span> <span class="p">+=</span> <span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">evt</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> <span class="n">l</span><span class="p">.</span><span class="n">Dispose</span><span class="p">();</span> <span class="k">if</span> <span class="p">(</span><span class="n">flowLayoutPanel1</span><span class="p">.</span><span class="n">Controls</span><span class="p">.</span><span class="n">Count</span> <span class="p">==</span> <span class="m">0</span><span class="p">)</span> <span class="n">Hide</span><span class="p">();</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">            <span class="n">flowLayoutPanel1</span><span class="p">.</span><span class="n">Controls</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="n">l</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">Show</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">timer1</span><span class="p">.</span><span class="n">Start</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>To use it from the other Form, just push a message to the concurrent queue. I don&rsquo;t like how I&rsquo;m calling it here, directly accessing it on the other Form, but this is a simple example. I&rsquo;ll leave it to anyone who finds it intriguing to come up with a better way to architect their app.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnUseSeparateForm_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">msgForm</span> <span class="p">==</span> <span class="kc">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">msgForm</span> <span class="p">=</span> <span class="k">new</span> <span class="n">frmMessages</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">msgForm</span><span class="p">.</span><span class="n">Show</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">frmMessages</span><span class="p">.</span><span class="n">Messages</span><span class="p">.</span><span class="n">Enqueue</span><span class="p">(</span><span class="s">$&#34;{DateTime.Now:h:mm:ss.fff tt}: {MSG_TEXT}&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The result is a small popup Form with some messages in them. Click a message to remove it. Remove all messages and the second Form hides itself until there&rsquo;s something else to display.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/other-ways-to-notify-user-besides-messagebox/image-9.png"
    width="451"
      height="528"></figure>

<h2 class="relative group">Everything all at once&hellip;
    <div id="everything-all-at-once" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#everything-all-at-once" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>And here&rsquo;s everything in one go.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/other-ways-to-notify-user-besides-messagebox/allthemessages.gif"
    width="936"
      height="472"></figure>
<p>Like I said earlier, these are just little examples to be taken with a grain of salt. The point is, a <code>MessageBox</code> is not the <em>only</em> way to send notifications to users. Depending on your needs, maybe something here will work or at least get the creative juices flowing.. and I&rsquo;m sure there are <em>plenty</em> of more robust ways out there for the intrepid dev. Good luck!</p>
]]></content:encoded><media:content url="https://grantwinney.com/other-ways-to-notify-user-besides-messagebox/feature.webp" medium="image" type="image/webp"/></item><item><title>How to call an async method from a synchronous one, without deadlocking</title><link>https://grantwinney.com/call-an-async-method-from-a-synchronous-one/</link><pubDate>Fri, 11 Aug 2023 03:59:55 +0000</pubDate><guid>https://grantwinney.com/call-an-async-method-from-a-synchronous-one/</guid><description>Writing async code whenever possible is great, but how do we do it when we&amp;rsquo;re stuck with legacy (and very synchronous) code?</description><content:encoded><![CDATA[<p>The async/await model introduced with <a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-50"  target="_blank" rel="noreferrer">C# 5.0</a> (over a decade ago) is probably one of the best things added to the language, right up there with LINQ (introduced a few years earlier in <a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-30"  target="_blank" rel="noreferrer">C# 3.0</a>). In the last few years, as I&rsquo;ve read up more on async/await and understand it better, I try to implement it where I reasonably can. In fresh code, like a new API or a side project, that&rsquo;s relatively easy. Not so much in older code.</p>
<p>As great as async is, it can be tricky in a legacy app where it&rsquo;s just not feasible to update everything at once (despite that being popular advice). When you&rsquo;re dealing with tens of thousands (or even millions) of lines of code, organized in a dozen layers representing multiple architectures, written over a couple decades by dozens of developers, wide sweeping changes are usually a recipe for disaster. And even for those brave souls that laugh in the face of such disaster, few companies are going to happily let someone spend days on a task that should&rsquo;ve taken a few hours, just because they decided to make the code &ldquo;better&rdquo;. That&rsquo;s a tough sell any day of the week.</p>
<p>That being said, what I&rsquo;m going to show you is an anti-pattern of sorts, and it even has a catchy name - &ldquo;sync over async&rdquo; - which is explained (and discouraged) by the likes of Stephen Toub, David Fowler, and Stephen Cleary (all very reliable sources in the world of C#/.NET) . I&rsquo;ll link to their articles at the end.</p>
<p><em>You should avoid this if you can. But what if you can&rsquo;t?</em></p>
<blockquote><p>The code in this post is available on <a href="https://github.com/grantwinney/SurvivingWinForms/tree/master/Threading/CallingAsyncMethodFromSynchronousCode"  target="_blank" rel="noreferrer">GitHub</a>, for you to use, expand upon, or just follow along while you read&hellip; and hopefully discover something new!</p>
</blockquote><p>Let&rsquo;s look at a few ways to do what we (sometimes) gotta do, starting with what we should <em>never</em> do, and finishing up with what we really ought to do.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/call-an-async-method-from-a-synchronous-one/image-11.png"
    width="660"
      height="249"></figure>
<p>We&rsquo;ll also assume there&rsquo;s an async method doing some really important stuff.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="n">ImportantStuffAsync</span><span class="p">(</span><span class="n">IProgress</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;</span> <span class="n">progress</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="n">Task</span><span class="p">.</span><span class="n">Delay</span><span class="p">(</span><span class="m">1000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">progress</span><span class="p">.</span><span class="n">Report</span><span class="p">(</span><span class="m">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="n">Task</span><span class="p">.</span><span class="n">Delay</span><span class="p">(</span><span class="m">1000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">progress</span><span class="p">.</span><span class="n">Report</span><span class="p">(</span><span class="m">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">await</span> <span class="n">Task</span><span class="p">.</span><span class="n">Delay</span><span class="p">(</span><span class="m">1000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">progress</span><span class="p">.</span><span class="n">Report</span><span class="p">(</span><span class="m">3</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s">$&#34;Done! ({DateTime.Now:G})&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h2 class="relative group">How to deadlock an app (bad)
    <div id="how-to-deadlock-an-app-bad" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#how-to-deadlock-an-app-bad" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The most obvious way to call async code from sync code is also the most obviously <em>wrong</em> way. Seeing an async method and, knowing you want the result, one might be tempted to just call the method directly and then access <code>.Result</code>.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// Example 1 - Let&#39;s cause a deadlock</span>
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnExample1_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// As soon as we call .Result or .Wait() here, all is lost...</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">result</span> <span class="p">=</span> <span class="n">ImportantStuffAsync</span><span class="p">(</span><span class="k">new</span> <span class="n">Progress</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;()).</span><span class="n">Result</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// ... the UI thread is deadlocked, so just restart the app. :(</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Example 1 - Deadlock</p>
<p>As far as I understand it:</p>
<ul>
<li>A call to <code>.Result</code> or <code>.Wait()</code> blocks the current (UI) thread while it waits for the Task to complete.</li>
<li>When the Task is finished, it attempts to take control of the UI thread to wrap up its work (like returning the value).. but it can&rsquo;t.</li>
<li>The Task code can&rsquo;t access the UI thread until the call to <code>.Result</code> or <code>.Wait()</code> completes, but the call to <code>.Result</code> or <code>.Wait()</code> will never complete until it gets the response from the Task. Deadlock.</li>
</ul>
<p>Possibly useless analogy time.. bear with me. This makes me think of an elevator, where the main UI thread is the elevator shaft, and the elevator itself is the single piece of work that can be handled at any time. If it appears many things are happening at once, it&rsquo;s because the elevator flies up and down the shaft at breakneck speed.</p>
<p>Occasionally, someone sticks their foot in the doorway (running some numbers, jotting some notes down, grabbing a coffee), preventing anyone else from using the elevator. That&rsquo;s the frozen UI. Then it finishes, the UI unfreezes, and the elevator moves again.</p>
<p>In this case though, the main UI thread starts the Task, and then calls the elevator to its floor and sticks its foot in the door, waiting for a response. It will not take its foot out until the Task responds. But when the Task finishes, it calls the elevator to load the result in. It will not be completely finished until the elevator arrives and it can do that. There&rsquo;s a stalemate. They&rsquo;re both trying to use the same resource, and neither will give up until the other is finished.</p>

<h2 class="relative group">How to avoid deadlocks (better)
    <div id="how-to-avoid-deadlocks-better" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#how-to-avoid-deadlocks-better" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The quickest fix for the above problem is to run the async code in its own Task, which allows the code in the async method to run in a separate thread from the UI, avoiding the deadlocking issue. There&rsquo;s a few ways you might approach this.</p>
<p>One option is to just run it without waiting for the Task to even complete. The UI stays responsive, but you won&rsquo;t get the result, if any. And if you hoped to lock down any part of the UI while the Task was running, that won&rsquo;t work either.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// Example 2 - Call async method from a sync method, without bothering to wait</span>
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnExample2_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Lock parts of the UI that should be inaccessible while the task runs</span>
</span></span><span class="line"><span class="cl">    <span class="n">pnlButtons</span><span class="p">.</span><span class="n">Enabled</span> <span class="p">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Task</span><span class="p">.</span><span class="n">Run</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="n">ImportantStuffAsync</span><span class="p">(</span><span class="n">progress</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// OOPS! The panel will be re-enabled before the Task completes</span>
</span></span><span class="line"><span class="cl">    <span class="n">pnlButtons</span><span class="p">.</span><span class="n">Enabled</span> <span class="p">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Example 2 - Run in a Task, without waiting for it to complete</p>
<p>You could also wait for the Task to complete and get the result, if any. The downside here is that the UI freezes while the UI thread waits for the Task to complete. The upside is that it eventually unfreezes, instead of deadlocking.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// Example 3 - Call async method from a sync method, but wait until it completes (freezes UI)</span>
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnExample3_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">pnlButtons</span><span class="p">.</span><span class="n">Enabled</span> <span class="p">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Task</span><span class="p">.</span><span class="n">Run</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="n">ImportantStuffAsync</span><span class="p">(</span><span class="n">progress</span><span class="p">)).</span><span class="n">Wait</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">pnlButtons</span><span class="p">.</span><span class="n">Enabled</span> <span class="p">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Example 3 - Run in a Task, waiting for it to complete</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// Example 4 - Call async method from a sync method, but wait for return value</span>
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnExample4_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">pnlButtons</span><span class="p">.</span><span class="n">Enabled</span> <span class="p">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">result</span> <span class="p">=</span> <span class="n">Task</span><span class="p">.</span><span class="n">Run</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="n">ImportantStuffAsync</span><span class="p">(</span><span class="n">progress</span><span class="p">)).</span><span class="n">Result</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">lblReturnValue</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="n">result</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">pnlButtons</span><span class="p">.</span><span class="n">Enabled</span> <span class="p">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Example 4 - Run in a task, waiting for the result to be returned</p>

<h2 class="relative group">How to async all the things (best)
    <div id="how-to-async-all-the-things-best" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#how-to-async-all-the-things-best" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The best option though, when you can, is to mark everything async on up the chain. Since event methods can be marked async in WinForms, it means a really small change in my really small example. Just &ldquo;await&rdquo; the async method and then mark the click event as async too, and watch the magic happen.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// Example 5 - Call async method from another async method.. the right way</span>
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">async</span> <span class="k">void</span> <span class="n">btnExample5_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">pnlButtons</span><span class="p">.</span><span class="n">Enabled</span> <span class="p">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">result</span> <span class="p">=</span> <span class="k">await</span> <span class="n">ImportantStuffAsync</span><span class="p">(</span><span class="n">progress</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">lblReturnValue</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="n">result</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">pnlButtons</span><span class="p">.</span><span class="n">Enabled</span> <span class="p">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h2 class="relative group">Learning More
    <div id="learning-more" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#learning-more" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you&rsquo;re interested in learning more, check out pretty much anything on the topic of async from Stephen Cleary. He&rsquo;s been writing about it since it came out, and you&rsquo;ll see his answers all over the SO forums, and some books, and on his blog. He&rsquo;s everywhere all at once, in true async fashion.</p>
<ul>
<li><a href="https://blog.stephencleary.com/2012/02/async-and-await.html"  target="_blank" rel="noreferrer">Async and Await</a></li>
</ul>
<p>Here&rsquo;s a comprehensive post I found from Stephen Toub, who&rsquo;s worked at Microsoft since C# became a thing. Set aside a few hours (days?) to take it in though.. it&rsquo;s a packed post to say the least. And a couple more I came across.</p>
<ul>
<li><a href="https://devblogs.microsoft.com/dotnet/how-async-await-really-works"  target="_blank" rel="noreferrer">How Async/Await Really Works in C#</a></li>
<li><a href="https://devblogs.microsoft.com/pfxteam/asyncawait-faq"  target="_blank" rel="noreferrer">Async/Await FAQ</a></li>
<li><a href="https://devblogs.microsoft.com/pfxteam/await-and-ui-and-deadlocks-oh-my"  target="_blank" rel="noreferrer">Await, and UI, and deadlocks! Oh my!</a></li>
</ul>
<p>And from David Fowler, another longtime Microsoft employee who works on .NET and ASP.NET Core, a <em>looong</em> list of do&rsquo;s and don&rsquo;ts type of advice when it comes to async.</p>
<ul>
<li><a href="https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/master/AsyncGuidance.md"  target="_blank" rel="noreferrer">ASP.NET Core Diagnostic Scenarios - Asynchronous Programming</a></li>
</ul>
<p>If you want to read more about multithreading and async code, I wrote a couple other posts about it too, from the perspective of using them in WinForms.</p>
<ul>
<li><a href="https://grantwinney.com/using-async-await-and-task-to-keep-the-winforms-ui-more-responsive/"  target="_blank" rel="noreferrer">Using Async, Await, and Task to keep the WinForms UI responsive</a></li>
<li><a href="https://grantwinney.com/convert-backgroundworker-to-task-with-taskcompletionsource/"  target="_blank" rel="noreferrer">Converting a BackgroundWorker to a Task with TaskCompletionSource</a></li>
</ul>
]]></content:encoded><media:content url="https://grantwinney.com/call-an-async-method-from-a-synchronous-one/feature.webp" medium="image" type="image/webp"/></item><item><title>What's the difference between singleton, scoped, and transient?</title><link>https://grantwinney.com/difference-between-singleton-scoped-transient/</link><pubDate>Fri, 28 Jul 2023 21:41:00 +0000</pubDate><guid>https://grantwinney.com/difference-between-singleton-scoped-transient/</guid><description>It&amp;rsquo;s trivial to register a dependency in a .NET API, but it&amp;rsquo;s important to clarify a few terms that drastically change a dependency&amp;rsquo;s lifetime.</description><content:encoded><![CDATA[<p>I saw an issue with a .NET 6 API recently, where dependency injection (DI) was in full use, but instead of getting a new instance of a dependency every time one was requested (as expected), the <em>same</em> instance kept being returned.</p>
<p>The problem didn&rsquo;t actually present itself that nicely (they never do), so it took quite awhile to track down. In the end though, it was obvious (as most solved problems are) that the dependency was registered incorrectly. The fix was a one-line change.</p>
<blockquote><p>The code in this post is available on <a href="https://github.com/grantwinney/CSharpDotNetExamples/tree/master/GeneralConcepts/SingletonVsTransientDI"  target="_blank" rel="noreferrer">GitHub</a>, for you to use, expand upon, or just follow along while you read&hellip; and hopefully discover something new!</p>
</blockquote><p>When we create APIs in .NET, it&rsquo;s pretty easy to register a class with the DI service, as it&rsquo;s supported right out of the box. But there&rsquo;s different ways a service can be registered, so it&rsquo;s important to understand the differences between <a href="https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.servicecollectionserviceextensions.addsingleton?view=dotnet-plat-ext-6.0"  target="_blank" rel="noreferrer">AddSingleton</a>, <a href="https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.servicecollectionserviceextensions.addscoped?view=dotnet-plat-ext-6.0"  target="_blank" rel="noreferrer">AddScoped</a>, and <a href="https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.servicecollectionserviceextensions.addtransient?view=dotnet-plat-ext-6.0"  target="_blank" rel="noreferrer">AddTransient</a>.</p>
<p>To <em>(really briefly)</em> summarize them:</p>
<ul>
<li>Singleton - One instance of a resource, reused anytime it&rsquo;s requested.</li>
<li>Scoped - One instance of a resource, but only for the current request. New request (i.e. hit an API endpoint again) = new instance</li>
<li>Transient - A different instance of a resource, everytime it&rsquo;s requested.</li>
</ul>
<p>It&rsquo;s usually easier to see things in action though, which as it turns out is fairly easy to do. Here&rsquo;s a class with a single property that provides a GUID value, which is generated when the class is instantiated, and some interfaces to use in the next step:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">interface</span> <span class="nc">IIDSingleton</span> <span class="p">:</span> <span class="n">IID</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">interface</span> <span class="nc">IIDScoped</span> <span class="p">:</span> <span class="n">IID</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">interface</span> <span class="nc">IIDTransient</span> <span class="p">:</span> <span class="n">IID</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">interface</span> <span class="nc">IID</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Guid</span> <span class="n">Value</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">ID</span> <span class="p">:</span> <span class="n">IIDSingleton</span><span class="p">,</span> <span class="n">IIDScoped</span><span class="p">,</span> <span class="n">IIDTransient</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Guid</span> <span class="n">Value</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="kd">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">Guid</span><span class="p">.</span><span class="n">NewGuid</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>And here&rsquo;s a minimal API with a single endpoint that defines some dependencies to inject and how those dependencies should be resolved. When someone requests an <code>IIDSingleton</code> for example, it should resolve to a single instance of the <code>ID</code> class&hellip; always just that single instance, no matter what. When someone requests an <code>IIDTransient</code> though, it should <em>always</em> be a new instance.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">SingletonVsTransientDI</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">WebApplication</span><span class="p">.</span><span class="n">CreateBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddSingleton</span><span class="p">&lt;</span><span class="n">IIDSingleton</span><span class="p">&gt;(</span><span class="k">new</span> <span class="n">ID</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddScoped</span><span class="p">&lt;</span><span class="n">IIDScoped</span><span class="p">,</span> <span class="n">ID</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl"><span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddTransient</span><span class="p">&lt;</span><span class="n">IIDTransient</span><span class="p">,</span> <span class="n">ID</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">app</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="n">Build</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">app</span><span class="p">.</span><span class="n">MapGet</span><span class="p">(</span><span class="s">&#34;/now&#34;</span><span class="p">,</span> <span class="p">(</span><span class="n">IIDSingleton</span> <span class="n">idSingleton</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="n">IIDScoped</span> <span class="n">idScoped1</span><span class="p">,</span> <span class="n">IIDScoped</span> <span class="n">idScoped2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="n">IIDTransient</span> <span class="n">idTransient1</span><span class="p">,</span> <span class="n">IIDTransient</span> <span class="n">idTransient2</span><span class="p">)</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s">$&#34;Singleton instance: {idSingleton.Value}\r\n\r\n&#34;</span> <span class="p">+</span>
</span></span><span class="line"><span class="cl">        <span class="s">$&#34;Scoped instance 1: {idScoped1.Value}\r\nScoped instance 2: {idScoped2.Value}\r\n\r\n&#34;</span> <span class="p">+</span>
</span></span><span class="line"><span class="cl">        <span class="s">$&#34;Transient instance 1: {idTransient1.Value}\r\nTransient instance 2: {idTransient2.Value}&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">app</span><span class="p">.</span><span class="n">Run</span><span class="p">();</span></span></span></code></pre></div></div>
<p>The way I&rsquo;m requesting two each of the <code>IIDScoped</code> and <code>IIDTransient</code> dependencies in the endpoint above is silly, but it&rsquo;s to keep the example simple. In reality, we&rsquo;d usually make requests like these in completely different areas of the code, and whether or not all those independent requests provided us with the same instance of the dependency or a new one would depend on how things were originally registered.</p>
<p>Here it is in action. When I press &ldquo;refresh&rdquo; to make a new request to the <code>/now</code> endpoint, keep an eye on three things - the singleton instance <em>never</em> changes, the scoped instance doesn&rsquo;t change until a new request is made (aka, I hit refresh), and the transient instance <em>always</em> changes.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="ditest.webp"
    ></figure>
<p>Oh, and I didn&rsquo;t want to delve any deeper into DI and IOC in this post, but if you&rsquo;re interested in learning more, here&rsquo;s a few good resources:</p>
<ul>
<li><a href="https://www.techtarget.com/searchapparchitecture/definition/dependency-injection"  target="_blank" rel="noreferrer">Using DI with OOP</a></li>
<li><a href="https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-usage"  target="_blank" rel="noreferrer">Using DI specifically with .NET</a></li>
<li><a href="https://pmichaels.net/2021/11/28/dependency-injection-in-minimal-apis-in-net-6/"  target="_blank" rel="noreferrer">Using DI in minimal APIs</a> (as I&rsquo;m doing in the example above)</li>
</ul>
]]></content:encoded><media:content url="https://grantwinney.com/difference-between-singleton-scoped-transient/feature.webp" medium="image" type="image/webp"/></item><item><title>How can I generate a new GUID?</title><link>https://grantwinney.com/how-can-i-generate-a-new-guid/</link><pubDate>Thu, 27 Jul 2023 22:12:21 +0000</pubDate><guid>https://grantwinney.com/how-can-i-generate-a-new-guid/</guid><description>GUIDs are heavily used in the world of development, so let&amp;rsquo;s look at a few quick and easy ways to generate them whenever we need them.</description><content:encoded><![CDATA[<p>GUIDs, or globally unique identifiers, are heavily used in the world of development. And while they certainly are unique (32 hex characters means all 8 billion people on Earth could generate a billion GUIDs every second, and there&rsquo;d still be enough for 1.3 <em>trillion</em> years), in reality no GUID <em>needs</em> to be unique across the world - just your system.</p>
<p>For the uninitiated, GUIDs can be used as identifiers in a database (or across databases) to link records, or as identifiers in other systems (like Windows does in its registry), or as temporary file names, or anywhere else you need guaranteed uniqueness but not human readableness <em>(totally a real word)</em>.</p>
<p>If you&rsquo;re interested in reading more about what goes on under the covers to create them, <a href="https://devblogs.microsoft.com/oldnewthing/20080627-00/?p=21823"  target="_blank" rel="noreferrer">Raymond Chen&rsquo;s article</a> should pique your interest.. or make your brain hurt. Personally, I&rsquo;m not so interested in how they&rsquo;re calculated, but how I can easily generate one (or several) when I need it.</p>
<p>Let&rsquo;s look at some of the different tools at our disposal.</p>

<h2 class="relative group">Visual Studio
    <div id="visual-studio" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#visual-studio" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There&rsquo;s a tiny utility for generating GUIDs that gets installed with every version of Visual Studio, going back to at least VS 2005 (the earliest I have installed). It&rsquo;s accessible in the VS menu but it&rsquo;s also a stand-alone exe, so you can just pin it or create a shortcut to it, and then assign a shortcut key to it if that&rsquo;s what you want.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-can-i-generate-a-new-guid/visual-studio-create-guid-1.png"
    width="484"
      height="338"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-can-i-generate-a-new-guid/visual-studio-create-guid-2.png"
    width="371"
      height="391"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-can-i-generate-a-new-guid/visual-studio-create-guid-shortcut.png"
    width="363"
      height="566"></figure>

<h2 class="relative group">VS Code
    <div id="vs-code" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#vs-code" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>VS Code has been my favorite editor for the last few years. There&rsquo;s <a href="https://marketplace.visualstudio.com/VSCode"  target="_blank" rel="noreferrer">extensions for just about anything</a>, including one for <a href="https://marketplace.visualstudio.com/items?itemName=heaths.vscode-guid"  target="_blank" rel="noreferrer">inserting GUIDs</a>. Just install it and you get two new options, one to insert a single GUID and another to insert multiple GUIDs.</p>
<p>Here&rsquo;s an example, that definitely only took 30 seconds to create and that I certainly didn&rsquo;t have to re-record 5 times because I kept pressing the wrong keys. Notice how the &ldquo;manager_id&rdquo; gets the same value as the manager&rsquo;s &ldquo;id&rdquo;, but then the two employees get different values for their IDs, depending on which command is selected. Convenience!</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-can-i-generate-a-new-guid/vscode_insertguid.gif"
    width="897"
      height="536"></figure>

<h2 class="relative group">PowerShell
    <div id="powershell" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#powershell" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Somewhere on my long list of things to get more familiar with is <a href="https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell"  target="_blank" rel="noreferrer">PowerShell</a>. For anyone out there who uses it regularly (maybe even leaves it open all day), there&rsquo;s a <a href="https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/new-guid"  target="_blank" rel="noreferrer">New-Guid</a> command that, well, does what you&rsquo;d think.</p>
<p>Running the command by itself presents a column header, which seems a little weird to me .. it&rsquo;s not like I&rsquo;m going to think that value is something <em>else.</em> Using the value in a string (whether it&rsquo;s saved in a variable first or just called inline), it outputs only the GUID, as I&rsquo;d expect it to.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-can-i-generate-a-new-guid/powershell-new-guid.png"
    width="747"
      height="301"></figure>

<h2 class="relative group">APIs
    <div id="apis" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#apis" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There&rsquo;s some sites out there that will provide you with a GUID, like the <a href="https://www.uuidgenerator.net/guid"  target="_blank" rel="noreferrer">Online GUID Generator Tool</a>, but what about making an API call? I wrote about the PasswordRandom.com API (by <a href="http://koshovyi.com/"  target="_blank" rel="noreferrer">Koshovyi Dmytro</a>) awhile back, but its usage is incredibly simple.</p>
<p><a href="https://grantwinney.com/passwordrandom-api/"  target="_blank" rel="noreferrer">Generate random passwords, numbers and GUIDs with the PasswordRandom API</a></p>
<p>You don&rsquo;t need to authenticate, so it&rsquo;s a simple one line call. Use it from an app, or just bookmark it if you need quick access to a few GUIDs from time to time. It can produce <code>json</code> and <code>xml</code> formats too.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">https://www.passwordrandom.com/query?command=guid&amp;count=3&amp;format=plain
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">12e99949-1ffd-4892-95ca-697f9ebacaae
</span></span><span class="line"><span class="cl">fe6d2f03-b4bb-4df8-9247-5ece632a9fc1
</span></span><span class="line"><span class="cl">09650940-a428-4bf0-a7aa-352dc1ae2eec</span></span></code></pre></div></div>

<h2 class="relative group">DuckDuckGo
    <div id="duckduckgo" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#duckduckgo" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The privacy-friendly search engine is well known (among those of us who use it) for its <a href="https://web.archive.org/web/20201104130902/https://dev.to/harshhhdev/fun-duckduckgo-tricks-4c5h"  target="_blank" rel="noreferrer">easter eggs</a> and <a href="https://itsfoss.com/duckduckgo-easter-eggs/"  target="_blank" rel="noreferrer">hidden features</a>, some that stick around and others that eventually disappear. One of those features is generating a GUID.</p>
<p>Just do a search for &ldquo;guid&rdquo; and above the search results you get a new one. It&rsquo;s a little more convenient if you have DDG setup as your default search engine, and then you can just type &ldquo;guid&rdquo; in the address bar.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-can-i-generate-a-new-guid/duckduckgo-guid.png"
    width="774"
      height="165"></figure>
<p>If you have a better way to generate GUIDs on-the-fly, for your app, personal use, or whatever else, feel free to share below. I&rsquo;d love to add a few more tools to my toolbox!</p>
]]></content:encoded><media:content url="https://grantwinney.com/how-can-i-generate-a-new-guid/feature.webp" medium="image" type="image/webp"/></item><item><title>Comparing files in VS Code</title><link>https://grantwinney.com/how-to-compare-files-using-vs-code/</link><pubDate>Wed, 26 Jul 2023 22:15:20 +0000</pubDate><guid>https://grantwinney.com/how-to-compare-files-using-vs-code/</guid><description>VS Code is a great editor with a lot of useful features, like being able to compare two random files for differences. Let&amp;rsquo;s see how.</description><content:encoded><![CDATA[<p>I recently found myself in need of comparing two versions of an XML file for a merge, something that seems to frequently confuse git. Even better, this particular file was generated as a <a href="https://grantwinney.com/minification-vs-obfuscation/"  target="_blank" rel="noreferrer">minified file</a>, which <em>really</em> confuses git. Fortunately, VS Code helps with formatting <em>and</em> comparing files, but let&rsquo;s stick to comparing.</p>

<h2 class="relative group">Files that are saved in the same folder
    <div id="files-that-are-saved-in-the-same-folder" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#files-that-are-saved-in-the-same-folder" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If the files are saved to disk in the same folder, open the folder, either with the &ldquo;Open Folder&rdquo; button on the left (if the &ldquo;Explorer&rdquo; pane is open), or with the &ldquo;Open Folder&rdquo; option in the &ldquo;File&rdquo; menu, or with the &ldquo;ctrl-k, ctrl-o&rdquo; shortcut (in Windows):</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-compare-files-using-vs-code/vscode-open-folder.png"
    width="940"
      height="577"></figure>
<p>Then select both files, right click, and choose &ldquo;Compare Selected&rdquo;:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-compare-files-using-vs-code/vscode-compare-selected.png"
    width="608"
      height="327"></figure>
<p>We get a nice little side-by-side that collapses into an inline comparison if the window&rsquo;s too small:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-compare-files-using-vs-code/vscode-xml-compare.gif"
    width="1154"
      height="433"></figure>

<h2 class="relative group">Files that <em>aren&rsquo;t</em> in the same folder.. or aren&rsquo;t saved at all
    <div id="files-that-aren-in-the-same-folder-or-arent-saved-at-all" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#files-that-aren-in-the-same-folder-or-arent-saved-at-all" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Whether your files are saved to disk or not, the first thing you&rsquo;ll need to do is show all open editors, something that doesn&rsquo;t seem to be displayed by default and is hidden in the &ldquo;three dots&rdquo; menu.</p>
<p>Open the &ldquo;Explorer&rdquo; pane on the left, then press the &ldquo;&hellip;&rdquo; item in the top corner and select &ldquo;Open Editors&rdquo;. Select both files, right click, and choose &ldquo;Compare Selected&rdquo; from the context menu:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-compare-files-using-vs-code/vscode-open-editors.png"
    width="692"
      height="231"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-compare-files-using-vs-code/vscode-compare-selected2.png"
    width="693"
      height="399"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-compare-files-using-vs-code/vscode-xml-compare2.png"
    width="1032"
      height="265"></figure>
<p>This works just as well for files that aren&rsquo;t saved too:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-compare-files-using-vs-code/vscode-compare-selected3.png"
    width="690"
      height="279"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-compare-files-using-vs-code/vscode-compare-files.png"
    width="761"
      height="227"></figure>
]]></content:encoded><media:content url="https://grantwinney.com/how-to-compare-files-using-vs-code/feature.webp" medium="image" type="image/webp"/></item><item><title>How to log messages to multiple targets with NLog</title><link>https://grantwinney.com/how-to-log-messages-to-multiple-targets-with-nlog/</link><pubDate>Sun, 02 Jul 2023 03:09:24 +0000</pubDate><guid>https://grantwinney.com/how-to-log-messages-to-multiple-targets-with-nlog/</guid><description>When it comes to finding a bug in an app, few things beat a good trail of logs. And for writing those logs, few tools beat NLog in simplicity or flexibility. I rarely appreciate just how flexible it is though, so it&amp;rsquo;s worth spending a little time taking a closer look.</description><content:encoded><![CDATA[<p>When it comes to finding a bug in an app, few things beat a good trail of logs. And for writing those logs, few tools beat NLog in simplicity or flexibility. I rarely appreciate just <em>how</em> flexible it is though, so it&rsquo;s worth spending a little time taking a closer look.</p>
<p>Generally when I&rsquo;ve used NLog, I configure it the same ol&rsquo; way.. writing logs to a file. To use NLog&rsquo;s vernacular, I <em>target</em> a file.. but there are a lot of other possible targets too. In fact there&rsquo;s <a href="https://nlog-project.org/config/?tab=targets"  target="_blank" rel="noreferrer">nearly a hundred</a> as I write this.</p>
<p>In this post I&rsquo;ll use three of them in a WinForms app, to target a file, an API, and a MessageBox. Yep, you can actually display certain logs to a user, if you had a reason for wanting to do it.</p>

<h2 class="relative group">Getting Started
    <div id="getting-started" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#getting-started" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I&rsquo;ve written about <a href="https://grantwinney.com/log-errors-in-winforms-with-nlog/"  target="_blank" rel="noreferrer">using NLog for logging in WinForms</a> before, and I think that&rsquo;s still a decent enough tutorial for getting started, so I won&rsquo;t rehash it here. The NLog readme and wiki has a ton of resources as well, like this one for <a href="https://github.com/NLog/NLog/wiki/Tutorial"  target="_blank" rel="noreferrer">getting started on the .NET Framework</a>.</p>
<p>If you&rsquo;ve got everything configured but nothing&rsquo;s happening, double-check that the NLog.config file is set to copy to the output directory. If it&rsquo;s not in the bin folder when the app runs, nothing&rsquo;ll happen. Otherwise, <a href="https://github.com/NLog/NLog/wiki/Logging-troubleshooting"  target="_blank" rel="noreferrer">follow their troubleshooting doc</a>.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-log-messages-to-multiple-targets-with-nlog/copy-the-nlog-file-to-output.png"
    width="349"
      height="424"></figure>

<h2 class="relative group">Configuring Multiple Targets
    <div id="configuring-multiple-targets" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#configuring-multiple-targets" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>As I just mentioned, there&rsquo;s three targets I&rsquo;ll show off:</p>
<ul>
<li><a href="https://github.com/NLog/NLog/wiki/File-target"  target="_blank" rel="noreferrer">File target · NLog</a> (send a message to a file on disk)</li>
<li><a href="https://github.com/NLog/NLog/wiki/MessageBox-target"  target="_blank" rel="noreferrer">MessageBox target · NLog</a> (popup a message to the user)</li>
<li><a href="https://github.com/DarekDan/NLog.Targets.HTTP/blob/master/README.md"  target="_blank" rel="noreferrer">NLog.Targets.HTTP · DarekDan</a> (send a POST to some end point)</li>
</ul>
<p>The first two are from the NLog team, and the third one is from someone else (Dariusz Danielewski). It&rsquo;s awesome that anyone can <a href="https://github.com/NLog/NLog/wiki/Extending-NLog"  target="_blank" rel="noreferrer">create their own targets</a>, but it&rsquo;s also prudent to give any code from a third-party a cursory review at least. NLog has it listed on their site, so I&rsquo;d hope they at least vetted it out initially, but anything can change over time.</p>
<p>In general, all the targets are good about including a short section that shows how to modify your nlog.config file to get things running, and the above three are no exception. Here&rsquo;s what my config file looks like after adding them.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="cp">&lt;?xml version=&#34;1.0&#34; encoding=&#34;utf-8&#34; ?&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;nlog</span> <span class="na">xmlns=</span><span class="s">&#34;http://www.nlog-project.org/schemas/NLog.xsd&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">xmlns:xsi=</span><span class="s">&#34;http://www.w3.org/2001/XMLSchema-instance&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;extensions&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;add</span> <span class="na">assembly=</span><span class="s">&#34;NLog.Targets.Http&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;/extensions&gt;</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;targets&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;target</span> <span class="na">name=</span><span class="s">&#34;logfile&#34;</span> <span class="na">xsi:type=</span><span class="s">&#34;File&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="na">fileName=</span><span class="s">&#34;file.txt&#34;</span> <span class="na">autoFlush=</span><span class="s">&#34;true&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;target</span> <span class="na">name=</span><span class="s">&#34;logmsg&#34;</span> <span class="na">xsi:type=</span><span class="s">&#34;MessageBox&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="na">caption=</span><span class="s">&#34;${level} Message (${shortdate})&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="na">layout=</span><span class="s">&#34;${message}&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;target</span> <span class="na">name=</span><span class="s">&#34;logapi&#34;</span> <span class="na">xsi:type=</span><span class="s">&#34;HTTP&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="na">URL=</span><span class="s">&#34;http://localhost:5112/log&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="na">ContentType=</span><span class="s">&#34;application/json&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;layout</span> <span class="na">type=</span><span class="s">&#34;JsonLayout&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;attribute</span> <span class="na">name=</span><span class="s">&#34;sourcetype&#34;</span> <span class="na">layout=</span><span class="s">&#34;_json&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;attribute</span> <span class="na">name=</span><span class="s">&#34;host&#34;</span> <span class="na">layout=</span><span class="s">&#34;${machinename}&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;attribute</span> <span class="na">name=</span><span class="s">&#34;event&#34;</span> <span class="na">encode=</span><span class="s">&#34;false&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&lt;layout</span> <span class="na">type=</span><span class="s">&#34;JsonLayout&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;attribute</span> <span class="na">name=</span><span class="s">&#34;level&#34;</span> <span class="na">layout=</span><span class="s">&#34;${level:upperCase=true}&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;attribute</span> <span class="na">name=</span><span class="s">&#34;source&#34;</span> <span class="na">layout=</span><span class="s">&#34;${logger}&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;attribute</span> <span class="na">name=</span><span class="s">&#34;thread&#34;</span> <span class="na">layout=</span><span class="s">&#34;${threadid}&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;attribute</span> <span class="na">name=</span><span class="s">&#34;message&#34;</span> <span class="na">layout=</span><span class="s">&#34;${message:withexception=true}&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;attribute</span> <span class="na">name=</span><span class="s">&#34;utc&#34;</span> <span class="na">layout=</span><span class="s">&#34;${date:universalTime=true:format=yyyy-MM-dd HH\:mm\:ss.fff}&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&lt;/layout&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;/attribute&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;/layout&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/target&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;/targets&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;rules&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;logger</span> <span class="na">name=</span><span class="s">&#34;*&#34;</span> <span class="na">minlevel=</span><span class="s">&#34;Trace&#34;</span> <span class="na">writeTo=</span><span class="s">&#34;logfile&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;logger</span> <span class="na">name=</span><span class="s">&#34;*&#34;</span> <span class="na">minlevel=</span><span class="s">&#34;Info&#34;</span> <span class="na">writeTo=</span><span class="s">&#34;logmsg&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;logger</span> <span class="na">name=</span><span class="s">&#34;*&#34;</span> <span class="na">minlevel=</span><span class="s">&#34;Error&#34;</span> <span class="na">writeTo=</span><span class="s">&#34;logapi&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;/rules&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/nlog&gt;</span></span></span></code></pre></div></div>
<p>There&rsquo;s a few interesting things to note in the above config:</p>
<ul>
<li>The name and type are on every target, but other attributes vary.</li>
<li>The third-party target, for posting to an endpoint, requires an entry in a separate &ldquo;extensions&rdquo; node. I assume it&rsquo;s because NLog knows to check for its own extensions, but needs a hint that other ones are being used.</li>
<li>Note the URL for the HTTP target. That&rsquo;s a <a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/overview?view=aspnetcore-7.0"  target="_blank" rel="noreferrer">minimal API</a> I wrote for demo purposes, which you can find in the <a href="https://github.com/grantwinney/Surviving-WinForms/tree/master/Debugging/Logging/MultipleNLogTargets"  target="_blank" rel="noreferrer">code</a> if you clone it&hellip; it&rsquo;s also using NLog to write to its own file.</li>
<li>The &ldquo;rules&rdquo; section at the bottom says to log <em>everything</em> to a local file, show anything informational or above to the user (they don&rsquo;t need to see debug or trace messages), and post any exception/critical messages to an external API (no need to send everything across the wire).</li>
</ul>

<h2 class="relative group">Using Multiple Targets
    <div id="using-multiple-targets" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#using-multiple-targets" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Adding a minimal amount of code, like below, is enough to get started. Easy!</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">readonly</span> <span class="n">Logger</span> <span class="n">logger</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="n">Form1</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">InitializeComponent</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">logger</span> <span class="p">=</span> <span class="n">LogManager</span><span class="p">.</span><span class="n">GetLogger</span><span class="p">(</span><span class="s">&#34;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnLogTrace_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">logger</span><span class="p">.</span><span class="n">Log</span><span class="p">(</span><span class="n">LogLevel</span><span class="p">.</span><span class="n">Trace</span><span class="p">,</span> <span class="n">txtMessage</span><span class="p">.</span><span class="n">Text</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnLogWarning_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">logger</span><span class="p">.</span><span class="n">Log</span><span class="p">(</span><span class="n">LogLevel</span><span class="p">.</span><span class="n">Warn</span><span class="p">,</span> <span class="n">txtMessage</span><span class="p">.</span><span class="n">Text</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnLogException_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">throw</span> <span class="k">new</span> <span class="n">Exception</span><span class="p">(</span><span class="n">txtMessage</span><span class="p">.</span><span class="n">Text</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">logger</span><span class="p">.</span><span class="n">Log</span><span class="p">(</span><span class="n">LogLevel</span><span class="p">.</span><span class="n">Error</span><span class="p">,</span> <span class="n">ex</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Better yet, if you decide to add more targets later, you can do it with minimal (or no) changes to your code. Below is part of my Form.. since I didn&rsquo;t give the various loggers names, and I don&rsquo;t specify one in the constructor below, they all apply all the time, filtered only by the &ldquo;minlevel&rdquo; attributes in the config file.</p>
<p>The &ldquo;layout&rdquo; section in the HTTP target produces JSON like this, which gets passed to the API and is logged on that side.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;sourcetype&#34;</span><span class="p">:</span> <span class="s2">&#34;_json&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;host&#34;</span><span class="p">:</span> <span class="s2">&#34;SOME-MACHINE-NAME&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;event&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;level&#34;</span><span class="p">:</span> <span class="s2">&#34;ERROR&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;thread&#34;</span><span class="p">:</span> <span class="s2">&#34;1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;message&#34;</span><span class="p">:</span> <span class="s2">&#34;Testing.. testing.. 123 testing...&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;utc&#34;</span><span class="p">:</span> <span class="s2">&#34;2023-06-29 21:06:03.476&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Once it&rsquo;s on that side, you can format the message however you like, as I&rsquo;ve done below in the &ldquo;/log&rdquo; endpoint.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">app</span><span class="p">.</span><span class="n">MapPost</span><span class="p">(</span><span class="s">&#34;/log&#34;</span><span class="p">,</span> <span class="kd">async</span> <span class="p">(</span><span class="n">HttpRequest</span> <span class="n">request</span><span class="p">)</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">payload</span> <span class="p">=</span> <span class="k">await</span> <span class="n">request</span><span class="p">.</span><span class="n">ReadFromJsonAsync</span><span class="p">&lt;</span><span class="n">AppLog</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">logMsg</span> <span class="p">=</span> <span class="s">$&#34;{payload.Event.UTC}|{payload.Host}|{payload.Event.Level}||({payload.Event.Thread}) {payload.Event.Message}&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">logger</span><span class="p">.</span><span class="n">Log</span><span class="p">(</span><span class="n">LogLevel</span><span class="p">.</span><span class="n">FromString</span><span class="p">(</span><span class="n">payload</span><span class="p">.</span><span class="n">Event</span><span class="p">.</span><span class="n">Level</span><span class="p">),</span> <span class="n">logMsg</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span></span></span></code></pre></div></div>

<h2 class="relative group">Seeing it in Action
    <div id="seeing-it-in-action" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#seeing-it-in-action" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you&rsquo;re dealing with a distributed app, having it send some logs to a central location will make it a lot easier to know about and debug critical issues faster.</p>
<p>Here&rsquo;s a short demo showing it in action. There are buttons in the Form to send messages of all varying levels, from trace to fatal. The textbox is just to simulate the informational message an app might show to the user, or a friendlier message that might get attached to an exception.</p>
<p>The file in the top half of VS Code is what&rsquo;s getting logged by the WinForms app on the client side. The file in the bottom half is being logged by the API after the desktop app sends it a log.</p>
<p>If you <a href="https://github.com/grantwinney/Surviving-WinForms/tree/master/Debugging/Logging/MultipleNLogTargets"  target="_blank" rel="noreferrer">give it a try</a> and have any thoughts to share, or you discover something really cool or surprising, feel free to reach out in the comments below!</p>
]]></content:encoded><media:content url="https://grantwinney.com/how-to-log-messages-to-multiple-targets-with-nlog/feature.webp" medium="image" type="image/webp"/></item><item><title>What is the NUnit constraint model?</title><link>https://grantwinney.com/nunit-constraint-model/</link><pubDate>Mon, 26 Jun 2023 10:45:44 +0000</pubDate><guid>https://grantwinney.com/nunit-constraint-model/</guid><description>I recently discovered the constraint model in NUnit. It&amp;rsquo;s been there for years, hiding in plain sight! What is it? Is it worth using? Let&amp;rsquo;s check it out.</description><content:encoded><![CDATA[<p>When I wrote about the new <a href="https://grantwinney.com/csharp-generic-math-support/"  target="_blank" rel="noreferrer">Generic Math support in C# 11</a>, along with some related topics like <a href="https://grantwinney.com/whats-a-static-abstract-interface-method-in-c/"  target="_blank" rel="noreferrer">static abstract interface methods</a> and <a href="https://grantwinney.com/csharp-overload-arithmetic-equality-comparison-operators/"  target="_blank" rel="noreferrer">overloading operators</a>, I created a few examples as I usually do. I added some unit tests using NUnit, and noticed warnings in Visual Studio that I hadn&rsquo;t seen before.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/nunit-constraint-model/nunit-constraint-model-warning.png"
    width="628"
      height="298"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="nunit-constraint-%20model-sugg-fixed.png"
    ></figure>
<p>Those helpful little squiggly prompts only show up when you reference the <a href="https://github.com/nunit/nunit.analyzers"  target="_blank" rel="noreferrer">analyzers for NUnit</a>, a <a href="https://www.nuget.org/packages/NUnit.Analyzers/"  target="_blank" rel="noreferrer">separate NuGet package</a> from the test library and not one I ever recall adding.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/nunit-constraint-model/nunit-analyzers-package.png"
    width="1137"
      height="275"></figure>
<p>Actually, I don&rsquo;t remember adding it <em>this</em> time either, and it isn&rsquo;t part of the NUnit project template in VS, so I&rsquo;m not sure how it got added&hellip; but I&rsquo;m glad it did. Today we get to learn something new!</p>

<h2 class="relative group">What is the classic model?
    <div id="what-is-the-classic-model" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-is-the-classic-model" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The classic model, as its name would suggest, is the original syntax for writing tests in NUnit, <a href="https://web.archive.org/web/20070214135309/sourceforge.net/project/showfiles.php?group_id=10749&amp;release_id=101664"  target="_blank" rel="noreferrer">starting in the early 2000s</a>. For the exceptionally curious, you can still check out their <a href="https://sourceforge.net/projects/nunit/files/"  target="_blank" rel="noreferrer">early source code on SourceForge</a>. I&rsquo;ve always written my NUnit tests using the above &ldquo;classic&rdquo; syntax (didn&rsquo;t know it had a name), probably because I read a tutorial back when I started programming 15 years ago and never looked back.</p>
<p>Over time I learned <a href="https://docs.nunit.org/articles/nunit/writing-tests/assertions/assertion-models/classic.html"  target="_blank" rel="noreferrer">all the different methods on the Assert class</a> that I might have to call (Less, GreaterOrEqual, IsTrue, etc). For many of them, it&rsquo;s super easy (and common) to mix up the parameters, even after years of use. Without checking for yourself (no cheating!), can you remember whether the <em>actual</em> or <em>expected</em> result is supposed to be passed in first or second in the following example?</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/nunit-constraint-model/nunit-classic-model-example.png"
    width="437"
      height="182"></figure>
<p>For this reason and more (some of which we&rsquo;ll look at below), the authors of NUnit have been <a href="https://github.com/nunit/nunit.analyzers/blob/master/documentation/NUnit2005.md"  target="_blank" rel="noreferrer">nudging devs</a> towards a different model for years.</p>

<h2 class="relative group">What is the constraint model.. and why is it better?
    <div id="what-is-the-constraint-model-and-why-is-it-better" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-is-the-constraint-model-and-why-is-it-better" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The &ldquo;constraint&rdquo; model is newer in the sense that NUnit was created over 20 years ago, but it&rsquo;s not really all that new. It&rsquo;s been around at least since <a href="https://github.com/nunit/nunit/releases/tag/3.0.0"  target="_blank" rel="noreferrer">v3.0</a> was released in 2015, and no doubt in discussion and development before that.</p>
<p>I couldn&rsquo;t find a good definition of what &ldquo;constraint&rdquo; means in this context (if you do, please let me know below), but the authors have really taken things in a different direction. Whereas the classic model has <a href="https://docs.nunit.org/articles/nunit/writing-tests/assertions/assertion-models/classic.html"  target="_blank" rel="noreferrer">many differently named methods</a> in a single <em>&ldquo;Assert&rdquo;</em> class, the constraint model uses a single <em>&ldquo;That&rdquo;</em> method with a syntax that makes it easier to understand at a glance what&rsquo;s being tested and how.</p>
<p>So if you&rsquo;re considering using it, or just recently found about it like I did and want to know more, read on. If you&rsquo;re wondering if (and how much) it&rsquo;s worth bothering about, let&rsquo;s check it out together!</p>

<h3 class="relative group">It reads more like natural language
    <div id="it-reads-more-like-natural-language" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#it-reads-more-like-natural-language" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The constraint model seems to flow better. When reading the examples below aloud, it sounds to me more like natural language. Of course, whether or not you think that sounds like a <em>good</em> idea is subjective (what isn&rsquo;t, lol).</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="na">[Test]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">EqualityTest</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">actualResult</span> <span class="p">=</span> <span class="m">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">actualMessage</span> <span class="p">=</span> <span class="s">&#34;Calculation Succeeded&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">expectedResult</span> <span class="p">=</span> <span class="m">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// classic</span>
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">AreEqual</span><span class="p">(</span><span class="n">expectedResult</span><span class="p">,</span> <span class="n">actualResult</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">AreNotEqual</span><span class="p">(</span><span class="s">&#34;Calculation Failed&#34;</span><span class="p">,</span> <span class="n">actualMessage</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// constraint</span>
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">actualResult</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="n">expectedResult</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">actualMessage</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">Not</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="s">&#34;Calculation Failed&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>I think I could hand off the titles of these tests to someone with little development experience, or have an automatic email sent out from a DevOps environment with just the title and result of failing tests, and most people would understand them.</p>
<p>True, the classic model isn&rsquo;t difficult to understand, per se, but it doesn&rsquo;t exactly roll off the tongue either. Given a choice between&hellip;</p>
<ul>
<li><em>&ldquo;assert are equal &lsquo;calculation failed&rsquo; and actual message&rdquo;,</em> or</li>
<li><em>&ldquo;assert that the message is not equal to &lsquo;calculation failed&rdquo;</em></li>
</ul>
<p>&hellip; personally I&rsquo;d choose the latter.</p>

<h3 class="relative group">It&rsquo;s less error prone
    <div id="its-less-error-prone" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#its-less-error-prone" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The equal and not equal cases are straight-forward, so let&rsquo;s consider some of the other comparisons. How about greater than or less than? Like before, it&rsquo;s impossible to tell at a glance which value is supposed to be greater (or less) than the other, unless you already know which is which.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="na">[Test]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">GreaterOrLessTest</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">actualResult</span> <span class="p">=</span> <span class="m">427</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">expectedMinPossibleValue</span> <span class="p">=</span> <span class="m">100</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">expectedMaxPossibleValue</span> <span class="p">=</span> <span class="m">999</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// classic</span>
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">GreaterOrEqual</span><span class="p">(</span><span class="n">actualResult</span><span class="p">,</span> <span class="n">expectedMinPossibleValue</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">LessOrEqual</span><span class="p">(</span><span class="n">actualResult</span><span class="p">,</span> <span class="n">expectedMaxPossibleValue</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// constraint</span>
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">actualResult</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">GreaterThanOrEqualTo</span><span class="p">(</span><span class="n">expectedMinPossibleValue</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">actualResult</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">LessThanOrEqualTo</span><span class="p">(</span><span class="n">expectedMaxPossibleValue</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Okay, my variable names kinda give it away, but that&rsquo;s no guarantee. What if I accidentally mixed up the parameters? I could inadvertently write a passing test (as the two tests below do), even though they should fail if you look closely at the values.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="na">[Test]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">GreaterOrLessOopsIGoofedUpTest</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">actualResult1</span> <span class="p">=</span> <span class="m">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">actualResult2</span> <span class="p">=</span> <span class="m">20000</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">expectedMinPossibleValue</span> <span class="p">=</span> <span class="m">100</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">expectedMaxPossibleValue</span> <span class="p">=</span> <span class="m">999</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">GreaterOrEqual</span><span class="p">(</span><span class="n">expectedMinPossibleValue</span><span class="p">,</span> <span class="n">actualResult1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">LessOrEqual</span><span class="p">(</span><span class="n">expectedMaxPossibleValue</span><span class="p">,</span> <span class="n">actualResult2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Thanks to the way the syntax is phrased in the constraint model, it&rsquo;s highly unlikely you&rsquo;ll mix up variables like that. If you somehow did put them wrong, the entire test reads weirdly too. Assert that the minimum possible value is greater than the actual result? Assert that the max possible value is less than the other actual result? Uh, what?</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// neither of these make sense when you read them out loud</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">expectedMinPossibleValue</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">GreaterThanOrEqualTo</span><span class="p">(</span><span class="n">actualResult1</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">expectedMaxPossibleValue</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">LessThanOrEqualTo</span><span class="p">(</span><span class="n">actualResult2</span><span class="p">));</span></span></span></code></pre></div></div>

<h3 class="relative group">It&rsquo;s more flexible
    <div id="its-more-flexible" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#its-more-flexible" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The NUnit wiki page on <a href="https://docs.nunit.org/articles/nunit/writing-tests/assertions/assertions.html"  target="_blank" rel="noreferrer">assertions</a> has a nice example of the increased flexibility the constraint model provides. In the following test, they&rsquo;re making sure the array has exactly one &ldquo;3&rdquo;, has exactly two numbers greater than &ldquo;1&rdquo;, etc.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">int</span><span class="p">[]</span> <span class="n">array</span> <span class="p">=</span> <span class="p">{</span> <span class="m">1</span><span class="p">,</span> <span class="m">2</span><span class="p">,</span> <span class="m">3</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="n">Has</span><span class="p">.</span><span class="n">Exactly</span><span class="p">(</span><span class="m">1</span><span class="p">).</span><span class="n">EqualTo</span><span class="p">(</span><span class="m">3</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="n">Has</span><span class="p">.</span><span class="n">Exactly</span><span class="p">(</span><span class="m">2</span><span class="p">).</span><span class="n">GreaterThan</span><span class="p">(</span><span class="m">1</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="n">Has</span><span class="p">.</span><span class="n">Exactly</span><span class="p">(</span><span class="m">3</span><span class="p">).</span><span class="n">LessThan</span><span class="p">(</span><span class="m">100</span><span class="p">));</span></span></span></code></pre></div></div>
<p>There are ways to achieve this with the classic model using LINQ, but none of them are as readable as what the above syntax produces.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">int</span><span class="p">[]</span> <span class="n">array</span> <span class="p">=</span> <span class="p">{</span> <span class="m">1</span><span class="p">,</span> <span class="m">2</span><span class="p">,</span> <span class="m">3</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">True</span><span class="p">(</span><span class="n">array</span><span class="p">.</span><span class="n">Count</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span> <span class="p">==</span> <span class="m">1</span><span class="p">)</span> <span class="p">==</span> <span class="m">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">IsTrue</span><span class="p">(</span><span class="n">array</span><span class="p">.</span><span class="n">Count</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span> <span class="p">&gt;</span> <span class="m">1</span><span class="p">)</span> <span class="p">==</span> <span class="m">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">AreEqual</span><span class="p">(</span><span class="m">3</span><span class="p">,</span> <span class="n">array</span><span class="p">.</span><span class="n">Count</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span> <span class="p">&lt;</span> <span class="m">100</span><span class="p">));</span></span></span></code></pre></div></div>

<h3 class="relative group">It&rsquo;s has clearer error messages
    <div id="its-has-clearer-error-messages" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#its-has-clearer-error-messages" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>As I said, you can achieve most of the constraint model stuff using LINQ, but many of those statements will just resolve to True or False. NUnit can&rsquo;t do much with that information other than tell you that you got one or the other, like in the following example.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">int</span><span class="p">[]</span> <span class="n">array</span> <span class="p">=</span> <span class="p">{</span> <span class="m">1</span><span class="p">,</span> <span class="m">2</span><span class="p">,</span> <span class="m">3</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">Multiple</span><span class="p">(()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">True</span><span class="p">(</span><span class="n">array</span><span class="p">.</span><span class="n">Count</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span> <span class="p">==</span> <span class="m">1</span><span class="p">)</span> <span class="p">==</span> <span class="m">50</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">IsTrue</span><span class="p">(</span><span class="n">array</span><span class="p">.</span><span class="n">Count</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span> <span class="p">&gt;</span> <span class="m">1</span><span class="p">)</span> <span class="p">==</span> <span class="m">500</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">AreEqual</span><span class="p">(</span><span class="m">5000</span><span class="p">,</span> <span class="n">array</span><span class="p">.</span><span class="n">Count</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span> <span class="p">&lt;</span> <span class="m">100</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/****
</span></span></span><span class="line"><span class="cl"><span class="cm">Message: 
</span></span></span><span class="line"><span class="cl"><span class="cm">  Multiple failures or warnings in test:
</span></span></span><span class="line"><span class="cl"><span class="cm">    1)   Expected: True
</span></span></span><span class="line"><span class="cl"><span class="cm">    But was:  False
</span></span></span><span class="line"><span class="cl"><span class="cm">
</span></span></span><span class="line"><span class="cl"><span class="cm">    2)   Expected: True
</span></span></span><span class="line"><span class="cl"><span class="cm">    But was:  False
</span></span></span><span class="line"><span class="cl"><span class="cm">
</span></span></span><span class="line"><span class="cl"><span class="cm">    3)   Expected: 5000
</span></span></span><span class="line"><span class="cl"><span class="cm">    But was:  3
</span></span></span><span class="line"><span class="cl"><span class="cm">****/</span></span></span></code></pre></div></div>
<p>Classic Model</p>
<p>Using the constraint syntax, you get a more specific response about what was expected and what the actual result was.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">int</span><span class="p">[]</span> <span class="n">array</span> <span class="p">=</span> <span class="p">{</span> <span class="m">1</span><span class="p">,</span> <span class="m">2</span><span class="p">,</span> <span class="m">3</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">Multiple</span><span class="p">(()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="n">Has</span><span class="p">.</span><span class="n">Exactly</span><span class="p">(</span><span class="m">50</span><span class="p">).</span><span class="n">EqualTo</span><span class="p">(</span><span class="m">1</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="n">Has</span><span class="p">.</span><span class="n">Exactly</span><span class="p">(</span><span class="m">500</span><span class="p">).</span><span class="n">GreaterThan</span><span class="p">(</span><span class="m">1</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="n">Has</span><span class="p">.</span><span class="n">Length</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="m">5000</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/****
</span></span></span><span class="line"><span class="cl"><span class="cm">//  Message: 
</span></span></span><span class="line"><span class="cl"><span class="cm">//    Multiple failures or warnings in test:
</span></span></span><span class="line"><span class="cl"><span class="cm">//      1)   Expected: exactly 50 items equal to 1
</span></span></span><span class="line"><span class="cl"><span class="cm">//      But was:  1 item &lt; 1, 2, 3 &gt;
</span></span></span><span class="line"><span class="cl"><span class="cm">//
</span></span></span><span class="line"><span class="cl"><span class="cm">//      2)   Expected: exactly 500 items greater than 1
</span></span></span><span class="line"><span class="cl"><span class="cm">//      But was:  2 items &lt; 1, 2, 3 &gt;
</span></span></span><span class="line"><span class="cl"><span class="cm">//
</span></span></span><span class="line"><span class="cl"><span class="cm">//      3)   Expected: property Length equal to 5000
</span></span></span><span class="line"><span class="cl"><span class="cm">//      But was:  3
</span></span></span><span class="line"><span class="cl"><span class="cm">****/</span></span></span></code></pre></div></div>
<p>Here&rsquo;s another example, where I created an <code>Employee</code> class, populated a collection of employees, and then wrote a few tests to validate the results.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">acme</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Company</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="n">acme</span><span class="p">.</span><span class="n">Employees</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="k">new</span> <span class="n">Employee</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Sam&#34;</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="n">acme</span><span class="p">.</span><span class="n">Employees</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="k">new</span> <span class="n">Employee</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Sue&#34;</span><span class="p">,</span> <span class="n">IsExec</span> <span class="p">=</span> <span class="kc">true</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="n">acme</span><span class="p">.</span><span class="n">Employees</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="k">new</span> <span class="n">Employee</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Wile E&#34;</span><span class="p">,</span> <span class="n">IsExec</span> <span class="p">=</span> <span class="kc">true</span><span class="p">,</span> <span class="n">IsCEO</span> <span class="p">=</span> <span class="kc">true</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="n">acme</span><span class="p">.</span><span class="n">Employees</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="k">new</span> <span class="n">Employee</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Ron&#34;</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="n">acme</span><span class="p">.</span><span class="n">Employees</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="k">new</span> <span class="n">Employee</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Gale&#34;</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">Multiple</span><span class="p">(()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">AreEqual</span><span class="p">(</span><span class="m">3</span><span class="p">,</span> <span class="n">acme</span><span class="p">.</span><span class="n">Execs</span><span class="p">.</span><span class="n">Count</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">Greater</span><span class="p">(</span><span class="m">10</span><span class="p">,</span> <span class="n">acme</span><span class="p">.</span><span class="n">Execs</span><span class="p">.</span><span class="n">Count</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">True</span><span class="p">(</span><span class="n">acme</span><span class="p">.</span><span class="n">CEO</span><span class="p">.</span><span class="n">Name</span><span class="p">.</span><span class="n">StartsWith</span><span class="p">(</span><span class="s">&#34;R&#34;</span><span class="p">)</span> <span class="p">&amp;&amp;</span> <span class="n">acme</span><span class="p">.</span><span class="n">CEO</span><span class="p">.</span><span class="n">Name</span><span class="p">.</span><span class="n">EndsWith</span><span class="p">(</span><span class="s">&#34;n&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/****
</span></span></span><span class="line"><span class="cl"><span class="cm">Message: 
</span></span></span><span class="line"><span class="cl"><span class="cm">  Multiple failures or warnings in test:
</span></span></span><span class="line"><span class="cl"><span class="cm">    1)   Expected: 3
</span></span></span><span class="line"><span class="cl"><span class="cm">    But was:  2
</span></span></span><span class="line"><span class="cl"><span class="cm">
</span></span></span><span class="line"><span class="cl"><span class="cm">    2)   Expected: less than 2
</span></span></span><span class="line"><span class="cl"><span class="cm">    But was:  5
</span></span></span><span class="line"><span class="cl"><span class="cm">
</span></span></span><span class="line"><span class="cl"><span class="cm">    3)   Expected: True
</span></span></span><span class="line"><span class="cl"><span class="cm">    But was:  False
</span></span></span><span class="line"><span class="cl"><span class="cm">****/</span></span></span></code></pre></div></div>
<p>Again, the constraint model provides us with more specific, more readable explanations when tests fail.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">acme</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Company</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="n">acme</span><span class="p">.</span><span class="n">Employees</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="k">new</span> <span class="n">Employee</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Sam&#34;</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="n">acme</span><span class="p">.</span><span class="n">Employees</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="k">new</span> <span class="n">Employee</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Sue&#34;</span><span class="p">,</span> <span class="n">IsExec</span> <span class="p">=</span> <span class="kc">true</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="n">acme</span><span class="p">.</span><span class="n">Employees</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="k">new</span> <span class="n">Employee</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Wile E&#34;</span><span class="p">,</span> <span class="n">IsExec</span> <span class="p">=</span> <span class="kc">true</span><span class="p">,</span> <span class="n">IsCEO</span> <span class="p">=</span> <span class="kc">true</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="n">acme</span><span class="p">.</span><span class="n">Employees</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="k">new</span> <span class="n">Employee</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Ron&#34;</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="n">acme</span><span class="p">.</span><span class="n">Employees</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="k">new</span> <span class="n">Employee</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Gale&#34;</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">Multiple</span><span class="p">(()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">acme</span><span class="p">.</span><span class="n">Execs</span><span class="p">,</span> <span class="n">Has</span><span class="p">.</span><span class="n">Count</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="m">3</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">acme</span><span class="p">.</span><span class="n">Execs</span><span class="p">,</span> <span class="n">Has</span><span class="p">.</span><span class="n">Count</span><span class="p">.</span><span class="n">GreaterThan</span><span class="p">(</span><span class="m">10</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">acme</span><span class="p">.</span><span class="n">CEO</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span> <span class="n">Does</span><span class="p">.</span><span class="n">StartWith</span><span class="p">(</span><span class="s">&#34;R&#34;</span><span class="p">).</span><span class="n">And</span><span class="p">.</span><span class="n">EndsWith</span><span class="p">(</span><span class="s">&#34;n&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">/****</span>
</span></span><span class="line"><span class="cl"><span class="n">Message</span><span class="p">:</span> 
</span></span><span class="line"><span class="cl">  <span class="n">Multiple</span> <span class="n">failures</span> <span class="n">or</span> <span class="n">warnings</span> <span class="k">in</span> <span class="n">test</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="m">1</span><span class="p">)</span>   <span class="n">Expected</span><span class="p">:</span> <span class="n">property</span> <span class="n">Count</span> <span class="n">equal</span> <span class="n">to</span> <span class="m">3</span>
</span></span><span class="line"><span class="cl">    <span class="n">But</span> <span class="n">was</span><span class="p">:</span>  <span class="m">2</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="m">2</span><span class="p">)</span>   <span class="n">Expected</span><span class="p">:</span> <span class="n">property</span> <span class="n">Count</span> <span class="n">greater</span> <span class="n">than</span> <span class="m">10</span>
</span></span><span class="line"><span class="cl">    <span class="n">But</span> <span class="n">was</span><span class="p">:</span>  <span class="m">2</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="m">3</span><span class="p">)</span>   <span class="n">Expected</span><span class="p">:</span> <span class="n">String</span> <span class="n">starting</span> <span class="n">with</span> <span class="s">&#34;R&#34;</span> <span class="n">and</span> <span class="n">String</span> <span class="n">ending</span> <span class="n">with</span> <span class="s">&#34;n&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">But</span> <span class="n">was</span><span class="p">:</span>  <span class="s">&#34;Wile E&#34;</span></span></span></code></pre></div></div>

<h3 class="relative group">It works side-by-side with the classic model
    <div id="it-works-side-by-side-with-the-classic-model" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#it-works-side-by-side-with-the-classic-model" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Both models can live happily side-by-side. In fact, under the covers <a href="https://docs.nunit.org/articles/nunit/writing-tests/assertions/assertions.html"  target="_blank" rel="noreferrer">the classic model just calls the constraint model</a>.</p>
<p>There&rsquo;s an <a href="https://github.com/nunit/nunit/issues/3688"  target="_blank" rel="noreferrer">ongoing discussion</a> about whether the classic model should officially be marked legacy or obsolete, but as I poked around it became evident that the authors have intentionally tried to <em>not</em> break codebases that use the classic syntax. It&rsquo;s not being actively developed, but I doubt it&rsquo;s going anywhere anytime soon.</p>
<p>So if you, like me, decide to give it a try the next time you&rsquo;re writing some tests, you can do it without feeling like you need to rewrite any old tests. And if after awhile you decide the constraint model isn&rsquo;t doing it for you and you switch back, you don&rsquo;t need to rewrite any of those constraint tests either.</p>
<p>Sounds to me like there&rsquo;s nothing to lose!</p>
]]></content:encoded><media:content url="https://grantwinney.com/nunit-constraint-model/feature.webp" medium="image" type="image/webp"/></item><item><title>Adding deconstructors to C# types</title><link>https://grantwinney.com/csharp-deconstructors/</link><pubDate>Thu, 15 Jun 2023 22:12:31 +0000</pubDate><guid>https://grantwinney.com/csharp-deconstructors/</guid><description>We can deconstruct tuples in C#, but does it work with other types? And assuming it does (spoiler - it does), is it worth bothering with?</description><content:encoded><![CDATA[<p>Earlier this year, I wrote about being able to <a href="https://grantwinney.com/using-tuple-and-deconstruction-to-return-multiple-values/"  target="_blank" rel="noreferrer">deconstruct tuples in C#</a>, something that was added to <a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-70"  target="_blank" rel="noreferrer">C# 7</a>. That kind of functional behavior is one of the <em>(so so very few)</em> things I miss from my years of writing Erlang code. It neatens up your code a bit, and you can read more about it here:</p>
<p><a href="https://grantwinney.com/using-tuple-and-deconstruction-to-return-multiple-values/"  target="_blank" rel="noreferrer">Using Tuples and deconstruction to return multiple values in C#</a></p>
<p>I got to thinking recently though - besides Tuples, is there anywhere else we can use this concept? The answer&rsquo;s yes, we <em>can</em> <a href="https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/deconstruct#user-defined-types"  target="_blank" rel="noreferrer">define our own deconstruction logic</a> in the classes we create, but now the question is&hellip; is it worth it?</p>
<blockquote><p>The code in this post is available on <a href="https://github.com/grantwinney/CSharpDotNetExamples/tree/master/C%23%2007/DeconstructingUserDefinedTypes"  target="_blank" rel="noreferrer">GitHub</a>, for you to use, expand upon, or just follow along while you read&hellip; and hopefully discover something new!</p>
</blockquote>
<h2 class="relative group">Can we deconstruct our own types?
    <div id="can-we-deconstruct-our-own-types" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#can-we-deconstruct-our-own-types" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Microsoft has <a href="https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/deconstruct#user-defined-types"  target="_blank" rel="noreferrer">their own example</a>, using employees, so we&rsquo;ll do something a little different&hellip; okay, maybe not <em>that</em> different. Here&rsquo;s a simple Country class that can hold a collection of States. Add a simple Deconstruct method and voila - we can extract a couple values, like &ldquo;name&rdquo; and &ldquo;population&rdquo;.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Country</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">State</span><span class="p">&gt;</span> <span class="n">States</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">State</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">long</span> <span class="n">TotalPopulation</span> <span class="p">=&gt;</span> <span class="n">States</span><span class="p">.</span><span class="n">Sum</span><span class="p">(</span><span class="n">s</span> <span class="p">=&gt;</span> <span class="n">s</span><span class="p">.</span><span class="n">Population</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;summary&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// Returns the name and population of the country.</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;/summary&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;param name=&#34;name&#34;&gt;Name&lt;/param&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;param name=&#34;pop&#34;&gt;Population&lt;/param&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">Deconstruct</span><span class="p">(</span><span class="k">out</span> <span class="kt">string</span> <span class="n">name</span><span class="p">,</span> <span class="k">out</span> <span class="kt">long</span> <span class="n">pop</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">name</span> <span class="p">=</span> <span class="n">Name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">pop</span> <span class="p">=</span> <span class="n">States</span><span class="p">.</span><span class="n">Sum</span><span class="p">(</span><span class="n">s</span> <span class="p">=&gt;</span> <span class="n">s</span><span class="p">.</span><span class="n">Population</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;summary&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// Returns the name, population, and number of states.</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;/summary&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;param name=&#34;name&#34;&gt;Name&lt;/param&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;param name=&#34;pop&#34;&gt;Population&lt;/param&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;param name=&#34;numberOfStates&#34;&gt;Number of states&lt;/param&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">Deconstruct</span><span class="p">(</span><span class="k">out</span> <span class="kt">string</span> <span class="n">name</span><span class="p">,</span> <span class="k">out</span> <span class="kt">long</span> <span class="n">pop</span><span class="p">,</span> <span class="k">out</span> <span class="kt">int</span> <span class="n">numberOfStates</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">name</span> <span class="p">=</span> <span class="n">Name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">pop</span> <span class="p">=</span> <span class="n">States</span><span class="p">.</span><span class="n">Sum</span><span class="p">(</span><span class="n">s</span> <span class="p">=&gt;</span> <span class="n">s</span><span class="p">.</span><span class="n">Population</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">numberOfStates</span> <span class="p">=</span> <span class="n">States</span><span class="p">.</span><span class="n">Count</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">State</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">long</span> <span class="n">Population</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/***********************/</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">DeconstructUserDefinedTypes</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">usa</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Country</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;United States&#34;</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="n">usa</span><span class="p">.</span><span class="n">States</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="k">new</span> <span class="n">State</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Utah&#34;</span><span class="p">,</span> <span class="n">Population</span> <span class="p">=</span> <span class="m">3380800</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="n">usa</span><span class="p">.</span><span class="n">States</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="k">new</span> <span class="n">State</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Maine&#34;</span><span class="p">,</span> <span class="n">Population</span> <span class="p">=</span> <span class="m">1385340</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="n">usa</span><span class="p">.</span><span class="n">States</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="k">new</span> <span class="n">State</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Florida&#34;</span><span class="p">,</span> <span class="n">Population</span> <span class="p">=</span> <span class="m">22244823</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">population</span><span class="p">)</span> <span class="p">=</span> <span class="n">usa</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;{name} has {population} people in it.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// United States has 27010963 people in it.</span></span></span></code></pre></div></div>
<p>Right out the gate, I see some deal-breakers. Even if the Deconstruct method has comments on it, they don&rsquo;t show up when you attempt to deconstruct an instance. Hovering over the name where you instantiated the class shows nothing useful. In fact, you don&rsquo;t even know whether there <em>is</em> a Deconstruct method on a class, or whether there&rsquo;s 5 or 50 overloads of it, without delving into the class itself.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/csharp-deconstructors/image.png"
    width="576"
      height="76"></figure>
<p>It&rsquo;s far more useful to call the Deconstruct method directly, but then it&rsquo;s just like any other public method with a couple &ldquo;out&rdquo; variables, so&hellip; nothing special there.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/csharp-deconstructors/image-1.png"
    width="523"
      height="99"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/csharp-deconstructors/image-2.png"
    width="515"
      height="103"></figure>
<p>Also, since method overloads can&rsquo;t have the same arity (numbers and types of parameters), you can&rsquo;t have one Deconstruct method that (for example) returns a country name and population, and another that returns the country name and total state count (assuming population and state count are both represented by the same type, like integer or long).</p>
<p>There&rsquo;s absolutely nothing here that performs better or is easier to use than just accessing the properties themselves:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;{usa.Name} has {usa.TotalPopulation} people in it.&#34;</span><span class="p">);</span></span></span></code></pre></div></div>
<p>So that begs the question, <em>is</em> there a good reason to use these? Or are they only available because of the way they were implemented for Tuples?</p>

<h2 class="relative group">Can we deconstruct built-in types?
    <div id="can-we-deconstruct-built-in-types" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#can-we-deconstruct-built-in-types" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Where I think they might be more helpful is in the use of <a href="https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/deconstruct#extension-methods-for-user-defined-types"  target="_blank" rel="noreferrer">extension methods</a> on built-in types that we already know and are relatively simple to understand. Take the <code>Point</code> and <code>Size</code> structs, for example. Let&rsquo;s write a couple extension methods that add a &ldquo;Deconstruct&rdquo; method to each of those:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="k">class</span> <span class="nc">ExtendedPoint</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="k">void</span> <span class="n">Deconstruct</span><span class="p">(</span><span class="k">this</span> <span class="n">Point</span> <span class="n">p</span><span class="p">,</span> <span class="k">out</span> <span class="kt">int</span> <span class="n">x</span><span class="p">,</span> <span class="k">out</span> <span class="kt">int</span> <span class="n">y</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">x</span> <span class="p">=</span> <span class="n">p</span><span class="p">.</span><span class="n">X</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">y</span> <span class="p">=</span> <span class="n">p</span><span class="p">.</span><span class="n">Y</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="k">class</span> <span class="nc">ExtendedSize</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="k">void</span> <span class="n">Deconstruct</span><span class="p">(</span><span class="k">this</span> <span class="n">Size</span> <span class="n">s</span><span class="p">,</span> <span class="k">out</span> <span class="kt">int</span> <span class="n">w</span><span class="p">,</span> <span class="k">out</span> <span class="kt">int</span> <span class="n">h</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">w</span> <span class="p">=</span> <span class="n">s</span><span class="p">.</span><span class="n">Width</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">h</span> <span class="p">=</span> <span class="n">s</span><span class="p">.</span><span class="n">Height</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>And then a Furniture class, that&rsquo;s maybe part of a home design app or something, and it has a size and location within the house.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">internal</span> <span class="k">class</span> <span class="nc">Furniture</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Point</span> <span class="n">Location</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Size</span> <span class="n">Size</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>If we define a couch (for example) with a given location and size, we can quickly deconstruct those values later on when we need them.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">couch</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Furniture</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Couch&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">Location</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Point</span><span class="p">(</span><span class="m">3</span><span class="p">,</span> <span class="m">4</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">Size</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Size</span><span class="p">(</span><span class="m">6</span><span class="p">,</span> <span class="m">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="p">=</span> <span class="n">couch</span><span class="p">.</span><span class="n">Location</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="p">(</span><span class="n">width</span><span class="p">,</span> <span class="n">height</span><span class="p">)</span> <span class="p">=</span> <span class="n">couch</span><span class="p">.</span><span class="n">Size</span><span class="p">;</span></span></span></code></pre></div></div>
<p>I feel like this works better, because the first thing I&rsquo;d assume to get from a <code>Point</code> is the x and y coordinates, and from a <code>Size</code> its width and height. It seems slightly more streamlined than doing <code>couch.Location.X</code> and <code>couch.Location.Y</code>.</p>
<p>On a side note, gotta say I&rsquo;m impressed with VS 2022. As I created the Deconstruct method for this example, it correctly guessed that I&rsquo;d want to set <code>w</code> to the width of the <code>Size</code> object and <code>h</code> to its height. Magic. 🪄</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/csharp-deconstructors/image-4.png"
    width="686"
      height="170"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/csharp-deconstructors/image-3.png"
    width="688"
      height="165"></figure>
<p>What do you think? Will you use deconstructors? Do you have your own ideas of how and when to implement them?</p>
<p>If you found this content useful, and want to learn more about a variety of C# features, check out <a href="https://github.com/grantwinney/CSharpDotNetExamples"  target="_blank" rel="noreferrer">this GitHub repo</a>, where you&rsquo;ll find links to plenty more blog posts and practical examples!</p>
]]></content:encoded><media:content url="https://grantwinney.com/csharp-deconstructors/feature.webp" medium="image" type="image/webp"/></item><item><title>Generic Math Support in C# 11</title><link>https://grantwinney.com/csharp-generic-math-support/</link><pubDate>Wed, 05 Apr 2023 03:50:14 +0000</pubDate><guid>https://grantwinney.com/csharp-generic-math-support/</guid><description>What is Generic Math support in C# 11, and how do we take advantage of it? Let&amp;rsquo;s dig in and find out! (part 3 of 3)</description><content:encoded><![CDATA[<p>This is post 3 in a 3-part series building up to a new C# 11 feature called <a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#generic-math-support"  target="_blank" rel="noreferrer">Generic Math</a>. First though, it might be helpful to read two other posts, to get familiar with <a href="https://grantwinney.com/whats-a-static-abstract-interface-method-in-c/"  target="_blank" rel="noreferrer">static abstract members</a> (also new to C# 11) and <a href="https://grantwinney.com/csharp-overload-arithmetic-equality-comparison-operators/"  target="_blank" rel="noreferrer">overloading operators</a> (not new, but useful).</p>
<blockquote><p>The code in this post is available on <a href="https://github.com/grantwinney/CSharpDotNetExamples/tree/master/C%23%2011/GenericMathSupport/GenericMathSupport"  target="_blank" rel="noreferrer">GitHub</a>, for you to use, expand upon, or just follow along while you read&hellip; and hopefully discover something new!</p>
</blockquote>
<h2 class="relative group">Generics
    <div id="generics" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#generics" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Since <a href="https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/types/generics"  target="_blank" rel="noreferrer">generics</a> were introduced in version 2.0 <a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-20"  target="_blank" rel="noreferrer">back in 2005</a>, it&rsquo;s been enhanced a few times over the years, most recently in C# 11. Generics let us write code in a way that the exact type of the data doesn&rsquo;t have to be known right away.</p>
<p>You can, for example, write a base class with generics that allows any class inheriting from it to define an &ldquo;identifier&rdquo; that&rsquo;s a different type, like this code does. Notice how Box uses an integer for its identifier, Crate uses a string, and Folder uses a Guid. We don&rsquo;t need to define the same <code>GetIdentifier()</code> method in each of the classes either - nice and <a href="https://deviq.com/principles/dont-repeat-yourself"  target="_blank" rel="noreferrer">DRY</a>.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Container</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">T</span> <span class="n">Identifier</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">GetIdentifier</span><span class="p">()</span> <span class="p">=&gt;</span> <span class="s">$&#34;The identifier is: {Identifier}&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Box</span> <span class="p">:</span> <span class="n">Container</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Box</span><span class="p">(</span><span class="kt">int</span> <span class="n">identifier</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Identifier</span> <span class="p">=</span> <span class="n">identifier</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Crate</span> <span class="p">:</span> <span class="n">Container</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Crate</span><span class="p">(</span><span class="kt">string</span> <span class="n">identifier</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Identifier</span> <span class="p">=</span> <span class="n">identifier</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Folder</span> <span class="p">:</span> <span class="n">Container</span><span class="p">&lt;</span><span class="n">Guid</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Folder</span><span class="p">(</span><span class="n">Guid</span> <span class="n">identifier</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Identifier</span> <span class="p">=</span> <span class="n">identifier</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">[Test]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">GetIdentifierWorksForAllTypesOfContainers</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">boxId</span> <span class="p">=</span> <span class="m">4</span><span class="p">;</span>  <span class="c1">// chosen by fair dice roll; randomness guaranteed</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">crateId</span> <span class="p">=</span> <span class="s">&#34;absolutely_unique_id&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">folderId</span> <span class="p">=</span> <span class="n">Guid</span><span class="p">.</span><span class="n">NewGuid</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">box</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Box</span><span class="p">(</span><span class="n">boxId</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">crate</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Crate</span><span class="p">(</span><span class="n">crateId</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">folder</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Folder</span><span class="p">(</span><span class="n">folderId</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">Multiple</span><span class="p">(()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">box</span><span class="p">.</span><span class="n">GetIdentifier</span><span class="p">(),</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="s">$&#34;The identifier is: {boxId}&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">crate</span><span class="p">.</span><span class="n">GetIdentifier</span><span class="p">(),</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="s">$&#34;The identifier is: {crateId}&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">folder</span><span class="p">.</span><span class="n">GetIdentifier</span><span class="p">(),</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="s">$&#34;The identifier is: {folderId}&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>You can use generics in interfaces too, like the .NET framework does with <a href="https://referencesource.microsoft.com/#mscorlib/system/collections/generic/ienumerable.cs,3acf01620172c7f0"  target="_blank" rel="noreferrer"><code>IEnumerable&lt;T&gt;</code></a> (heavily used with LINQ).</p>
<p>Here&rsquo;s a slightly different version of the above code, replacing the generic base class with a generic interface. Anything that accepts an <code>IContainer&lt;T&gt;</code> can rest assured that the classes implementing the interface will have an identifier (whose type may vary per class, like with the int, string, and Guid above) and a method that prints a description about the identifier.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">interface</span> <span class="nc">IContainer</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">T</span> <span class="n">Identifier</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span> <span class="n">GetDescription</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Box</span> <span class="p">:</span> <span class="n">IContainer</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">Identifier</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">GetDescription</span><span class="p">()</span> <span class="p">=&gt;</span> <span class="s">$&#34;The box id is {Identifier}.&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Crate</span> <span class="p">:</span> <span class="n">IContainer</span><span class="p">&lt;</span><span class="n">Guid</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Guid</span> <span class="n">Identifier</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">GetDescription</span><span class="p">()</span> <span class="p">=&gt;</span> <span class="s">$&#34;The guid is: {Identifier}&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">[Test]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">GetDescriptionWorksForAllTypesOfContainers</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">boxId</span> <span class="p">=</span> <span class="m">4</span><span class="p">;</span>  <span class="c1">// chosen by fair dice roll; randomness guaranteed</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">crateId</span> <span class="p">=</span> <span class="n">Guid</span><span class="p">.</span><span class="n">NewGuid</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">box</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Box</span> <span class="p">{</span> <span class="n">Identifier</span> <span class="p">=</span> <span class="n">boxId</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">crate</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Crate</span> <span class="p">{</span> <span class="n">Identifier</span> <span class="p">=</span> <span class="n">crateId</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">Multiple</span><span class="p">(()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">box</span><span class="p">.</span><span class="n">GetDescription</span><span class="p">(),</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="s">$&#34;The box id is {boxId}.&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">crate</span><span class="p">.</span><span class="n">GetDescription</span><span class="p">(),</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="s">$&#34;The guid is: {crateId}&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h2 class="relative group">Generic Math
    <div id="generic-math" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#generic-math" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>One thing we haven&rsquo;t really been able to do before, though, is add &ldquo;static&rdquo; members to an interface. Actually, since C# 8 we&rsquo;ve apparently been able to add static methods to interfaces as long as they declare a default body. I&rsquo;m sure there&rsquo;s a good reason for it, but I haven&rsquo;t used it yet.</p>
<p>Anyway, since <a href="https://grantwinney.com/csharp-overload-arithmetic-equality-comparison-operators/"  target="_blank" rel="noreferrer">overloading an operator</a> requires defining a static method on a class, and there&rsquo;s never been a way to specify a static <em>abstract</em> member in an interface (aka one without a default body), it hasn&rsquo;t been possible to have an interface require that classes overload certain operators. Until now.</p>
<p>As of C# 11, you can add <a href="https://grantwinney.com/whats-a-static-abstract-interface-method-in-c/"  target="_blank" rel="noreferrer">static abstract members</a> to interfaces, which means you <em>can</em> require classes to have to implement one or more overloaded operators. Although you can certainly test this with your own interfaces <em>(</em><a href="https://grantwinney.com/whats-a-static-abstract-interface-method-in-c/"  target="_blank" rel="noreferrer"><em>learn more here</em></a><em>),</em> you can also make use of the new interfaces that C# 11 has given us. There&rsquo;s <a href="https://source.dot.net/#System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/Numerics/IAdditionOperators.cs,67cc175feb3d46df"  target="_blank" rel="noreferrer">IAdditionOperators</a> and <a href="https://source.dot.net/#System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/Numerics/IComparisonOperators.cs,75f68921e83607a1"  target="_blank" rel="noreferrer">IComparisonOperators</a>, as well as <a href="https://learn.microsoft.com/en-us/dotnet/standard/generics/math#operator-interfaces"  target="_blank" rel="noreferrer">quite a few others</a>. Let&rsquo;s take a closer look&hellip;</p>

<h3 class="relative group">Practical Examples
    <div id="practical-examples" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#practical-examples" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>First, let&rsquo;s look at a &ldquo;Fraction&rdquo; class that implements the two interfaces I mentioned above (inspired by the Fraction struct in Microsoft&rsquo;s <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/operator-overloading"  target="_blank" rel="noreferrer">operator overloading</a> docs).</p>
<p>Implementing the <code>IAdditionOperators</code> interface forces overloading the <code>+</code> operator. For this class, I&rsquo;m performing some simple math on two fractions&rsquo; numerators and denominators, and not bothering with edge-cases (like a zero denominator).</p>
<p>Implementing the <code>IComparisonOperators</code> interface forces overloading all the other operators in the following example. To keep things simple, I&rsquo;m just performing simple division (gotta watch out for the effects of <a href="https://mathworld.wolfram.com/IntegerDivision.html"  target="_blank" rel="noreferrer">integer division</a>!) and using the decimal value for comparisons.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Fraction</span> <span class="p">:</span> <span class="n">IAdditionOperators</span><span class="p">&lt;</span><span class="n">Fraction</span><span class="p">,</span> <span class="n">Fraction</span><span class="p">,</span> <span class="n">Fraction</span><span class="p">&gt;,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">IComparisonOperators</span><span class="p">&lt;</span><span class="n">Fraction</span><span class="p">,</span> <span class="n">Fraction</span><span class="p">,</span> <span class="kt">bool</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Fraction</span><span class="p">(</span><span class="kt">int</span> <span class="n">numerator</span><span class="p">,</span> <span class="kt">int</span> <span class="n">denominator</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Numerator</span> <span class="p">=</span> <span class="n">numerator</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Denominator</span> <span class="p">=</span> <span class="n">denominator</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">Numerator</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="kd">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">Denominator</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="kd">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">decimal</span> <span class="n">Value</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">Numerator</span> <span class="p">/</span> <span class="p">(</span><span class="kt">decimal</span><span class="p">)</span><span class="n">Denominator</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="n">Fraction</span> <span class="kd">operator</span> <span class="p">+(</span><span class="n">Fraction</span> <span class="n">f1</span><span class="p">,</span> <span class="n">Fraction</span> <span class="n">f2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">=&gt;</span> <span class="k">new</span><span class="p">(</span><span class="n">f1</span><span class="p">.</span><span class="n">Numerator</span> <span class="p">*</span> <span class="n">f2</span><span class="p">.</span><span class="n">Denominator</span> <span class="p">+</span> <span class="n">f2</span><span class="p">.</span><span class="n">Numerator</span> <span class="p">*</span> <span class="n">f1</span><span class="p">.</span><span class="n">Denominator</span><span class="p">,</span> <span class="n">f1</span><span class="p">.</span><span class="n">Denominator</span> <span class="p">*</span> <span class="n">f2</span><span class="p">.</span><span class="n">Denominator</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">bool</span> <span class="kd">operator</span> <span class="p">==(</span><span class="n">Fraction</span><span class="p">?</span> <span class="n">left</span><span class="p">,</span> <span class="n">Fraction</span><span class="p">?</span> <span class="n">right</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">=&gt;</span> <span class="n">left</span><span class="p">?.</span><span class="n">Value</span> <span class="p">==</span> <span class="n">right</span><span class="p">?.</span><span class="n">Value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">bool</span> <span class="kd">operator</span> <span class="p">!=(</span><span class="n">Fraction</span><span class="p">?</span> <span class="n">left</span><span class="p">,</span> <span class="n">Fraction</span><span class="p">?</span> <span class="n">right</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">=&gt;</span> <span class="p">!(</span><span class="n">left</span> <span class="p">==</span> <span class="n">right</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">bool</span> <span class="kd">operator</span> <span class="p">&lt;(</span><span class="n">Fraction</span> <span class="n">left</span><span class="p">,</span> <span class="n">Fraction</span> <span class="n">right</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">=&gt;</span> <span class="n">left</span><span class="p">.</span><span class="n">Value</span> <span class="p">&lt;</span> <span class="n">right</span><span class="p">.</span><span class="n">Value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">bool</span> <span class="kd">operator</span> <span class="p">&gt;(</span><span class="n">Fraction</span> <span class="n">left</span><span class="p">,</span> <span class="n">Fraction</span> <span class="n">right</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">=&gt;</span> <span class="n">left</span><span class="p">.</span><span class="n">Value</span> <span class="p">&gt;</span> <span class="n">right</span><span class="p">.</span><span class="n">Value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">bool</span> <span class="kd">operator</span> <span class="p">&lt;=(</span><span class="n">Fraction</span> <span class="n">left</span><span class="p">,</span> <span class="n">Fraction</span> <span class="n">right</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">=&gt;</span> <span class="n">left</span><span class="p">.</span><span class="n">Value</span> <span class="p">&lt;=</span> <span class="n">right</span><span class="p">.</span><span class="n">Value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">bool</span> <span class="kd">operator</span> <span class="p">&gt;=(</span><span class="n">Fraction</span> <span class="n">left</span><span class="p">,</span> <span class="n">Fraction</span> <span class="n">right</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">=&gt;</span> <span class="n">left</span><span class="p">.</span><span class="n">Value</span> <span class="p">&gt;=</span> <span class="n">right</span><span class="p">.</span><span class="n">Value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>A &ldquo;Fraction&rdquo; class, overloading addition and comparison operators</p>
<p>Here&rsquo;s another class, this time one that represents a Folder that can hold a collection of file paths.</p>
<p>To implement the <code>IAdditionOperators</code> interface and overload the <code>+</code> operator, I&rsquo;m just creating a new Folder and adding all the files to it.</p>
<p>To implement the <code>IComparisonOperators</code> interface and overload the other operators, I&rsquo;m just using a count of the files. If one Folder has less files, it&rsquo;s considered less. If two Folders have an equal number of files, even if the paths are totally different, they&rsquo;re still equal. That&rsquo;d be problematic in reality, but oh well&hellip; it&rsquo;ll work for what we need to do. :)</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Folder</span> <span class="p">:</span> <span class="n">IAdditionOperators</span><span class="p">&lt;</span><span class="n">Folder</span><span class="p">,</span> <span class="n">Folder</span><span class="p">,</span> <span class="n">Folder</span><span class="p">&gt;,</span>
</span></span><span class="line"><span class="cl">                      <span class="n">IComparisonOperators</span><span class="p">&lt;</span><span class="n">Folder</span><span class="p">,</span> <span class="n">Folder</span><span class="p">,</span> <span class="kt">bool</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Folder</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Folder</span><span class="p">(</span><span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="n">filesA</span><span class="p">,</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="n">filesB</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Files</span><span class="p">.</span><span class="n">AddRange</span><span class="p">(</span><span class="n">filesA</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">Files</span><span class="p">.</span><span class="n">AddRange</span><span class="p">(</span><span class="n">filesB</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="n">Files</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="n">Folder</span> <span class="kd">operator</span> <span class="p">+(</span><span class="n">Folder</span> <span class="n">folder1</span><span class="p">,</span> <span class="n">Folder</span> <span class="n">folder2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">=&gt;</span> <span class="k">new</span><span class="p">(</span><span class="n">folder1</span><span class="p">.</span><span class="n">Files</span><span class="p">,</span> <span class="n">folder2</span><span class="p">.</span><span class="n">Files</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">bool</span> <span class="kd">operator</span> <span class="p">==(</span><span class="n">Folder</span><span class="p">?</span> <span class="n">left</span><span class="p">,</span> <span class="n">Folder</span><span class="p">?</span> <span class="n">right</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">=&gt;</span> <span class="n">left</span><span class="p">?.</span><span class="n">Files</span><span class="p">.</span><span class="n">Count</span> <span class="p">==</span> <span class="n">right</span><span class="p">?.</span><span class="n">Files</span><span class="p">.</span><span class="n">Count</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">bool</span> <span class="kd">operator</span> <span class="p">!=(</span><span class="n">Folder</span><span class="p">?</span> <span class="n">left</span><span class="p">,</span> <span class="n">Folder</span><span class="p">?</span> <span class="n">right</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">=&gt;</span> <span class="p">!(</span><span class="n">left</span> <span class="p">==</span> <span class="n">right</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">bool</span> <span class="kd">operator</span> <span class="p">&lt;(</span><span class="n">Folder</span> <span class="n">left</span><span class="p">,</span> <span class="n">Folder</span> <span class="n">right</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">=&gt;</span> <span class="n">left</span><span class="p">.</span><span class="n">Files</span><span class="p">.</span><span class="n">Count</span> <span class="p">&lt;</span> <span class="n">right</span><span class="p">.</span><span class="n">Files</span><span class="p">.</span><span class="n">Count</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">bool</span> <span class="kd">operator</span> <span class="p">&gt;(</span><span class="n">Folder</span> <span class="n">left</span><span class="p">,</span> <span class="n">Folder</span> <span class="n">right</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">=&gt;</span> <span class="n">right</span><span class="p">.</span><span class="n">Files</span><span class="p">.</span><span class="n">Count</span> <span class="p">&lt;</span> <span class="n">left</span><span class="p">.</span><span class="n">Files</span><span class="p">.</span><span class="n">Count</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">bool</span> <span class="kd">operator</span> <span class="p">&lt;=(</span><span class="n">Folder</span> <span class="n">left</span><span class="p">,</span> <span class="n">Folder</span> <span class="n">right</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">=&gt;</span> <span class="n">left</span><span class="p">.</span><span class="n">Files</span><span class="p">.</span><span class="n">Count</span> <span class="p">&lt;=</span> <span class="n">right</span><span class="p">.</span><span class="n">Files</span><span class="p">.</span><span class="n">Count</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">bool</span> <span class="kd">operator</span> <span class="p">&gt;=(</span><span class="n">Folder</span> <span class="n">left</span><span class="p">,</span> <span class="n">Folder</span> <span class="n">right</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">=&gt;</span> <span class="n">left</span><span class="p">.</span><span class="n">Files</span><span class="p">.</span><span class="n">Count</span> <span class="p">&gt;=</span> <span class="n">right</span><span class="p">.</span><span class="n">Files</span><span class="p">.</span><span class="n">Count</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>A &ldquo;Folder&rdquo; class, overloading the same operators</p>
<p>Once we&rsquo;ve got a couple classes that implement the new interfaces&hellip; what then? Well, we can write utilities against those interfaces, to perform mathematical operations on objects without needing to know what the exact type is ahead of time.</p>
<p>At runtime, when the generic methods below are called, the code will grab the <em>actual</em> implementation of whatever class is being passed to it and figure out what it means to &ldquo;sum&rdquo; up fractions, or find the &ldquo;least&rdquo; box in a collection of boxes - according to how <em>you</em> defined it.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="k">class</span> <span class="nc">Utilities</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="n">T</span> <span class="n">Sum</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;(</span><span class="k">this</span> <span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;</span> <span class="n">items</span><span class="p">)</span> <span class="k">where</span> <span class="n">T</span> <span class="p">:</span> <span class="n">IAdditionOperators</span><span class="p">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">T</span><span class="p">,</span> <span class="n">T</span><span class="p">&gt;,</span> <span class="k">new</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(!</span><span class="n">items</span><span class="p">.</span><span class="n">Any</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="k">new</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">T</span> <span class="n">sum</span> <span class="p">=</span> <span class="n">items</span><span class="p">.</span><span class="n">First</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">item</span> <span class="k">in</span> <span class="n">items</span><span class="p">.</span><span class="n">Skip</span><span class="p">(</span><span class="m">1</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">            <span class="n">sum</span> <span class="p">+=</span> <span class="n">item</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">sum</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="n">T</span><span class="p">?</span> <span class="n">Least</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;(</span><span class="k">this</span> <span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;</span> <span class="n">items</span><span class="p">)</span> <span class="k">where</span> <span class="n">T</span> <span class="p">:</span> <span class="n">IComparisonOperators</span><span class="p">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">T</span><span class="p">,</span> <span class="kt">bool</span><span class="p">&gt;,</span> <span class="k">new</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">T</span><span class="p">?</span> <span class="n">min</span> <span class="p">=</span> <span class="k">default</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">foreach</span> <span class="p">(</span><span class="n">T</span> <span class="n">item</span> <span class="k">in</span> <span class="n">items</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">min</span> <span class="p">==</span> <span class="kc">null</span> <span class="p">||</span> <span class="n">item</span> <span class="p">&lt;</span> <span class="n">min</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="n">min</span> <span class="p">=</span> <span class="n">item</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">min</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Functions for handling math.. in a generic way (dun dun dunnnn)</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="na">[Test]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">CanFindTheSumOfAllTheThings</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">boxes</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Box</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">new</span> <span class="n">Box</span><span class="p">(</span><span class="m">2</span><span class="p">,</span> <span class="m">7</span><span class="p">,</span> <span class="m">2</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="k">new</span> <span class="n">Box</span><span class="p">(</span><span class="m">10</span><span class="p">,</span> <span class="m">10</span><span class="p">,</span> <span class="m">10</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="k">new</span> <span class="n">Box</span><span class="p">(</span><span class="m">3</span><span class="p">,</span> <span class="m">4</span><span class="p">,</span> <span class="m">3</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">folders</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Folder</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">new</span> <span class="n">Folder</span> <span class="p">{</span> <span class="n">Files</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="p">{</span> <span class="s">&#34;c:/file1.txt&#34;</span><span class="p">,</span> <span class="s">&#34;c:/file2.txt&#34;</span> <span class="p">}</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="k">new</span> <span class="n">Folder</span> <span class="p">{</span> <span class="n">Files</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="p">{</span> <span class="s">&#34;d:/fileA.txt&#34;</span><span class="p">,</span> <span class="s">&#34;d:/fileB.txt&#34;</span> <span class="p">}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">fractions</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Fraction</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">new</span> <span class="n">Fraction</span><span class="p">(</span><span class="m">1</span><span class="p">,</span> <span class="m">3</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="k">new</span> <span class="n">Fraction</span><span class="p">(</span><span class="m">2</span><span class="p">,</span> <span class="m">6</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="k">new</span> <span class="n">Fraction</span><span class="p">(</span><span class="m">2</span><span class="p">,</span> <span class="m">5</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">Multiple</span><span class="p">(()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">boxes</span><span class="p">.</span><span class="n">Sum</span><span class="p">().</span><span class="n">Height</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="m">21</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">folders</span><span class="p">.</span><span class="n">Sum</span><span class="p">().</span><span class="n">Files</span><span class="p">,</span> <span class="n">Has</span><span class="p">.</span><span class="n">Count</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="m">4</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">fractions</span><span class="p">.</span><span class="n">Sum</span><span class="p">().</span><span class="n">Numerator</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="m">96</span><span class="p">));</span>  <span class="c1">// 96/90</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">[Test]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">CanFindTheLeastOfAllTheThings</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">boxes</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Box</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">new</span> <span class="n">Box</span><span class="p">(</span><span class="m">2</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="m">2</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="k">new</span> <span class="n">Box</span><span class="p">(</span><span class="m">10</span><span class="p">,</span> <span class="m">10</span><span class="p">,</span> <span class="m">10</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="k">new</span> <span class="n">Box</span><span class="p">(</span><span class="m">3</span><span class="p">,</span> <span class="m">4</span><span class="p">,</span> <span class="m">3</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">folders</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Folder</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">new</span> <span class="n">Folder</span> <span class="p">{</span> <span class="n">Files</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="p">{</span> <span class="s">&#34;c:/file1.txt&#34;</span><span class="p">,</span> <span class="s">&#34;c:/file2.txt&#34;</span><span class="p">,</span> <span class="s">&#34;c:/file3.txt&#34;</span> <span class="p">}</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="k">new</span> <span class="n">Folder</span> <span class="p">{</span> <span class="n">Files</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="p">{</span> <span class="s">&#34;d:/fileA.txt&#34;</span><span class="p">,</span> <span class="s">&#34;d:/fileB.txt&#34;</span> <span class="p">}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">fractions</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Fraction</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">new</span> <span class="n">Fraction</span><span class="p">(</span><span class="m">2</span><span class="p">,</span> <span class="m">3</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="k">new</span> <span class="n">Fraction</span><span class="p">(</span><span class="m">1</span><span class="p">,</span> <span class="m">10</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="k">new</span> <span class="n">Fraction</span><span class="p">(</span><span class="m">3</span><span class="p">,</span> <span class="m">4</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">Multiple</span><span class="p">(()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">boxes</span><span class="p">.</span><span class="n">Least</span><span class="p">().</span><span class="n">Height</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="m">1</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">folders</span><span class="p">.</span><span class="n">Least</span><span class="p">().</span><span class="n">Files</span><span class="p">,</span> <span class="n">Has</span><span class="p">.</span><span class="n">Count</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="m">2</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">fractions</span><span class="p">.</span><span class="n">Least</span><span class="p">().</span><span class="n">Denominator</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="m">10</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>A few tests, to show what&rsquo;s happening</p>
<p>And that&rsquo;s it! That&rsquo;s the new Generic Math feature in C# 11. As I mentioned earlier, <a href="https://github.com/grantwinney/CSharpDotNetExamples/tree/master/C%23%2011/GenericMathSupport/GenericMathSupport"  target="_blank" rel="noreferrer">all the examples here are available on GitHub</a>, if you want to mess with it more.</p>
<p>I found a great video on YouTube demonstrating some of these same concepts, so if that&rsquo;s more your style, <a href="https://www.youtube.com/watch?v=Sclx7F8hFso"  target="_blank" rel="noreferrer">go watch Jasper Kent&rsquo;s tutorial too</a>. Sometimes, seeing something presented in multiple ways drives it home that much more.. at least it does for me.</p>
<p>One thing I didn&rsquo;t show is that you can reference the <a href="https://source.dot.net/#System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/Numerics/INumber.cs,0f558758e750a740"  target="_blank" rel="noreferrer"><code>INumber&lt;T&gt;</code></a> interface, which references most (all?) of the other new interfaces. You&rsquo;ll have to implement dozens and dozens of methods, but then your new class can basically be treated like any other number type in C#. You can read more about that, and Generic Math in general, in the <a href="https://learn.microsoft.com/en-us/dotnet/standard/generics/math"  target="_blank" rel="noreferrer">Microsoft docs</a>.</p>
<p>If you found this content useful, and want to learn more about a variety of <a href="https://grantwinney.com/tags/csharp/"  target="_blank" rel="noreferrer">C#</a> features, check out <a href="https://github.com/grantwinney/CSharpDotNetExamples"  target="_blank" rel="noreferrer">this GitHub repo</a>, where you&rsquo;ll find links to plenty more blog posts and practical examples!</p>
]]></content:encoded><media:content url="https://grantwinney.com/csharp-generic-math-support/feature.webp" medium="image" type="image/webp"/></item><item><title>Overloading arithmetic, equality, and comparison operators in C#</title><link>https://grantwinney.com/csharp-overload-arithmetic-equality-comparison-operators/</link><pubDate>Sat, 01 Apr 2023 03:54:20 +0000</pubDate><guid>https://grantwinney.com/csharp-overload-arithmetic-equality-comparison-operators/</guid><description>What&amp;rsquo;s it mean to overload operators in C#? And what&amp;rsquo;s that have to do with Generic Math in C# 11? Let&amp;rsquo;s find out! (part 2 of 3)</description><content:encoded><![CDATA[<p>This is part of a series building up to the new C# 11 feature called <a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#generic-math-support"  target="_blank" rel="noreferrer">Generic Math</a>. Before tackling that though, let&rsquo;s check out <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/operator-overloading"  target="_blank" rel="noreferrer">overload operators</a> and how they put us in control of how two instances of an object are considered to be related.</p>
<blockquote><p>The code in this post is available on <a href="https://github.com/grantwinney/CSharpDotNetExamples/tree/master/C%23%2011/GenericMathSupport/GenericMathSupport"  target="_blank" rel="noreferrer">GitHub</a>, for you to use, expand upon, or just follow along while you read&hellip; and hopefully discover something new!</p>
</blockquote>
<h2 class="relative group">Arithmetic Operators
    <div id="arithmetic-operators" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#arithmetic-operators" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>By overloading the arithmetic operators, you get to decide what it means to add two objects together. In Microsoft&rsquo;s own <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/operator-overloading"  target="_blank" rel="noreferrer">example</a>, they create a Fraction struct that defines what should happen when two fractions are added together, subtracted, etc.</p>
<p>Let&rsquo;s define a Box class instead. We can decide to say that, if two boxes are added together, you should get back a box that could fit both of them. It&rsquo;ll take the larger width and depth of the two boxes, and then combine their heights, so you can stack both smaller boxes inside the larger one. Notice how the overloaded operator is a static method that takes the two instance you want to compare (add, in this case).</p>
<p>This is incredibly <em>not</em> optimal, but judging by some of the packages I&rsquo;ve gotten from Amazon with very small items inside, I might not be that far off from the industry standard. 😅</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Box</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Box</span><span class="p">(</span><span class="kt">int</span> <span class="n">width</span><span class="p">,</span> <span class="kt">int</span> <span class="n">height</span><span class="p">,</span> <span class="kt">int</span> <span class="n">depth</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Width</span> <span class="p">=</span> <span class="n">width</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Height</span> <span class="p">=</span> <span class="n">height</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Depth</span> <span class="p">=</span> <span class="n">depth</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">Width</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="kd">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">Height</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="kd">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">Depth</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="kd">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="n">Box</span> <span class="kd">operator</span> <span class="p">+(</span><span class="n">Box</span> <span class="n">box1</span><span class="p">,</span> <span class="n">Box</span> <span class="n">box2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">widestWidth</span> <span class="p">=</span> <span class="n">Math</span><span class="p">.</span><span class="n">Max</span><span class="p">(</span><span class="n">box1</span><span class="p">.</span><span class="n">Width</span><span class="p">,</span> <span class="n">box2</span><span class="p">.</span><span class="n">Width</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">deepestDepth</span> <span class="p">=</span> <span class="n">Math</span><span class="p">.</span><span class="n">Max</span><span class="p">(</span><span class="n">box1</span><span class="p">.</span><span class="n">Depth</span><span class="p">,</span> <span class="n">box2</span><span class="p">.</span><span class="n">Depth</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">combinedHeight</span> <span class="p">=</span> <span class="n">box1</span><span class="p">.</span><span class="n">Height</span> <span class="p">+</span> <span class="n">box2</span><span class="p">.</span><span class="n">Height</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">new</span> <span class="n">Box</span><span class="p">(</span><span class="n">widestWidth</span><span class="p">,</span> <span class="n">combinedHeight</span><span class="p">,</span> <span class="n">deepestDepth</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">[Test]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">AddingTwoBoxes_ReturnsBoxToFitBoth</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">box1</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Box</span><span class="p">(</span><span class="m">2</span><span class="p">,</span> <span class="m">3</span><span class="p">,</span> <span class="m">7</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">box2</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Box</span><span class="p">(</span><span class="m">4</span><span class="p">,</span> <span class="m">6</span><span class="p">,</span> <span class="m">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">box3</span> <span class="p">=</span> <span class="n">box1</span> <span class="p">+</span> <span class="n">box2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">Multiple</span><span class="p">(()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">box3</span><span class="p">.</span><span class="n">Width</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="m">4</span><span class="p">));</span>   <span class="c1">// larger width from box2</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">box3</span><span class="p">.</span><span class="n">Depth</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="m">7</span><span class="p">));</span>   <span class="c1">// larger depth from box1</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">box3</span><span class="p">.</span><span class="n">Height</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="m">9</span><span class="p">));</span>  <span class="c1">// combined height</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>How about another? A Folder class this time, that can hold a list of files.. If someone tries to add two Folders together, they get back a new Folder with <em>all</em> the files in it, like this.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Folder</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Folder</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Folder</span><span class="p">(</span><span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="n">filesA</span><span class="p">,</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="n">filesB</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Files</span><span class="p">.</span><span class="n">AddRange</span><span class="p">(</span><span class="n">filesA</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">Files</span><span class="p">.</span><span class="n">AddRange</span><span class="p">(</span><span class="n">filesB</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="n">Files</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="n">Folder</span> <span class="kd">operator</span> <span class="p">+(</span><span class="n">Folder</span> <span class="n">fdr1</span><span class="p">,</span> <span class="n">Folder</span> <span class="n">fdr2</span><span class="p">)</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="k">new</span><span class="p">(</span><span class="n">fdr1</span><span class="p">.</span><span class="n">Files</span><span class="p">,</span> <span class="n">fdr2</span><span class="p">.</span><span class="n">Files</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">[Test]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">AddingTwoFolders_CombinesTheirFiles</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">folder1</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Folder</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Files</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="p">{</span> <span class="s">&#34;c:/file1.txt&#34;</span><span class="p">,</span> <span class="s">&#34;c:/file2.txt&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">folder2</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Folder</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Files</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="p">{</span> <span class="s">&#34;d:/fileA.txt&#34;</span><span class="p">,</span> <span class="s">&#34;d:/fileB.txt&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">bigFolder</span> <span class="p">=</span> <span class="n">folder1</span> <span class="p">+</span> <span class="n">folder2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">Multiple</span><span class="p">(()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">bigFolder</span><span class="p">.</span><span class="n">Files</span><span class="p">,</span> <span class="n">Has</span><span class="p">.</span><span class="n">Count</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="m">4</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">bigFolder</span><span class="p">.</span><span class="n">Files</span><span class="p">.</span><span class="n">SingleOrDefault</span><span class="p">(</span><span class="n">f</span> <span class="p">=&gt;</span> <span class="n">f</span><span class="p">.</span><span class="n">Contains</span><span class="p">(</span><span class="s">&#34;file2&#34;</span><span class="p">)),</span> <span class="n">Is</span><span class="p">.</span><span class="n">Not</span><span class="p">.</span><span class="n">Null</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">bigFolder</span><span class="p">.</span><span class="n">Files</span><span class="p">.</span><span class="n">SingleOrDefault</span><span class="p">(</span><span class="n">f</span> <span class="p">=&gt;</span> <span class="n">f</span><span class="p">.</span><span class="n">Contains</span><span class="p">(</span><span class="s">&#34;fileA&#34;</span><span class="p">)),</span> <span class="n">Is</span><span class="p">.</span><span class="n">Not</span><span class="p">.</span><span class="n">Null</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Both of the above examples overloaded the <code>+</code> operator, but there&rsquo;s no reason you can&rsquo;t overload a different operator.. or all of them. We&rsquo;ll do one more with arithmetic, this time overloading the <code>*</code> operator.</p>
<p>How about a class that can hold shades of colors, kind of like a <a href="https://sureswatch.com/utilizing-paint-swatches-to-revamp-your-house/"  target="_blank" rel="noreferrer">paint swatch</a>? Multiplying two of them together should combine all the colors from one swatch with colors from the other, so 3 colors on one side and 3 on the other produces 9 &ldquo;mixed&rdquo; colors.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">ColorSwatch</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">Color</span><span class="p">&gt;</span> <span class="n">Shades</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="kd">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">ColorSwatch</span><span class="p">(</span><span class="kt">string</span> <span class="n">name</span><span class="p">,</span> <span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">Color</span><span class="p">&gt;</span> <span class="n">shades</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Name</span> <span class="p">=</span> <span class="n">name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Shades</span> <span class="p">=</span> <span class="n">shades</span><span class="p">.</span><span class="n">ToList</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="n">IList</span><span class="p">&lt;</span><span class="n">Color</span><span class="p">&gt;</span> <span class="kd">operator</span> <span class="p">*(</span><span class="n">ColorSwatch</span> <span class="n">cs1</span><span class="p">,</span> <span class="n">ColorSwatch</span> <span class="n">cs2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">colorMatrix</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Color</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">amount</span> <span class="p">=</span> <span class="m">0.5</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">s1</span> <span class="k">in</span> <span class="n">cs1</span><span class="p">.</span><span class="n">Shades</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">s2</span> <span class="k">in</span> <span class="n">cs2</span><span class="p">.</span><span class="n">Shades</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="c1">// Thank you Timwi.. stackoverflow.com/a/3722337</span>
</span></span><span class="line"><span class="cl">                <span class="kt">byte</span> <span class="n">r</span> <span class="p">=</span> <span class="p">(</span><span class="kt">byte</span><span class="p">)(</span><span class="n">s1</span><span class="p">.</span><span class="n">R</span> <span class="p">*</span> <span class="n">amount</span> <span class="p">+</span> <span class="n">s2</span><span class="p">.</span><span class="n">R</span> <span class="p">*</span> <span class="p">(</span><span class="m">1</span> <span class="p">-</span> <span class="n">amount</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">                <span class="kt">byte</span> <span class="n">g</span> <span class="p">=</span> <span class="p">(</span><span class="kt">byte</span><span class="p">)(</span><span class="n">s1</span><span class="p">.</span><span class="n">G</span> <span class="p">*</span> <span class="n">amount</span> <span class="p">+</span> <span class="n">s2</span><span class="p">.</span><span class="n">G</span> <span class="p">*</span> <span class="p">(</span><span class="m">1</span> <span class="p">-</span> <span class="n">amount</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">                <span class="kt">byte</span> <span class="n">b</span> <span class="p">=</span> <span class="p">(</span><span class="kt">byte</span><span class="p">)(</span><span class="n">s1</span><span class="p">.</span><span class="n">B</span> <span class="p">*</span> <span class="n">amount</span> <span class="p">+</span> <span class="n">s2</span><span class="p">.</span><span class="n">B</span> <span class="p">*</span> <span class="p">(</span><span class="m">1</span> <span class="p">-</span> <span class="n">amount</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">                <span class="n">colorMatrix</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="n">Color</span><span class="p">.</span><span class="n">FromArgb</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">g</span><span class="p">,</span> <span class="n">b</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">colorMatrix</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>This one&rsquo;s a little tougher to imagine the result of, so I wrote a quick little app that creates a colored square for each of the shades, and then a square for each &ldquo;mixed&rdquo; color. I won&rsquo;t throw the code for that here, but it&rsquo;s on <a href="https://github.com/grantwinney/CSharpDotNetExamples/tree/master/C%23%2011/GenericMathSupport/GenericMathSupport"  target="_blank" rel="noreferrer">GitHub</a> too if you want to see it.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/csharp-overload-arithmetic-equality-comparison-operators/mix-colors-app.png"
    width="314"
      height="336"></figure>

<h2 class="relative group">Comparison Operators
    <div id="comparison-operators" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#comparison-operators" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There&rsquo;s comparison operators too, like greater than and less than. Using the Box class again, let&rsquo;s decide that a box is <em>less than</em> another box if it&rsquo;s area is smaller; likewise, it&rsquo;s <em>greater than</em> another box if it&rsquo;s area is larger.</p>
<p>This could be represented like this, where the operators are again static method that take two &ldquo;box&rdquo; objects to compare.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Box</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Box</span><span class="p">(</span><span class="kt">int</span> <span class="n">width</span><span class="p">,</span> <span class="kt">int</span> <span class="n">height</span><span class="p">,</span> <span class="kt">int</span> <span class="n">depth</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Width</span> <span class="p">=</span> <span class="n">width</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Height</span> <span class="p">=</span> <span class="n">height</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Depth</span> <span class="p">=</span> <span class="n">depth</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">Width</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="kd">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">Height</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="kd">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">Depth</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="kd">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">Area</span> <span class="p">{</span> <span class="k">get</span> <span class="p">=&gt;</span> <span class="n">Width</span> <span class="p">*</span> <span class="n">Height</span> <span class="p">*</span> <span class="n">Depth</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">bool</span> <span class="kd">operator</span> <span class="p">&lt;(</span><span class="n">Box</span> <span class="n">box1</span><span class="p">,</span> <span class="n">Box</span> <span class="n">box2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">=&gt;</span> <span class="n">box1</span><span class="p">.</span><span class="n">Area</span> <span class="p">&lt;</span> <span class="n">box2</span><span class="p">.</span><span class="n">Area</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">bool</span> <span class="kd">operator</span> <span class="p">&gt;(</span><span class="n">Box</span> <span class="n">box1</span><span class="p">,</span> <span class="n">Box</span> <span class="n">box2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">=&gt;</span> <span class="n">box2</span><span class="p">.</span><span class="n">Area</span> <span class="p">&lt;</span> <span class="n">box1</span><span class="p">.</span><span class="n">Area</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">[Test]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">Box1IsLessThanBox2_WhenAreaIsSmaller</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">box1</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Box</span><span class="p">(</span><span class="m">2</span><span class="p">,</span> <span class="m">3</span><span class="p">,</span> <span class="m">7</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">box2</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Box</span><span class="p">(</span><span class="m">2</span><span class="p">,</span> <span class="m">4</span><span class="p">,</span> <span class="m">7</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">Multiple</span><span class="p">(()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">box1</span> <span class="p">&lt;</span> <span class="n">box2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">box2</span> <span class="p">&gt;</span> <span class="n">box1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>There&rsquo;s other comparison (aka relational) operators too, but you probably get the point by now.</p>

<h2 class="relative group">Equality Operators
    <div id="equality-operators" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#equality-operators" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Let&rsquo;s check out equality operators too, and then call it a day. :)</p>
<p>By overriding the <code>==</code> and <code>!=</code> operators, you can decide what makes two instances equal. Building off of Microsoft&rsquo;s example with the <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/operator-overloading"  target="_blank" rel="noreferrer">Fraction struct</a>, you might do something like this. Of course this isn&rsquo;t very robust since 1/2 and 2/4 won&rsquo;t be seen as equal, but you get the idea.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="kt">bool</span> <span class="kd">operator</span> <span class="p">==(</span><span class="n">Fraction</span> <span class="n">a</span><span class="p">,</span> <span class="n">Fraction</span> <span class="n">b</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">=&gt;</span> <span class="n">a</span><span class="p">.</span><span class="n">num</span> <span class="p">==</span> <span class="n">b</span><span class="p">.</span><span class="n">num</span> <span class="p">&amp;&amp;</span> <span class="n">a</span><span class="p">.</span><span class="n">den</span> <span class="p">==</span> <span class="n">b</span><span class="p">.</span><span class="n">den</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="kt">bool</span> <span class="kd">operator</span> <span class="p">!=(</span><span class="n">Fraction</span> <span class="n">a</span><span class="p">,</span> <span class="n">Fraction</span> <span class="n">b</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">=&gt;</span> <span class="p">!(</span><span class="n">a</span> <span class="p">==</span> <span class="n">b</span><span class="p">)</span></span></span></code></pre></div></div>
<p>Or maybe in that same <code>Box</code> class again, you want to be able to compare two boxes to see if they&rsquo;re the same size. We could compare the area again like above, or maybe a different approach is to just check all 3 dimensions to see if they&rsquo;re the same size, like this. Not very robust, but it&rsquo;ll do for our purpose!</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Box</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Box</span><span class="p">(</span><span class="kt">int</span> <span class="n">width</span><span class="p">,</span> <span class="kt">int</span> <span class="n">height</span><span class="p">,</span> <span class="kt">int</span> <span class="n">depth</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Width</span> <span class="p">=</span> <span class="n">width</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Height</span> <span class="p">=</span> <span class="n">height</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Depth</span> <span class="p">=</span> <span class="n">depth</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">bool</span> <span class="kd">operator</span> <span class="p">==(</span><span class="n">Box</span> <span class="n">box1</span><span class="p">,</span> <span class="n">Box</span> <span class="n">box2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">=&gt;</span> <span class="n">box1</span><span class="p">.</span><span class="n">Width</span> <span class="p">==</span> <span class="n">box2</span><span class="p">.</span><span class="n">Width</span> <span class="p">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">           <span class="n">box1</span><span class="p">.</span><span class="n">Height</span> <span class="p">==</span> <span class="n">box2</span><span class="p">.</span><span class="n">Height</span> <span class="p">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">           <span class="n">box1</span><span class="p">.</span><span class="n">Depth</span> <span class="p">==</span> <span class="n">box2</span><span class="p">.</span><span class="n">Depth</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">bool</span> <span class="kd">operator</span> <span class="p">!=(</span><span class="n">Box</span> <span class="n">box1</span><span class="p">,</span> <span class="n">Box</span> <span class="n">box2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">=&gt;</span> <span class="p">!(</span><span class="n">box1</span> <span class="p">==</span> <span class="n">box2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">Width</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="kd">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">Height</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="kd">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">Depth</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="kd">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>I wrote a longer post about equality operators a few years ago too, if you want to read more about it and see some different examples.</p>
<p><a href="https://grantwinney.com/csharp-compare-two-objects-for-equality/"  target="_blank" rel="noreferrer">Comparing Two Objects for Equality in C#</a></p>
<p>Next up, we&rsquo;ll see how operation overloading and the <a href="https://grantwinney.com/whats-a-static-abstract-interface-method-in-c/"  target="_blank" rel="noreferrer">static abstract concept</a> applies to <a href="https://grantwinney.com/csharp-generic-math-support/"  target="_blank" rel="noreferrer">Generic Math</a>.</p>
<p>If you found this content useful, and want to learn more about a variety of <a href="https://grantwinney.com/tags/csharp/"  target="_blank" rel="noreferrer">C#</a> features, check out <a href="https://github.com/grantwinney/CSharpDotNetExamples"  target="_blank" rel="noreferrer">this GitHub repo</a>, where you&rsquo;ll find links to plenty more blog posts and practical examples!</p>
]]></content:encoded><media:content url="https://grantwinney.com/csharp-overload-arithmetic-equality-comparison-operators/feature.webp" medium="image" type="image/webp"/></item><item><title>What is a static abstract interface method in C#?</title><link>https://grantwinney.com/whats-a-static-abstract-interface-method-in-c/</link><pubDate>Fri, 31 Mar 2023 03:59:47 +0000</pubDate><guid>https://grantwinney.com/whats-a-static-abstract-interface-method-in-c/</guid><description>What are static abstract members (new in C# 11), what can we do with them, and how are they related to Generic Math? (part 1 of 3)</description><content:encoded><![CDATA[<p>This is part of a series building up to the new C# 11 feature called <a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#generic-math-support"  target="_blank" rel="noreferrer">Generic Math</a>. Before tackling that though, let&rsquo;s check out another new C# 11 feature called the <a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/static-virtual-interface-members"  target="_blank" rel="noreferrer">static abstract interface method</a> (aka static virtual members), and compare it to what we&rsquo;ve had up until now.</p>
<blockquote><p>The code in this article is available on <a href="https://github.com/grantwinney/CSharpDotNetExamples/tree/master/C%23%2011/GenericMathSupport/GenericMathSupport"  target="_blank" rel="noreferrer">GitHub</a>, if you&rsquo;d like to use it in your own projects or just follow along while you read.</p>
</blockquote>
<h2 class="relative group">The interfaces we know and love
    <div id="the-interfaces-we-know-and-love" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-interfaces-we-know-and-love" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>We use interfaces a lot in C#. They&rsquo;re essentially contracts, so if a class implements a particular interface, then you can be confident that the class includes all the properties and methods defined in that interface. In the following example, <code>EmployeeReport</code> implements everything in <code>IEmployeeReport</code>, and <code>VendorReport</code> implements everything in <code>IVendorReport</code>.</p>
<p>Interfaces can extend one another too, like the two interfaces below are doing with <code>IBaseReport</code>. The classes need to implement everything in that base interface too, like the ones below are doing by defining <code>ReportName</code> and <code>IsSensitive</code>.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="cm">/******************
</span></span></span><span class="line"><span class="cl"><span class="cm"> * BASE REPORT INTERFACE
</span></span></span><span class="line"><span class="cl"><span class="cm"> * *****************/</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">interface</span> <span class="nc">IBaseReport</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span> <span class="n">ReportName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">IsSensitive</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/******************
</span></span></span><span class="line"><span class="cl"><span class="cm"> * EMPLOYEE REPORT w/ INTERFACE
</span></span></span><span class="line"><span class="cl"><span class="cm"> * *****************/</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">interface</span> <span class="nc">IEmployeeReport</span> <span class="p">:</span> <span class="n">IBaseReport</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">DateTime</span> <span class="n">HireDate</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">DateTime</span><span class="p">?</span> <span class="n">TermDate</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">EmployeeReport</span> <span class="p">:</span> <span class="n">IEmployeeReport</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">ReportName</span> <span class="p">=&gt;</span> <span class="s">&#34;Employee Profile&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">bool</span> <span class="n">IsSensitive</span> <span class="p">=&gt;</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">DateTime</span> <span class="n">HireDate</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">DateTime</span><span class="p">?</span> <span class="n">TermDate</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/******************
</span></span></span><span class="line"><span class="cl"><span class="cm"> * VENDOR REPORT CLASS w/ INTERFACE
</span></span></span><span class="line"><span class="cl"><span class="cm"> * *****************/</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">interface</span> <span class="nc">IVendorReport</span> <span class="p">:</span> <span class="n">IBaseReport</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span> <span class="n">VendorName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span> <span class="n">VendorContactName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span> <span class="n">VendorContactPhone</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">VendorReport</span> <span class="p">:</span> <span class="n">IVendorReport</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">ReportName</span> <span class="p">=&gt;</span> <span class="s">&#34;Vendor Summary&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">bool</span> <span class="n">IsSensitive</span> <span class="p">=&gt;</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">VendorName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">VendorContactName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">VendorContactPhone</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Any method that might do something with one of these reports, can operate on the interface instead. The <code>GetReportInfo</code> method below is assured that anything implementing <code>IBaseReport</code> has 2 properties on it. And bonus - we don&rsquo;t need to define a method for each type of report, just one which can handle any report.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="cm">/******************
</span></span></span><span class="line"><span class="cl"><span class="cm"> * A CLASS THAT PROCESSES REPORTS
</span></span></span><span class="line"><span class="cl"><span class="cm"> * *****************/</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">ISwearImAnInterestingClass</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">GetVendorReportStatus</span><span class="p">(</span><span class="n">IVendorReport</span> <span class="n">rpt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// i.e. Running &#39;Vendor Summary&#39; for: Acme Inc</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s">$&#34;Running &#39;{rpt.ReportName}&#39; for: {rpt.VendorName}&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">GetReportInfo</span><span class="p">(</span><span class="n">IBaseReport</span> <span class="n">rpt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// i.e. Employee Profile is a sensitive report.</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s">$&#34;{rpt.ReportName} {(rpt.IsSensitive ? &#34;</span><span class="k">is</span><span class="s">&#34; : &#34;</span><span class="k">is</span> <span class="n">not</span><span class="s">&#34;)} a sensitive report.&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Aside from acting as a contract and helping us write <a href="https://deviq.com/principles/dont-repeat-yourself"  target="_blank" rel="noreferrer">DRY code</a>, interfaces are useful for unit testing too. If you want to learn more about that, I&rsquo;ve written before about how <a href="https://grantwinney.com/what-is-mocking-a-dependency/"  target="_blank" rel="noreferrer">interfaces help with mocking dependencies</a> when unit testing.</p>
<p>However, something we <em>can&rsquo;t</em> do with interfaces is define a static member and have classes implement those. For instance, if you knew you wanted every kind of report to have a <code>ReportName</code>, but you didn&rsquo;t want to have to instantiate a report just to get to a name that never changes, you couldn&rsquo;t do something like this&hellip;</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="cm">/******************
</span></span></span><span class="line"><span class="cl"><span class="cm"> * BASE REPORT INTERFACE
</span></span></span><span class="line"><span class="cl"><span class="cm"> * *****************/</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">interface</span> <span class="nc">IBaseReport</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">static</span> <span class="kt">string</span> <span class="n">ReportName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">IsSensitive</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The other method, the one that references the interface to get the <code>ReportName</code>, will suggest you use an actual instance to get to the static member. Ok sure&hellip; except this is an interface so you <em>can&rsquo;t</em> just instantiate it. Maybe you could add some code in the <code>GetReportInfo</code> method below, to check for every possible report type that implements <code>IBaseReport</code> but then that&rsquo;d make for some repetitive, hard-to-maintain code.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/whats-a-static-abstract-interface-method-in-c/image-38.png"
    width="790"
      height="301"></figure>
<p>On top of that, the classes that implement the <code>IBaseReport</code> interface don&rsquo;t have to include that static member to satisfy the contract with the interface anymore. This <em>(which doesn&rsquo;t define ReportName)</em> won&rsquo;t throw a compilation error:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">EmployeeReport</span> <span class="p">:</span> <span class="n">IEmployeeReport</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">bool</span> <span class="n">IsSensitive</span> <span class="p">=&gt;</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">DateTime</span> <span class="n">HireDate</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">DateTime</span><span class="p">?</span> <span class="n">TermDate</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Okay, enough about interfaces&hellip;</p>

<h2 class="relative group">The new and improved interfaces
    <div id="the-new-and-improved-interfaces" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-new-and-improved-interfaces" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Just kidding! <em>More interfaces!</em></p>
<p>The <a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/static-virtual-interface-members"  target="_blank" rel="noreferrer">static abstract</a> concept seems to have been mostly added to support <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/operator-overloading"  target="_blank" rel="noreferrer">overloaded operators</a> and the other new concept of <a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#generic-math-support"  target="_blank" rel="noreferrer">generic math</a>, but let&rsquo;s take a look at what else we can do with it, without muddying the waters too much.</p>
<p>I&rsquo;ve changed the <code>IBaseReport</code> interface (below) to use the new <a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/static-virtual-interface-members"  target="_blank" rel="noreferrer">static abstract</a> modifiers. For good measure, I&rsquo;ve added in a <code>static abstract</code> method to show that those can be static too, and a normal property that&rsquo;s not static to demonstrate that we can have a mix.</p>
<p>Here&rsquo;s a few things to look for and keep in mind as you check out the similar, but not-quite-the-same block of code, below:</p>
<ul>
<li>The classes are <em>required</em> to implement the static abstract members.</li>
<li>The classes are required to implement the <code>GenerateUniqueId</code> method too, but <em>how</em> they choose to implement that can differ wildly per class.</li>
<li>The <code>GetReportInfo</code> method in that last class, and the <code>GetNewReportId</code> method I threw in too, are implemented differently than before. They&rsquo;re capable of accessing the normal members of an instance, as well as the static members of the class too.</li>
</ul>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="cm">/******************
</span></span></span><span class="line"><span class="cl"><span class="cm"> * BASE REPORT INTERFACE WITH
</span></span></span><span class="line"><span class="cl"><span class="cm"> * STATIC ABSTRACT MEMBERS
</span></span></span><span class="line"><span class="cl"><span class="cm"> * *****************/</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">interface</span> <span class="nc">IBaseReport</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">static</span> <span class="kd">abstract</span> <span class="kt">string</span> <span class="n">ReportName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">static</span> <span class="kd">abstract</span> <span class="kt">bool</span> <span class="n">IsSensitive</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">static</span> <span class="kd">abstract</span> <span class="kt">string</span> <span class="n">GenerateUniqueId</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">DateTime</span> <span class="n">RequestedTime</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/******************
</span></span></span><span class="line"><span class="cl"><span class="cm"> * EMPLOYEE REPORT w/ INTERFACE
</span></span></span><span class="line"><span class="cl"><span class="cm"> * AND IMPLEMENTING STATIC MEMBERS
</span></span></span><span class="line"><span class="cl"><span class="cm"> * *****************/</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">interface</span> <span class="nc">IEmployeeReport</span> <span class="p">:</span> <span class="n">IBaseReport</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">DateTime</span> <span class="n">HireDate</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">DateTime</span><span class="p">?</span> <span class="n">TermDate</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">EmployeeReport</span> <span class="p">:</span> <span class="n">IEmployeeReport</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">string</span> <span class="n">ReportName</span> <span class="p">=&gt;</span> <span class="s">&#34;Employee Profile&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">bool</span> <span class="n">IsSensitive</span> <span class="p">=&gt;</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">DateTime</span> <span class="n">RequestedTime</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">string</span> <span class="n">GenerateUniqueId</span><span class="p">()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="s">$&#34;{ReportName.Replace(&#34;</span> <span class="s">&#34;,&#34;&#34;)}-{DateTime.Now:yyyy-MM-dd-hh-mm-ss:ffffff}&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">DateTime</span> <span class="n">HireDate</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">DateTime</span><span class="p">?</span> <span class="n">TermDate</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/******************
</span></span></span><span class="line"><span class="cl"><span class="cm"> * VENDOR REPORT CLASS w/ INTERFACE
</span></span></span><span class="line"><span class="cl"><span class="cm"> * AND IMPLEMENTING STATIC MEMBERS
</span></span></span><span class="line"><span class="cl"><span class="cm"> * *****************/</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">interface</span> <span class="nc">IVendorReport</span> <span class="p">:</span> <span class="n">IBaseReport</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span> <span class="n">VendorName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span> <span class="n">VendorContactName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span> <span class="n">VendorContactPhone</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">VendorReport</span> <span class="p">:</span> <span class="n">IVendorReport</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">string</span> <span class="n">ReportName</span> <span class="p">=&gt;</span> <span class="s">&#34;Vendor Summary&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">bool</span> <span class="n">IsSensitive</span> <span class="p">=&gt;</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">DateTime</span> <span class="n">RequestedTime</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">string</span> <span class="n">GenerateUniqueId</span><span class="p">()</span> <span class="p">=&gt;</span> <span class="s">$&#34;{Guid.NewGuid()}&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">VendorName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">VendorContactName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">VendorContactPhone</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/******************
</span></span></span><span class="line"><span class="cl"><span class="cm"> * A CLASS THAT PROCESSES REPORTS
</span></span></span><span class="line"><span class="cl"><span class="cm"> * *****************/</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">ISwearImAnInterestingClass</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">GetVendorReportStatus</span><span class="p">(</span><span class="n">IVendorReport</span> <span class="n">rpt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s">$&#34;Running &#39;{VendorReport.ReportName}&#39; for: {rpt.VendorName}&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">GetReportInfo</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;(</span><span class="n">T</span> <span class="n">rpt</span><span class="p">)</span> <span class="k">where</span> <span class="n">T</span> <span class="p">:</span> <span class="n">IBaseReport</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s">$&#34;{T.ReportName} was requested on {rpt.RequestedTime:d}.&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">GetNewReportId</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;()</span> <span class="k">where</span> <span class="n">T</span> <span class="p">:</span> <span class="n">IBaseReport</span>
</span></span><span class="line"><span class="cl">        <span class="p">=&gt;</span> <span class="n">T</span><span class="p">.</span><span class="n">GenerateUniqueId</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h2 class="relative group">What about unit tests?
    <div id="what-about-unit-tests" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-about-unit-tests" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Of course, it&rsquo;s always a good idea to create some tests <em>(and if you&rsquo;re using WinForms, you might want to brush up on</em> <a href="https://grantwinney.com/its-possible-to-test-a-winforms-app-using-mvp/"  target="_blank" rel="noreferrer"><em>using MVP to help with testing</em></a><em>)</em> to make sure everything looks good, and to prove that the static abstract interface members, as weird as they might look, <em>do</em> actually work.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">ISwearImAnInterestingClass</span> <span class="n">ic</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">[SetUp]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">Setup</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">ic</span> <span class="p">=</span> <span class="k">new</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">[Test]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">ISwearImAnInterestingClass_ReturnsExpectedValues_ForEmployeeReport</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">employeeReport</span> <span class="p">=</span> <span class="k">new</span> <span class="n">EmployeeReport</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Bob&#34;</span><span class="p">,</span> <span class="n">HireDate</span> <span class="p">=</span> <span class="k">new</span> <span class="n">DateTime</span><span class="p">(</span><span class="m">2010</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="m">1</span><span class="p">),</span> <span class="n">RequestedTime</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">Multiple</span><span class="p">(()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">ic</span><span class="p">.</span><span class="n">GetReportInfo</span><span class="p">(</span><span class="n">employeeReport</span><span class="p">),</span> <span class="n">Does</span><span class="p">.</span><span class="n">StartWith</span><span class="p">(</span><span class="s">$&#34;{EmployeeReport.ReportName} was requested on &#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">ic</span><span class="p">.</span><span class="n">GetNewReportId</span><span class="p">&lt;</span><span class="n">EmployeeReport</span><span class="p">&gt;(),</span> <span class="n">Does</span><span class="p">.</span><span class="n">StartWith</span><span class="p">(</span><span class="s">&#34;EmployeeProfile-20&#34;</span><span class="p">));</span>  <span class="c1">// will fail in 2100 :p</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">[Test]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">ISwearImAnInterestingClass_ReturnsExpectedValues_ForVendorReport</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">vendorReport</span> <span class="p">=</span> <span class="k">new</span> <span class="n">VendorReport</span> <span class="p">{</span> <span class="n">VendorName</span> <span class="p">=</span> <span class="s">&#34;Acme Inc&#34;</span><span class="p">,</span> <span class="n">VendorContactName</span> <span class="p">=</span> <span class="s">&#34;Mary&#34;</span><span class="p">,</span> <span class="n">RequestedTime</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">Multiple</span><span class="p">(()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">ic</span><span class="p">.</span><span class="n">GetVendorReportStatus</span><span class="p">(</span><span class="n">vendorReport</span><span class="p">),</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="s">$&#34;Running &#39;{VendorReport.ReportName}&#39; for: {vendorReport.VendorName}&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">ic</span><span class="p">.</span><span class="n">GetReportInfo</span><span class="p">(</span><span class="n">vendorReport</span><span class="p">),</span> <span class="n">Does</span><span class="p">.</span><span class="n">StartWith</span><span class="p">(</span><span class="s">$&#34;{VendorReport.ReportName} was requested on &#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">Guid</span><span class="p">.</span><span class="n">TryParse</span><span class="p">(</span><span class="n">ic</span><span class="p">.</span><span class="n">GetNewReportId</span><span class="p">&lt;</span><span class="n">VendorReport</span><span class="p">&gt;(),</span> <span class="k">out</span> <span class="kt">var</span> <span class="n">_</span><span class="p">),</span> <span class="n">Is</span><span class="p">.</span><span class="n">True</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Unfortunately, this new construct doesn&rsquo;t seem to play nicely yet with the popular <a href="https://github.com/Moq/moq4?ref=grant-winney"  target="_blank" rel="noreferrer">Moq</a> framework that helps you mock out interface calls during testing. I&rsquo;m most familiar with Moq, but maybe <a href="https://www.telerik.com/products/mocking.aspx?ref=grant-winney"  target="_blank" rel="noreferrer">JustMock</a> or <a href="http://www.typemock.com/?ref=grant-winney"  target="_blank" rel="noreferrer">TypeMock</a> supports it?</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/whats-a-static-abstract-interface-method-in-c/image-39.png"
    width="985"
      height="196"></figure>
<p>Next up, we&rsquo;ll check out another C# feature we&rsquo;ve had for a long time - <a href="https://grantwinney.com/csharp-overload-arithmetic-equality-comparison-operators/"  target="_blank" rel="noreferrer">operator overloading</a>. After that, we&rsquo;ll take a closer look at what this <a href="https://grantwinney.com/csharp-generic-math-support/"  target="_blank" rel="noreferrer">Generic Math</a> thing is all about.</p>
<p>If you found this content useful, and want to learn more about a variety of <a href="https://grantwinney.com/tags/csharp/"  target="_blank" rel="noreferrer">C#</a> features, check out <a href="https://github.com/grantwinney/CSharpDotNetExamples"  target="_blank" rel="noreferrer">this GitHub repo</a>, where you&rsquo;ll find links to plenty more blog posts and practical examples!</p>
]]></content:encoded><media:content url="https://grantwinney.com/whats-a-static-abstract-interface-method-in-c/feature.webp" medium="image" type="image/webp"/></item><item><title>What is the point of points?</title><link>https://grantwinney.com/whats-the-point-of-points/</link><pubDate>Wed, 22 Mar 2023 10:32:44 +0000</pubDate><guid>https://grantwinney.com/whats-the-point-of-points/</guid><description>Points aren&amp;rsquo;t hours, but they sorta represent hours. Or do they? 🤔 If you&amp;rsquo;re as perplexed as I used to be, here&amp;rsquo;s a few thoughts about points.</description><content:encoded><![CDATA[<p>A quick disclaimer - everything that follows is just one developer&rsquo;s experience (guess whose lol), having been in agile/scrum environments of varying degrees for the past decade. I say varying degrees because everyone seems to do it slightly differently, which is good actually. One size seldom fits all.</p>
<p>The first time I was part of a team that tried using points to plan some upcoming work, I didn&rsquo;t get it <em>at all</em>. It was a few years into my first dev job, and I had enough experience to grab a card and dig in, but not enough to see (or to be honest, care) how my piece might fit into some larger puzzle.</p>
<p>As the years have gone by, and other places I&rsquo;ve worked have used points too, I&rsquo;ve grown to appreciate what they can do for planning. I don&rsquo;t know what the agile trainers, coaches, and other &ldquo;official&rdquo; sources say, but here&rsquo;s what I think the point (sorry, not sorry) of points are.</p>

<h2 class="relative group">Building in wiggle room
    <div id="building-in-wiggle-room" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#building-in-wiggle-room" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Someone always, eventually, comes around asking uncomfortable questions about how long will this take, and when can that be picked up, yadda yadda. They have valid reasons to ask, but what do you tell them?</p>
<p>The 10 years ago me might&rsquo;ve thought, &ldquo;well, the stuff I&rsquo;m doing has taken some time, it&rsquo;ll take a little more time, and after this I&rsquo;ll grab that other thing that&rsquo;ll take time too&rdquo;. Now I recognize (sometimes begrudgingly) that you can&rsquo;t plan anything further out than today if everyone is just plugging away, and no one has a clear idea of how long anything will take.</p>
<p>Even when we all sit down though, talk about <del>our feelings</del> the details of the work, and come up with some estimates, nothing <em>ever</em> goes according to plan. Small tasks might take slightly longer, and larger tasks will almost certainly take more longer&hellip;er. And that&rsquo;s the beauty of using the fibonacci sequence instead of just straight-up points.</p>
<p>If the team thinks a small task might take half a day for a couple people to code and test, they might toss 2 points on it.. oh, but they have to round up to 3. Then a larger card comes along with a little more complexity. That seems like a 6, but nope.. gotta round up to 8. The next thing seems like a 9 or 10? Make it 13. More complexity leads to more surprises and unknowns, so there&rsquo;s a bigger jump in points the higher you go.</p>

<h2 class="relative group">Setting the cadence
    <div id="setting-the-cadence" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#setting-the-cadence" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>One of the things that drove me nuts early on was that everyone said points don&rsquo;t equate to hours. Except there&rsquo;s still 40 hours in a week and 120 hours in the typical <a href="https://www.atlassian.com/agile/scrum/sprints"  target="_blank" rel="noreferrer">sprint</a>, so uh yeah they do. But exactly <em>how many</em> hours a point equals will vary largely by team.</p>
<p>Eventually, the longer a team&rsquo;s together, and the more familiar with the project they get, and the better everything&rsquo;s flowing, the more meaning points will have. If three people think a task is worth 5 points and three think it&rsquo;s worth 8, and they hash things out and agree to go with 8, then next time they&rsquo;re all more likely to go with 8 again for a similar task. And however long that task takes them to actually complete (an hour, a day, or several days) is the amount of time 8 points takes them.</p>
<p>Side note&hellip; either smaller teams have to be together long enough for the points to take on some meaning for them, or the <em>entire</em> team has to be swapped around frequently enough for <em>everyone</em> to come to an agreement on what a point&rsquo;s worth. But keeping a team together for a few months, just about long enough for them to start coming to an unspoken agreement, and then unnecessarily switching them around? Might as well toss points out the window.</p>
<p>Anyway, one team might err on the side of caution, point things large, but always knock them all out. Their 120 hour sprint is usually 150 points of tasks. Another team points things small, but always knocks them out too. Their 120 hour sprint is only 60 points of tasks. Either way, when the next sprint comes along, each knows about how many points worth of tasks they can commit to doing. Oh, and if anyone judges one team against another solely on points, they really don&rsquo;t get it at all.</p>

<h2 class="relative group">Predicting the end date
    <div id="predicting-the-end-date" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#predicting-the-end-date" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>End date like the end of the project, not like the &ldquo;end times&rdquo;. What do I know though, maybe you&rsquo;re on a project that&rsquo;s so awful the end times sound preferable in contrast. This too shall pass? I hope.</p>
<p>The third point of points though is to predict when the project might be done. Again, it only works well if the same team is together throughout the project. If one team planned the work for their project to be 1000 points total, and they&rsquo;re getting roughly 100 points done a sprint, then they&rsquo;ll be done (using 3rd grade math) in about 30 weeks. If another team planned the same work as 500 points, and tends to get 50 points done a sprint, then it&rsquo;s the same deal. 30 weeks.</p>
<p>Of course things rarely go according to plan, due to missed complexity and requirements, scope creep, shuffling people around too much, unexpected fires, etc, etc. So at the end of the day, multiply the whole thing by the square root of pi or planck&rsquo;s constant and you should be fine. 😂</p>
<p><em>(If you want to read more about the scrum process, as well as agile, kanban, and other things that seem to always go hand-in-hand with scrum,</em> <a href="https://www.atlassian.com/agile/scrum"  target="_blank" rel="noreferrer"><em>Atlassian has a lot of material</em></a> <em>to check out.)</em></p>
]]></content:encoded><media:content url="https://grantwinney.com/whats-the-point-of-points/feature.webp" medium="image" type="image/webp"/></item><item><title>Why doesn't VS 2022 show my WinForms UI at design time?</title><link>https://grantwinney.com/why-doesnt-vs2022-show-my-winforms-ui/</link><pubDate>Sat, 14 Jan 2023 20:36:55 +0000</pubDate><guid>https://grantwinney.com/why-doesnt-vs2022-show-my-winforms-ui/</guid><description>Someone at work asked about whether we&amp;rsquo;d be able to use VS 2022 to work on our main WinForms app. It works just fine in VS 2019, so it should work in VS 2022, right? Except it doesn&amp;rsquo;t. What we get is white screens of brokenness whenever we try to open a Form in the designer. But why?</description><content:encoded><![CDATA[<p>After migrating some newer projects at work from .NET Core 3.1 (which reached <a href="https://devblogs.microsoft.com/dotnet/net-core-3-1-will-reach-end-of-support-on-december-13-2022/"  target="_blank" rel="noreferrer">end of support</a> a month ago) to .NET 6, I sent a quick message to my teammates about installing VS 2022, which is required to work on .NET 6 apps. That naturally brought up the question about whether we&rsquo;d be able to use VS 2022 to work on the WinForms app that is our main bread and butter. It works just fine in VS 2019, so it should work in VS 2022, right? Except it doesn&rsquo;t.</p>
<p>Not only does VS2022 throw build errors that VS2019 doesn&rsquo;t, which I managed to clear up with some package upgrades and other minor changes, but it also shows white screens of brokenness whenever a Form is opened in the designer. Well.. that&rsquo;s gonna make things tough.</p>
<p>After a little research, I found a thread in which <a href="https://developercommunity.visualstudio.com/t/Winforms-net-framework-projects-cant-d/1601210"  target="_blank" rel="noreferrer">Merrie McGaw and Klaus Löeffelmann explain why this is happening</a>. Basically, it&rsquo;s because <a href="https://devblogs.microsoft.com/dotnet/msbuild-and-64-bit-visual-studio-2022/"  target="_blank" rel="noreferrer">Visual Studio is now a 64-bit app</a> and the WinForms designer runs in the same process as VS. In other words, since VS moved to 64-bit, the &ldquo;designer&rdquo; portion of VS is also (now) 64-bit, which doesn&rsquo;t behave well when you try opening Forms with components that are 32-bit. I think it&rsquo;s taken us devs <em>(me, at least!)</em> by surprise because Microsoft is usually good about backwards-compatibility, not to mention all the reminders we&rsquo;ve gotten to upgrade to VS2022 and messaging (<a href="https://devblogs.microsoft.com/visualstudio/visual-studio-2022/#visual-studio-2022-is-64-bit"  target="_blank" rel="noreferrer">like here</a>) that states, <em>&ldquo;Visual Studio will continue to be a great tool for building 32-bit apps.&rdquo;</em></p>
<p>The fix (for them) is to split out the form designer into a second process, running separately from Visual Studio, and do all the work of making the processes talk to each other smoothly while still looking like one big happy app. <a href="https://devblogs.microsoft.com/dotnet/custom-controls-for-winforms-out-of-process-designer/#the-out-of-process-winforms-designer"  target="_blank" rel="noreferrer">They did it for .NET Core</a>, which was apparently a <a href="https://visualstudiomagazine.com/articles/2019/12/05/winforms-designer.aspx"  target="_blank" rel="noreferrer">huge challenge</a>:</p>
<blockquote><p><em>For example, when you drag a Button from the Toolbox onto a form – this action is handled by Visual Studio (devenv.exe process which is .NET Framework). But, once you release the mouse button to drop the Button on the form, all further actions (instantiating a Button, rendering it at a specific location, and so on) are related to .NET Core. That means .NET Framework process can no longer handle it.</em></p>
</blockquote><p>The fix (for us) is to retarget apps to x64 or AnyCPU, which (if you read through the thread) is difficult and impractical in a lot of situations. Microsoft is still working on fixing it for the .NET Framework, but it&rsquo;s still very much a WIP and Merrie admits that even when they <em>do</em> fix it, it doesn&rsquo;t mean legacy projects won&rsquo;t need to change: <em>&ldquo;They may need to be built against this new architecture for full support in the out of process designer.&rdquo;</em></p>
<p>The more realistic fix for us is to just keep using VS2019 (which as far as I can tell, <a href="https://devblogs.microsoft.com/visualstudio/support-ends-for-older-versions-of-visual-studio-feb2022/#what-does-this-mean-for-you"  target="_blank" rel="noreferrer">will receive security updates until 2029</a>) for legacy WinForms development until they figure it all out.</p>

<h2 class="relative group">From the Experts
    <div id="from-the-experts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#from-the-experts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>So all that above was me trying to summarize things. Hopefully I didn&rsquo;t make it more confusing, but here&rsquo;s a few cherry-picked quotes from the mouths (fingers?) of the Microsoft devs themselves. And further down, my own example showing off the issue, although if you&rsquo;re here you&rsquo;ve probably got your own. 😔</p>
<p><a href="https://developercommunity.visualstudio.com/t/Winforms-net-framework-projects-cant-d/1601210#T-N1601090"  target="_blank" rel="noreferrer">Merrie McGaw (response #1)</a>:</p>
<blockquote><p>[T]there were some scenarios related to .NET Framework 32-bit components that we could not make work in Windows Forms designer.</p>
</blockquote><blockquote><p>For .NET Framework, the designer remained in-process. &hellip; Since Visual Studio is now 64-bit, the .NET Framework-based designer now also runs in 64-bit, which means 32-bit code cannot be loaded at design time any longer. &hellip; [I]f you have a .NET Framework application that is referencing a 32-bit component, this component cannot be loaded in Visual Studio 2022 Windows Forms designer, and you will see a white error screen instead. This will not affect your end users, as your application will run just fine, it only affects the designer experience inside of Visual Studio. In the future, we plan to add support for .NET Framework projects to the out of process designer. Once that support is available, it will be possible to load forms that have 32-bit specific dependencies in the designer.</p>
</blockquote><blockquote><p>Due to the way the Framework WinForms Designer was created, the form you are designing runs in the same process as Visual Studio runs in. Unfortunately, with the move to 64bit it meant that references now must be of an architecture that x64 can work with (AnyCPU or 64-bit). If you have access to the source code of the original projects the key is to design them with the reference as AnyCPU, even if you ultimately build and release a 32bit version of the reference.</p>
</blockquote><p><a href="https://developercommunity.visualstudio.com/t/Winforms-net-framework-projects-cant-d/1601210#T-N1616994"  target="_blank" rel="noreferrer">Merrie McGaw (response #2)</a>:</p>
<blockquote><p>The only way for us to fix this in WinForms is a rearchitecting of the designer entirely to run out of the process of Visual Studio - and if it were to do that, you would be able to design with your references in whatever architecture suited your needs. Unfortunately moving the designer out of process is far more than a bug fix; it’s an entire re-architecting of how the design surface in one process communicates with the Visual Studio process and passes the information back and forth about control properties. We have been working on creating an out-of-process WinForms Designer for .NET applications, and we’re getting pretty happy with the user experience in this last VS release. That said, there is still more work to be done to support .NET Framework projects and it doesn’t preclude the need to do something with the references that are causing you trouble now. They may need to be built against this new architecture for full support in the out of process designer.</p>
</blockquote><p><a href="https://developercommunity.visualstudio.com/t/Winforms-net-framework-projects-cant-d/1601210#T-N10222903"  target="_blank" rel="noreferrer">Klaus Löeffelmann</a>: <em>(he also wrote a detailed</em> <a href="https://devblogs.microsoft.com/dotnet/state-of-the-windows-forms-designer-for-net-applications/"  target="_blank" rel="noreferrer"><em>blog post</em></a> <em>about this)</em></p>
<blockquote><p>The problem we are discussing in this thread is multifaceted and we are not talking about a simple bug-fix here. We are talking about conceptual work that was started in the middle of last year and will - even for quite some time - continue well into the new year.</p>
</blockquote><blockquote><p>The question folks on this thread might have in this context: why didn’t we do all this in advance before the 64-bit conversion of Visual Studio? It’s simple: Because many customers (including myself at the time being on the “other side”) would rather have 64-bit support in VS <em>immediately</em>, since they are and were <em>much</em> more dependent on the 64-bit need than on the not-yet-resolved 32-bit dependency. They say: For what’s still 32-bit, we’ll use VS2019, either until we’ve made the switch, or the out-of-process WinForms Designer is ready to handle the most common 32-bit scenarios.</p>
</blockquote><blockquote><p>And that’s the status quo. We already have a rudimentary out-of-process .NET Framework 32-bit WinForms Designer under Preview Features in VS 2022.5 Preview 1, which we will continue to make more and more 32-bit legacy-compatible over the next year. You can test this out under <em><strong>Tools-&gt;Options -&gt; Preview Features</strong></em>. So, the situation is constantly improving, and of course we will be approaching a feasible compromise spot from both sides, so to speak&hellip;</p>
</blockquote><blockquote><p>It’s not about fixing <em>the one bug</em> in this scenario, and there is certainly not just one single course of action which would make everything work. It’s about identifying the most diverse scenarios one after the other, prioritizing them and then making them intrinsically work again. . . . And to be honest and transparent and to say it <em>very</em> clearly: We also have to reckon with components or scenarios that we can no longer make work at all, because the underlying core technologies used are simply too old and raise security issues that we simply must not risk deploying.</p>
</blockquote><blockquote><p>[T]he better we work together in this respect, the faster we can make progress here. Until then, trying to target <em><strong>AnyCPU</strong></em> for all projects in Visual Studio 2022 for many .NET Framework-only scenarios or continue using Visual Studio 2019 for the time being remains your alternative for problematic scenarios.</p>
</blockquote>
<h2 class="relative group">From the Users
    <div id="from-the-users" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#from-the-users" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>And in case someone out there is like, oh boo-hoo just retarget for Any CPU and move on with life, that&rsquo;s not an option for a lot of companies.</p>
<p><a href="https://developercommunity.visualstudio.com/t/Winforms-net-framework-projects-cant-d/1601210#T-N1677854"  target="_blank" rel="noreferrer">DJ Sures</a>:</p>
<blockquote><p>We cannot be expected to change our target framework on the platform to x64 because your design plan was flawed and broke a significant Visual Studio feature. The 100+ third-party developers cannot “simply” be asked to re-build, test, and re-build their 721 libraries - and then ask 35,723 customers to upgrade.</p>
</blockquote><p><a href="https://developercommunity.visualstudio.com/t/Winforms-net-framework-projects-cant-d/1601210#T-N1693701"  target="_blank" rel="noreferrer">Erwin Lunger</a>:</p>
<blockquote><p>I use only one 32-bit ActiveX-control, but this one isn’t available as 64-bit version. The program which uses this control is very often used in our company and it would be very, very helpful if you could fix the bug. I have now to make a change in the program which is using this ActiveX-control and i am not able to do the change as of your bug.</p>
</blockquote><p><a href="https://developercommunity.visualstudio.com/t/Winforms-net-framework-projects-cant-d/1601210#T-N10010079"  target="_blank" rel="noreferrer">Troy Willmot</a>:</p>
<blockquote><p>I specifically have to compile to x86 because I do not have the source code for the third party components (and won’t be given it), and they do not work compiled into an ‘any cpu’ binary and run on a 64 bit OS (which almost all our customers have now) because they are 32 bit ActiveX controls. . . . I also don’t have this problem with just one vendor, so my chances of getting them all to rewrite for 64 bit (which I have been asking them to do for years) are zilch. Those solutions are for private hardware too (EFTPOS systems and the like), so there aren’t alternatives&hellip;</p>
</blockquote><p><a href="https://developercommunity.visualstudio.com/t/Winforms-net-framework-projects-cant-d/1601210#T-N10011743"  target="_blank" rel="noreferrer">HerbF</a>:</p>
<blockquote><p>Our solution works with Office and VSTO, which is 32-bit and requires .Net Framework 4.8. We can’t move to a newer nor a 64-bit version. If we have to leave existing VSTO behind, we may as well leave Office behind as well.</p>
</blockquote>
<h2 class="relative group">Minimal Example
    <div id="minimal-example" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#minimal-example" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Enough of that. You can <a href="https://developercommunity.visualstudio.com/t/Winforms-net-framework-projects-cant-d/1601210"  target="_blank" rel="noreferrer">read the thread</a> for lots more, but it&rsquo;s time for that example! I wanted to <a href="https://github.com/grantwinney/BlogCodeSamples/tree/master/DevTools/WinFormsDesignerInVS2022"  target="_blank" rel="noreferrer">recreate it</a>, which was dead simple. Just create a solution with two .NET Framework projects - a WinForms project and a Class Library - and set the target to x64 on the library. It doesn&rsquo;t seem to matter if you leave the main WinForms app as x86 <em>(why is that?).</em></p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-doesnt-vs2022-show-my-winforms-ui/image-23.png"
    width="492"
      height="323"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-doesnt-vs2022-show-my-winforms-ui/image-24.png"
    width="608"
      height="272"></figure>
<p>Then you can set it up any number of ways to see the problem, although here&rsquo;s a couple easy ones.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-doesnt-vs2022-show-my-winforms-ui/image-36.png"
    width="1354"
      height="376"></figure>
<p>Add a Form to the WinForms project, which inherits from a BaseForm defined in the Class library&hellip;</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-doesnt-vs2022-show-my-winforms-ui/image-37.png"
    width="1175"
      height="428"></figure>
<p>&hellip; or add a Form that contains a User Control defined in the Class Library.</p>
<p>To see the issue:</p>
<ol>
<li>Close the designer portion of the Forms, if they&rsquo;re open.</li>
<li>Change the &ldquo;Platform target&rdquo; value for the Class Library to x86 (or back to x64).</li>
<li>Rebuild the solution.</li>
<li>Open the Forms again. They&rsquo;re broken in the designer when targeting x86 (but the app runs), they show in the designer for x64 (but it won&rsquo;t run), and everything is just peachy when targeting Any CPU.</li>
</ol>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-doesnt-vs2022-show-my-winforms-ui/image-28.png"
    width="800"
      height="380"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-doesnt-vs2022-show-my-winforms-ui/image-27.png"
    width="800"
      height="380"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-doesnt-vs2022-show-my-winforms-ui/image-29.png"
    width="800"
      height="380"></figure>
<p>Targeting x86 for class library (designer broken; project runs)</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-doesnt-vs2022-show-my-winforms-ui/image-30.png"
    width="800"
      height="380"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-doesnt-vs2022-show-my-winforms-ui/image-31.png"
    width="800"
      height="380"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-doesnt-vs2022-show-my-winforms-ui/image-32.png"
    width="800"
      height="380"></figure>
<p>Targeting x64 for class library (designer works; project doesn&rsquo;t run)</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-doesnt-vs2022-show-my-winforms-ui/image-33.png"
    width="800"
      height="380"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-doesnt-vs2022-show-my-winforms-ui/image-34.png"
    width="800"
      height="380"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-doesnt-vs2022-show-my-winforms-ui/image-35.png"
    width="800"
      height="380"></figure>
<p>Targeting Any CPU for class library (designer works <em>and</em> project runs)</p>
<p>If you want to try out the example yourself, <a href="https://github.com/grantwinney/BlogCodeSamples/tree/master/DevTools/WinFormsDesignerInVS2022"  target="_blank" rel="noreferrer">get the code here</a>. Hopefully we&rsquo;ll be able to go all-in on VS 2022 soon, but I wouldn&rsquo;t bet on it until later this year at least.</p>
]]></content:encoded><media:content url="https://grantwinney.com/why-doesnt-vs2022-show-my-winforms-ui/feature.webp" medium="image" type="image/webp"/></item><item><title>Using Tuples and deconstruction to return multiple values in C#</title><link>https://grantwinney.com/using-tuple-and-deconstruction-to-return-multiple-values/</link><pubDate>Thu, 05 Jan 2023 00:38:03 +0000</pubDate><guid>https://grantwinney.com/using-tuple-and-deconstruction-to-return-multiple-values/</guid><description>A big challenge with any language is trying to group and organize things sensibly, and returning multiple values is no exception. Let&amp;rsquo;s check out Tuples and deconstruction, and see how they can help us out.</description><content:encoded><![CDATA[<p>One of the biggie challenges when programming in any language is figuring how to group and organize things sensibly. Even a small project can get out of hand quickly, and once you&rsquo;ve got a dozen devs working in something for years, all bets are off.</p>
<p>So we have methods and functions, organized into classes <em>(even</em> <a href="https://www.javascripttutorial.net/es6/javascript-class/"  target="_blank" rel="noreferrer"><em>JS has classes</em></a> <em>now)</em> and namespaces, separate projects and assemblies, and on and on. The tricky part of having so many ways to organize things is knowing how and when to use one of them. A lot of it&rsquo;s up for debate <em>(what isn&rsquo;t?)</em> and some of it&rsquo;s not&hellip; I wouldn&rsquo;t recommend publishing a NuGet package for <a href="https://www.sciencealert.com/how-a-programmer-almost-broke-the-internet-by-deleting-11-lines-of-code"  target="_blank" rel="noreferrer">a few lines of code</a>, lol.</p>
<blockquote><p>The code in this article is available on <a href="https://github.com/grantwinney/Surviving-WinForms/tree/master/ClarityConciseness/TupleDeconstruction"  target="_blank" rel="noreferrer">GitHub</a>, if you&rsquo;d like to use it in your own projects or just follow along while you read.</p>
</blockquote><p>In C#, it&rsquo;s not uncommon to use classes to group similar logic together, and then provide properties to access whatever values are in the class - a person with a name and birthdate, a car with an engine type and model, whatever. If you&rsquo;re going to have lots of properties, a class probably makes sense. But what if you don&rsquo;t? If there&rsquo;s a more lightweight option than a class and a good use case for it, I&rsquo;d be interested in hearing about it.. or writing about it as the case may be.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Circle</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">radius</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Circle</span><span class="p">(</span><span class="kt">int</span> <span class="n">radius</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="k">this</span><span class="p">.</span><span class="n">radius</span> <span class="p">=</span> <span class="n">radius</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">Diameter</span> <span class="p">=&gt;</span> <span class="n">radius</span> <span class="p">*</span> <span class="m">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">double</span> <span class="n">Circumference</span> <span class="p">=&gt;</span> <span class="n">Math</span><span class="p">.</span><span class="n">PI</span> <span class="p">*</span> <span class="n">Diameter</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">double</span> <span class="n">Area</span> <span class="p">=&gt;</span> <span class="n">Math</span><span class="p">.</span><span class="n">PI</span> <span class="p">*</span> <span class="n">Math</span><span class="p">.</span><span class="n">Pow</span><span class="p">(</span><span class="n">radius</span><span class="p">,</span> <span class="m">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>One alternative is to create a method with several &ldquo;out&rdquo; parameters.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">GetCircle</span><span class="p">(</span><span class="kt">int</span> <span class="n">radius</span><span class="p">,</span> <span class="k">out</span> <span class="kt">int</span> <span class="n">diameter</span><span class="p">,</span> <span class="k">out</span> <span class="kt">double</span> <span class="n">circumference</span><span class="p">,</span> <span class="k">out</span> <span class="kt">double</span> <span class="n">area</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">diameter</span> <span class="p">=</span> <span class="n">radius</span> <span class="p">*</span> <span class="m">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">circumference</span> <span class="p">=</span> <span class="n">Math</span><span class="p">.</span><span class="n">PI</span> <span class="p">*</span> <span class="n">diameter</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">area</span> <span class="p">=</span> <span class="n">Math</span><span class="p">.</span><span class="n">PI</span> <span class="p">*</span> <span class="n">Math</span><span class="p">.</span><span class="n">Pow</span><span class="p">(</span><span class="n">radius</span><span class="p">,</span> <span class="m">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>I used to think that was ugly since we had to define the variables before calling the method, but now they can be defined right <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/out-parameter-modifier#calling-a-method-with-an-out-argument"  target="_blank" rel="noreferrer">as we call the method</a> so it&rsquo;s a lot more palatable. That was introduced in <a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-70"  target="_blank" rel="noreferrer">C# 7.0</a>.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">GetCircle</span><span class="p">(</span><span class="m">6</span><span class="p">,</span> <span class="k">out</span> <span class="kt">var</span> <span class="n">diameter</span><span class="p">,</span> <span class="k">out</span> <span class="kt">var</span> <span class="n">circumference</span><span class="p">,</span> <span class="k">out</span> <span class="kt">var</span> <span class="n">area</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Diameter: {diameter}&#34;</span><span class="p">);</span>               <span class="c1">// 12</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Circumference: {circumference:N2}&#34;</span><span class="p">);</span>  <span class="c1">// 37.70</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Area: {area:N2}&#34;</span><span class="p">);</span>                    <span class="c1">// 113.10</span></span></span></code></pre></div></div>
<p>Another alternative, and the one I want to really focus on, is using tuples. I used to like them even less than &ldquo;out&rdquo; parameters, since whatever values you returned were only accessible by referencing <code>.Item1</code>, <code>.Item2</code>, etc. You immediately lost context of what was being returned.. very unfriendly. There have been some major changes though since <a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-70"  target="_blank" rel="noreferrer">C# 7.0</a>, and it makes tuples much easier on the eyes.. brain&hellip; something. They&rsquo;re easier to work with.</p>
<p>Here&rsquo;s an example that&rsquo;s pretty similar to the one above using &ldquo;out&rdquo; parameters, but it returns a <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/value-tuples"  target="_blank" rel="noreferrer">tuple type</a> instead. Since tuple types can have <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/value-tuples#tuple-field-names"  target="_blank" rel="noreferrer">field names</a> too, this really seems to behave more like a class to me.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="p">(</span><span class="kt">int</span> <span class="n">diameter</span><span class="p">,</span> <span class="kt">double</span> <span class="n">circumference</span><span class="p">,</span> <span class="kt">double</span> <span class="n">area</span><span class="p">)</span> <span class="n">GetCircle</span><span class="p">(</span><span class="kt">int</span> <span class="n">radius</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">diameter</span> <span class="p">=</span> <span class="n">radius</span> <span class="p">*</span> <span class="m">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">circumference</span> <span class="p">=</span> <span class="n">Math</span><span class="p">.</span><span class="n">PI</span> <span class="p">*</span> <span class="n">diameter</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">area</span> <span class="p">=</span> <span class="n">Math</span><span class="p">.</span><span class="n">PI</span> <span class="p">*</span> <span class="n">Math</span><span class="p">.</span><span class="n">Pow</span><span class="p">(</span><span class="n">radius</span><span class="p">,</span> <span class="m">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="n">diameter</span><span class="p">,</span> <span class="n">circumference</span><span class="p">,</span> <span class="n">area</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Being able to access each element of the tuple by name makes this <em>soo</em> much friendlier than in the past.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// Access each tuple element individually</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">circle</span> <span class="p">=</span> <span class="n">GetCircle</span><span class="p">(</span><span class="m">3</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Diameter: {circle.diameter}&#34;</span><span class="p">);</span>               <span class="c1">// 6</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Circumference: {circle.circumference:N2}&#34;</span><span class="p">);</span>  <span class="c1">// 18.85</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Area: {circle.area:N2}&#34;</span><span class="p">);</span>                    <span class="c1">// 28.27</span></span></span></code></pre></div></div>
<p>You can <a href="https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/discards#tuple-and-object-deconstruction"  target="_blank" rel="noreferrer">desconstruct a tuple</a> if you&rsquo;d like, assigning all the elements in one place, which is similar to how the &ldquo;out&rdquo; example works.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// Deconstruct the tuple elements into separate local variables</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="p">(</span><span class="n">diameter</span><span class="p">,</span> <span class="n">circumference</span><span class="p">,</span> <span class="n">area</span><span class="p">)</span> <span class="p">=</span> <span class="n">GetCircle</span><span class="p">(</span><span class="m">4</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Diameter: {diameter}&#34;</span><span class="p">);</span>                      <span class="c1">// 8</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Circumference: {circumference:N2}&#34;</span><span class="p">);</span>         <span class="c1">// 25.13</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Area: {area:N2}&#34;</span><span class="p">);</span>                           <span class="c1">// 50.27</span></span></span></code></pre></div></div>
<p>And if you don&rsquo;t need all of the elements, you can even <a href="https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/discards#tuple-and-object-deconstruction"  target="_blank" rel="noreferrer">discard them</a>. Here&rsquo;s an example where all I needed was the area of the circle, so I discarded the rest.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// Deconstruct the tuple elements and discard the ones you don&#39;t need</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">area</span><span class="p">)</span> <span class="p">=</span> <span class="n">GetCircle</span><span class="p">(</span><span class="m">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Area: {area:N2}&#34;</span><span class="p">);</span>                          <span class="c1">// 78.54</span></span></span></code></pre></div></div>
<p>What do you think? Like it more than classes and &ldquo;out&rdquo; variables? Love it? Hate it? Or do you have your own way of returning multiple values at once?</p>
<p>If you found this content useful, and want to learn more about a variety of C# features, check out <a href="https://github.com/grantwinney/CSharpDotNetExamples"  target="_blank" rel="noreferrer">this GitHub repo</a>, where you&rsquo;ll find links to plenty more blog posts and practical examples!</p>
]]></content:encoded><media:content url="https://grantwinney.com/using-tuple-and-deconstruction-to-return-multiple-values/feature.webp" medium="image" type="image/webp"/></item><item><title>Why do I need to install an extension just to copy/paste?</title><link>https://grantwinney.com/why-do-i-need-to-install-an-extension-just-to-copy-paste/</link><pubDate>Sat, 31 Dec 2022 17:18:11 +0000</pubDate><guid>https://grantwinney.com/why-do-i-need-to-install-an-extension-just-to-copy-paste/</guid><description>I was creating a document in Office365 the other day, and when I tried to paste with their custom right-click menu I got a popup telling me to download a browser addon. Well, that&amp;rsquo;s weird.</description><content:encoded><![CDATA[<p>I was creating a document in Office365 the other day - something I&rsquo;ve done a hundred times - but when I tried to paste something into the document with their custom right-click menu (the keyword here is &ldquo;custom&rdquo;, but more on that later), I was greeted with the following popup. Well, that&rsquo;s weird.</p>
<p>Apparently, every time I&rsquo;ve pasted into a document the past few years, I&rsquo;ve just hit Ctrl+V without thinking about it? That seems unlikely, but it&rsquo;s even less likely that the online app was just upgraded and was using the native context menu before. Either way, I don&rsquo;t know why I decided to right-click this time, but since I did and got an oddball popup, it begs the question&hellip; <em>why</em>?</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-do-i-need-to-install-an-extension-just-to-copy-paste/image-21.png"
    width="549"
      height="535"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-do-i-need-to-install-an-extension-just-to-copy-paste/image-22.png"
    width="467"
      height="311"></figure>
<p>Judging by the ten million users of the <a href="https://chrome.google.com/webstore/detail/office-enable-copy-and-pa/ifbmcpbgkhlpfcodhjhdbllhiaomkdej/related"  target="_blank" rel="noreferrer">Office - Enable Copy and Paste</a> extension, versus a few hundred reviews, it&rsquo;s pretty obvious most people just figure whatever, you present a hoop so I jump. Can&rsquo;t blame them.. technology is weird and confusing, and getting weirder and more confusing all the time.</p>
<p>Are the comments fair though? People assume that Microsoft has somehow managed to screw up something as basic as copy/paste, and instead of fixing said basic issue, they&rsquo;ve decided to write an entire extension that simply shouldn&rsquo;t be required.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-do-i-need-to-install-an-extension-just-to-copy-paste/image-10.png"
    width="810"
      height="97"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-do-i-need-to-install-an-extension-just-to-copy-paste/image-16.png"
    width="844"
      height="92"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-do-i-need-to-install-an-extension-just-to-copy-paste/image-17.png"
    width="855"
      height="96"></figure>
<p>The last one is really amusing. It&rsquo;s much easier to write a scathing review than spend 10 seconds checking the obvious thing first. Google Docs has a custom context menu too.. do they know something Microsoft doesn&rsquo;t?</p>
<p>Nope.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-do-i-need-to-install-an-extension-just-to-copy-paste/image-19.png"
    width="787"
      height="386"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-do-i-need-to-install-an-extension-just-to-copy-paste/image-20.png"
    width="488"
      height="340"></figure>
<p>The <a href="https://chrome.google.com/webstore/detail/google-docs-offline/ghbmnnjooekpmoecnnnilnnbdlolhkhi"  target="_blank" rel="noreferrer">Google Docs Offline</a> extension bundles way more than just enabling copy/paste into their addon. They have a similar number of users as Microsoft&rsquo;s, but 10x as many poor reviews, because apparently it&rsquo;s all kinds of broken. But the copy/paste functionality is required for the same reason as Microsoft&rsquo;s - they created a custom context menu.</p>

<h2 class="relative group">What if websites could (still) read your clipboard without you knowing?
    <div id="what-if-websites-could-still-read-your-clipboard-without-you-knowing" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-if-websites-could-still-read-your-clipboard-without-you-knowing" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Time for a short history lesson.</p>
<p>Going way back, it used to be possible for <em>any</em> website to <a href="https://devblogs.microsoft.com/scripting/how-can-i-grab-a-url-from-the-clipboard-and-then-open-that-web-site-in-a-browser/"  target="_blank" rel="noreferrer">read from your clipboard</a> in IE, without you having to choose to &ldquo;paste&rdquo; it. That article was actually posted on Microsoft&rsquo;s dev blogs, of all places, and ScriptingGuy1 seemed to feel it was an undocumented <em>feature,</em> and not a horrible glaring oversight. Other sites clearly recognized it for <a href="https://www.arstdesign.com/articles/clipboardexploit.html"  target="_blank" rel="noreferrer">the exploit that it was</a>. Imagine how many of us were screwed by this and never realized it.</p>
<p>You&rsquo;d think if the site you were visiting was legit then you&rsquo;d be fine, but check out that second article above. How much do you want to bet some very legit sites had commenting systems that would allow someone to post a bit of script, which would then get run every time someone visited the page and all the comments loaded? Imagine you were just on your banking website and had your password on the clipboard for some reason? Or maybe your checking account routing number? Or really, anything in the world that you happened to copy, and figured was safe.</p>
<p>Just as nefarious sites, and legit sites with nefarious code, can no longer access your clipboard without your manual intervention, Office365 and Google Docs can&rsquo;t either. Copying something <em>to</em> your clipboard isn&rsquo;t as big a deal, because at least that doesn&rsquo;t tell them anything, but they definitely shouldn&rsquo;t be able to read <em>from</em> it (when you choose to do a &ldquo;paste&rdquo;) without your consent.</p>

<h2 class="relative group">Microsoft&rsquo;s and Google&rsquo;s workaround - custom browser addons
    <div id="microsofts-and-googles-workaround---custom-browser-addons" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#microsofts-and-googles-workaround---custom-browser-addons" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If they had stuck with the standard context menu like practically every other site does, there wouldn&rsquo;t be an issue. The browser handles the copy/paste, you trust the browser, and it won&rsquo;t just send that stuff to a website without your manual intervention (i.e. pressing Ctrl+V).</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-do-i-need-to-install-an-extension-just-to-copy-paste/image-23.png"
    width="514"
      height="375"></figure>
<p>Instead, they decided to write lengthy addons that allow their custom context menus to work by requesting access to the <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Interact_with_the_clipboard#reading_from_the_clipboard"  target="_blank" rel="noreferrer">clipboardRead permission</a>.</p>

<h3 class="relative group">Microsoft
    <div id="microsoft" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#microsoft" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p><a href="https://chrome.google.com/webstore/detail/office-enable-copy-and-pa/ifbmcpbgkhlpfcodhjhdbllhiaomkdej"  target="_blank" rel="noreferrer">Microsoft&rsquo;s</a> is the more basic of the two, requesting the <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Interact_with_the_clipboard#reading_from_the_clipboard"  target="_blank" rel="noreferrer">clipboardRead permission</a> and running on the following sites (all Microsoft domains, except that last one that seems to be a DoD site). There&rsquo;s a lot of code to handle different types of data that might be stored on the clipboard, but from what I can tell that&rsquo;s pretty much all it&rsquo;s doing.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">&#34;externally_connectable&#34;</span><span class="err">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;matches&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://*.officeapps.live.com/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://*.partner.officewebapps.cn/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://*.gov.online.office365.us/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://*.dod.online.office365.us/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://project.microsoft.com/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://*.whiteboard.microsoft.com/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://whiteboard.office.com/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://whiteboard.office365.us/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://whiteboard.apps.mil/*&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="err">,</span>
</span></span><span class="line"><span class="cl"><span class="s2">&#34;permissions&#34;</span><span class="err">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;clipboardRead&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">]</span><span class="err">,</span></span></span></code></pre></div></div>
<p>When you install it, you get a straight-forward prompt to allow the permission.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-do-i-need-to-install-an-extension-just-to-copy-paste/image-8.png"
    width="450"
      height="131"></figure>

<h3 class="relative group">Google
    <div id="google" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#google" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p><a href="https://chrome.google.com/webstore/detail/google-docs-offline/ghbmnnjooekpmoecnnnilnnbdlolhkhi"  target="_blank" rel="noreferrer">Google&rsquo;s</a> is bundled with a bunch of other functionality, as I mentioned earlier. They request the clipboardRead permission too, but in a funny way. First, they request access to unlimited storage, alarms, etc., for a series of sites:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">&#34;externally_connectable&#34;</span><span class="err">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;matches&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://docs.google.com/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://drive.google.com/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://drive-daily-0.corp.google.com/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://drive-daily-1.corp.google.com/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://drive-daily-2.corp.google.com/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://drive-daily-3.corp.google.com/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://drive-daily-4.corp.google.com/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://drive-daily-5.corp.google.com/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://drive-daily-6.corp.google.com/*&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="err">,</span>
</span></span><span class="line"><span class="cl"><span class="s2">&#34;permissions&#34;</span><span class="err">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;alarms&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;storage&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;unlimitedStorage&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;https://docs.google.com/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;https://drive.google.com/*&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">]</span><span class="err">,</span></span></span></code></pre></div></div>
<p>Then in a separate section, they request the clipboard permissions. I can&rsquo;t find anything on &ldquo;content_capabilities&rdquo;, except a bit of <a href="https://source.chromium.org/chromium/chromium/src/&#43;/main:extensions/common/api/_manifest_features.json;l=96"  target="_blank" rel="noreferrer">source code</a> in Chromium that suggests it&rsquo;s only allowed for a few Google sites. Ooookay.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">&#34;content_capabilities&#34;</span><span class="err">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;matches&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://docs.google.com/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://drive.google.com/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://drive-daily-0.corp.google.com/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://drive-daily-1.corp.google.com/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://drive-daily-2.corp.google.com/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://drive-daily-3.corp.google.com/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://drive-daily-4.corp.google.com/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://drive-daily-5.corp.google.com/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://drive-daily-6.corp.google.com/*&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;permissions&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;clipboardRead&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;clipboardWrite&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;unlimitedStorage&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="err">,</span></span></span></code></pre></div></div>
<p>Even though installing the addon adds the ability to read from your clipboard in Google Docs, you don&rsquo;t get a prompt warning you of that fact when you install it.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-do-i-need-to-install-an-extension-just-to-copy-paste/image-9.png"
    width="450"
      height="129"></figure>

<h2 class="relative group">The Clipboard API - a better solution?
    <div id="the-clipboard-api---a-better-solution" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-clipboard-api---a-better-solution" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>What&rsquo;s interesting to me is how Microsoft and Google have chosen to handle the issue. There&rsquo;s a whole <a href="https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API"  target="_blank" rel="noreferrer">Clipboard API</a> that would eliminate the need for these addons, and it seems far easier to use than what they&rsquo;ve created. The <a href="https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/readText"  target="_blank" rel="noreferrer">clipboard.readText</a> function, for example, just requests the browser to allow access to the clipboard. Then the browser prompts you, and asks if you&rsquo;d like to honor that request, and if so then for how long?</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-do-i-need-to-install-an-extension-just-to-copy-paste/image-17.png"
    width="855"
      height="96"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-do-i-need-to-install-an-extension-just-to-copy-paste/image-18.png"
    width="330"
      height="290"></figure>
<p>You can easily use it yourself with very minimal coding, like I did below. Before you play with it, two things:</p>
<ol>
<li>The code below is entirely client-side.. I don&rsquo;t know what&rsquo;s on your clipboard, nor do I care.</li>
<li>After you try it, you can open settings and set Clipboard back to the default &ldquo;ask&rdquo; here: chrome://settings/content/siteDetails?site=https%3A%2F%2Fgrantwinney.com%2F</li>
</ol>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-do-i-need-to-install-an-extension-just-to-copy-paste/image-24.png"
    width="620"
      height="104"></figure>
<p>Back to the example. Select some text from somewhere (anywhere), and press &ldquo;Read Clipboard&rdquo;. You&rsquo;ll be prompted to allow this site to access your clipboard, which is the whole point. You&rsquo;re in control. If you allow access, it&rsquo;ll read whatever text is on the clipboard and display it in the input field.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">button</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;testButton&#34;</span><span class="p">&gt;</span>Read Clipboard<span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">input</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;testOutput&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s2">&#34;testButton&#34;</span><span class="p">).</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;click&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">navigator</span><span class="p">.</span><span class="nx">clipboard</span><span class="p">.</span><span class="nx">readText</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">clipText</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s2">&#34;testOutput&#34;</span><span class="p">).</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">clipText</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p>If you need to read other <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types"  target="_blank" rel="noreferrer">mime types</a> (like images), then there&rsquo;s a more general <a href="https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/read"  target="_blank" rel="noreferrer">clipboard.read</a> API call. It does more, allowing you to iterate through all the various types of items on the clipboard, but it&rsquo;s more complicated to implement. Per the MDN link above, it looks like there&rsquo;s decent support for it, but not full support yet. Edge and Opera seem to support it, Chrome supports it somewhat, and Firefox doesn&rsquo;t really yet.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/why-do-i-need-to-install-an-extension-just-to-copy-paste/image-28.png"
    width="768"
      height="294"></figure>
<p>And that, I think, is why Microsoft and Chrome still need their extensions. They probably wrote them before the above API existed in any form, and even now they can&rsquo;t really dump them because the support isn&rsquo;t fully there. Hopefully it will be soon.</p>
<p>If you want to learn more, here&rsquo;s a couple other interesting articles too:</p>
<ul>
<li><a href="https://zapier.com/blog/why-cant-you-copy-and-paste-in-google-docs/"  target="_blank" rel="noreferrer">Why can&rsquo;t I copy and paste in Google Docs? | Zapier</a></li>
<li><a href="https://www.usatoday.com/story/tech/columnist/2014/04/13/copy-and-paste-in-google-docs/7568661/"  target="_blank" rel="noreferrer">Why your browser doesn&rsquo;t like copy and paste</a></li>
</ul>
]]></content:encoded><media:content url="https://grantwinney.com/why-do-i-need-to-install-an-extension-just-to-copy-paste/feature.webp" medium="image" type="image/webp"/></item><item><title>Converting a BackgroundWorker to a Task with TaskCompletionSource</title><link>https://grantwinney.com/convert-backgroundworker-to-task-with-taskcompletionsource/</link><pubDate>Tue, 06 Dec 2022 00:20:13 +0000</pubDate><guid>https://grantwinney.com/convert-backgroundworker-to-task-with-taskcompletionsource/</guid><description>Sometimes the safer way to &amp;ldquo;update&amp;rdquo; old code is to leave it be and paint over it with a newer construct. Let&amp;rsquo;s see how to modernize a BackgroundWorker using Tasks and TaskCompletionSource.</description><content:encoded><![CDATA[<p>The reality about working with an application that&rsquo;s years - maybe even decades - old is that we don&rsquo;t have the time or resources to rewrite everything to be modern, nor would that be wise. Every legacy app, and the different areas within it, represents ideas and business functions that a company has paid dozens or hundreds of employees millions of dollars for, over the course of many years.</p>
<p>It&rsquo;s a good feeling getting to overhaul something when the opportunity arises, but &ldquo;modern&rdquo; is a moving target and certain areas of the code will chug along for years, happily unaware it lives in the dark ages.</p>
<p>Even when we can&rsquo;t change code, though, it&rsquo;s always possible to hide it without having to replace the old code right away.. or at all, if it&rsquo;s in some codebase you don&rsquo;t have access to. Take the <a href="https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.backgroundworker?view=netframework-2.0"  target="_blank" rel="noreferrer">BackgroundWorker</a> for example, which has been around since 2005 (.NET 2.0). It has methods for safely sending progress updates to the UI, cancelling it before completion, and setting the result when it finally <em>does</em> complete. 10 years ago, it was a great way to toss some piece of work to a separate thread.</p>
<p>Now, of course, there&rsquo;s <a href="https://grantwinney.com/using-async-await-and-task-to-keep-the-winforms-ui-more-responsive"  target="_blank" rel="noreferrer">async/await and Tasks</a>.</p>
<p>When I wrote about Tasks before, I considered how you might turn a bunch of synchronous code into async code, but what if the code is <em>already</em> asynchronous? What if you just want to modernize it, like effectively turning a BackgroundWorker into a Task? <a href="https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcompletionsource-1"  target="_blank" rel="noreferrer">TaskCompletionSource</a> to the rescue!</p>
<blockquote><p>The code in this article is available on <a href="https://github.com/grantwinney/Surviving-WinForms/tree/master/Threading/TaskCompletion"  target="_blank" rel="noreferrer">GitHub</a>, if you&rsquo;d like to use it in your own projects or just follow along while you read.</p>
</blockquote><p><em>Assuming you can, I&rsquo;d suggest cloning the repo and opening it in VS 2022 or later to follow along. To test both examples I describe below, open the Program.cs file and comment one &ldquo;Application.Run&rdquo; line out or the other.</em></p>

<h2 class="relative group">Sample legacy class with BackgroundWorker
    <div id="sample-legacy-class-with-backgroundworker" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#sample-legacy-class-with-backgroundworker" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Let&rsquo;s start with a class that lives in some legacy library, the source code of which we&rsquo;ll pretend we don&rsquo;t have access to&hellip;.. even though it&rsquo;s right in the repo. It uses a BackgroundWorker to call out to <a href="https://grantwinney.com/what-is-iss-notify-api"  target="_blank" rel="noreferrer">a space API</a> and return some data about the ISS. I picked this particular API because it&rsquo;s dead simple to use and requires no authentication.</p>
<p>Before you get too excited about the code below, I targeted this &ldquo;legacy&rdquo; library for .NET 2.0 (when the BackgroundWorker was introduced), so everything else had to work within those constraints too. That meant relying on <a href="https://learn.microsoft.com/en-us/dotnet/api/system.net.webrequest?view=netframework-2.0"  target="_blank" rel="noreferrer">WebRequest</a> instead of <a href="https://restsharp.dev/"  target="_blank" rel="noreferrer">RestSharp</a>, and <a href="https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/"  target="_blank" rel="noreferrer">covariance</a> in the form of <code>List&lt;object&gt;</code> instead of a <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/value-tuples"  target="_blank" rel="noreferrer">Tuple</a>. Fortunately, <a href="https://www.newtonsoft.com/json"  target="_blank" rel="noreferrer">Json.NET</a> supports .NET 2.0, so at least deserialization of the response was easy.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">OldSpaceLibrary</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">readonly</span> <span class="n">BackgroundWorker</span> <span class="n">Worker</span> <span class="p">=</span> <span class="k">new</span> <span class="n">BackgroundWorker</span> <span class="p">{</span> <span class="n">WorkerReportsProgress</span> <span class="p">=</span> <span class="kc">true</span><span class="p">,</span> <span class="n">WorkerSupportsCancellation</span> <span class="p">=</span> <span class="kc">true</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">ISSLocation</span> <span class="n">ISSLocation</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="kd">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">ISSAstronauts</span> <span class="n">ISSAstronauts</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="kd">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">OldSpaceLibrary</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Worker</span><span class="p">.</span><span class="n">DoWork</span> <span class="p">+=</span> <span class="n">Worker_DoWork</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Worker</span><span class="p">.</span><span class="n">RunWorkerCompleted</span> <span class="p">+=</span> <span class="n">Worker_RunWorkerCompleted</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">GetData</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">Worker</span><span class="p">.</span><span class="n">IsBusy</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">ISSLocation</span> <span class="p">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">ISSAstronauts</span> <span class="p">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">Worker</span><span class="p">.</span><span class="n">RunWorkerAsync</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">void</span> <span class="n">Worker_DoWork</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">DoWorkEventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Worker</span><span class="p">.</span><span class="n">ReportProgress</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="s">&#34;Requesting data...&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          
</span></span><span class="line"><span class="cl">        <span class="n">Thread</span><span class="p">.</span><span class="n">Sleep</span><span class="p">(</span><span class="m">2000</span><span class="p">);</span>   
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">Worker</span><span class="p">.</span><span class="n">CancellationPending</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">e</span><span class="p">.</span><span class="n">Cancel</span> <span class="p">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">issLocation</span> <span class="p">=</span> <span class="n">JsonConvert</span><span class="p">.</span><span class="n">DeserializeObject</span><span class="p">&lt;</span><span class="n">ISSLocation</span><span class="p">&gt;(</span>
</span></span><span class="line"><span class="cl">            <span class="n">GetDataAsString</span><span class="p">(</span><span class="s">&#34;http://api.open-notify.org/iss-now.json&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="n">Worker</span><span class="p">.</span><span class="n">ReportProgress</span><span class="p">(</span><span class="m">25</span><span class="p">,</span> <span class="s">&#34;1/2 requests done.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">Thread</span><span class="p">.</span><span class="n">Sleep</span><span class="p">(</span><span class="m">2000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">Worker</span><span class="p">.</span><span class="n">CancellationPending</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">e</span><span class="p">.</span><span class="n">Cancel</span> <span class="p">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">issAstronauts</span> <span class="p">=</span> <span class="n">JsonConvert</span><span class="p">.</span><span class="n">DeserializeObject</span><span class="p">&lt;</span><span class="n">ISSAstronauts</span><span class="p">&gt;(</span>
</span></span><span class="line"><span class="cl">            <span class="n">GetDataAsString</span><span class="p">(</span><span class="s">&#34;http://api.open-notify.org/astros.json&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="n">issAstronauts</span><span class="p">.</span><span class="n">People</span><span class="p">.</span><span class="n">RemoveAll</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">Craft</span> <span class="p">!=</span> <span class="s">&#34;ISS&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">Worker</span><span class="p">.</span><span class="n">ReportProgress</span><span class="p">(</span><span class="m">50</span><span class="p">,</span> <span class="s">&#34;2/2 requests done.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">Worker</span><span class="p">.</span><span class="n">ReportProgress</span><span class="p">(</span><span class="m">75</span><span class="p">,</span> <span class="s">&#34;Processing data...&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">Thread</span><span class="p">.</span><span class="n">Sleep</span><span class="p">(</span><span class="m">2000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">Worker</span><span class="p">.</span><span class="n">CancellationPending</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">e</span><span class="p">.</span><span class="n">Cancel</span> <span class="p">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">Worker</span><span class="p">.</span><span class="n">ReportProgress</span><span class="p">(</span><span class="m">100</span><span class="p">,</span> <span class="s">&#34;Processing complete!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">e</span><span class="p">.</span><span class="n">Result</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">object</span><span class="p">&gt;</span> <span class="p">{</span> <span class="n">issLocation</span><span class="p">,</span> <span class="n">issAstronauts</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kt">string</span> <span class="n">GetDataAsString</span><span class="p">(</span><span class="kt">string</span> <span class="n">endpoint</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">request</span> <span class="p">=</span> <span class="n">WebRequest</span><span class="p">.</span><span class="n">Create</span><span class="p">(</span><span class="n">endpoint</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">response</span> <span class="p">=</span> <span class="p">(</span><span class="n">HttpWebResponse</span><span class="p">)</span><span class="n">request</span><span class="p">.</span><span class="n">GetResponse</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">        <span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">dataStream</span> <span class="p">=</span> <span class="n">response</span><span class="p">.</span><span class="n">GetResponseStream</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">        <span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">reader</span> <span class="p">=</span> <span class="k">new</span> <span class="n">StreamReader</span><span class="p">(</span><span class="n">dataStream</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">reader</span><span class="p">.</span><span class="n">ReadToEnd</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">void</span> <span class="n">Worker_RunWorkerCompleted</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">RunWorkerCompletedEventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(!</span><span class="n">e</span><span class="p">.</span><span class="n">Cancelled</span> <span class="p">&amp;&amp;</span> <span class="n">e</span><span class="p">.</span><span class="n">Error</span> <span class="p">==</span> <span class="kc">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">data</span> <span class="p">=</span> <span class="p">(</span><span class="n">List</span><span class="p">&lt;</span><span class="kt">object</span><span class="p">&gt;)</span><span class="n">e</span><span class="p">.</span><span class="n">Result</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">ISSLocation</span> <span class="p">=</span> <span class="p">(</span><span class="n">ISSLocation</span><span class="p">)</span><span class="n">data</span><span class="p">[</span><span class="m">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">            <span class="n">ISSAstronauts</span> <span class="p">=</span> <span class="p">(</span><span class="n">ISSAstronauts</span><span class="p">)</span><span class="n">data</span><span class="p">[</span><span class="m">1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>I think, if you&rsquo;re familiar with BackgroundWorker, most of the above should be pretty straight-forward. The <code>GetData</code> method is the entry point for whatever&rsquo;s calling this class. It starts the BackgroundWorker, where the main work happens in the <code>DoWork</code> method on a different thread, periodically sending updates to the UI with <code>ReportProgress</code> and eventually returning a result, which is picked up by the <code>RunWorkerCompleted</code> method. There&rsquo;s code to check the <code>CancellationPending</code> flag occasionally too, to see if the worker thread should be terminated early.</p>
<p>There&rsquo;s some things that are goofy in this &ldquo;legacy&rdquo; class that hopefully wouldn&rsquo;t happen in real life, but I&rsquo;m just trying to make a point here, not a masterpiece of engineering, lol.</p>

<h2 class="relative group">Approach #1: Using the BackgroundWorker as-is
    <div id="approach-1-using-the-backgroundworker-as-is" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#approach-1-using-the-backgroundworker-as-is" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>One approach to using the above is to just call it as-is, subscribing to the <code>ProgressChanged</code> and <code>RunWorkerCompleted</code> events. I&rsquo;m using the <code>GetData</code> button below for two things - to start the worker, and to cancel it if they click the button a second time (by calling <code>CancelAsync</code> ).</p>
<p>Note: Even if the worker is cancelled or throws an exception, you&rsquo;ll always end up in the <code>RunWorkerCompleted</code> event back on the main thread, so that&rsquo;s where I&rsquo;m checking all the different conditions.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="cs">/// &lt;summary&gt;</span>
</span></span><span class="line"><span class="cl"><span class="cs">/// Using the BackgroundWorker as provided in the &#34;legacy space library&#34; class.</span>
</span></span><span class="line"><span class="cl"><span class="cs">/// &lt;/summary&gt;</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">partial</span> <span class="k">class</span> <span class="nc">frmCallBackgroundWorker</span> <span class="p">:</span> <span class="n">Form</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">readonly</span> <span class="n">OldSpaceLibrary</span> <span class="n">oldSpaceLibrary</span> <span class="p">=</span> <span class="k">new</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">frmCallBackgroundWorker</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">InitializeComponent</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">oldSpaceLibrary</span><span class="p">.</span><span class="n">Worker</span><span class="p">.</span><span class="n">ProgressChanged</span> <span class="p">+=</span> <span class="n">Worker_ProgressChanged</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">oldSpaceLibrary</span><span class="p">.</span><span class="n">Worker</span><span class="p">.</span><span class="n">RunWorkerCompleted</span> <span class="p">+=</span> <span class="n">Worker_RunWorkerCompleted</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">void</span> <span class="n">btnGetData_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">oldSpaceLibrary</span><span class="p">.</span><span class="n">Worker</span><span class="p">.</span><span class="n">IsBusy</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">oldSpaceLibrary</span><span class="p">.</span><span class="n">Worker</span><span class="p">.</span><span class="n">CancelAsync</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">            <span class="n">btnGetData</span><span class="p">.</span><span class="n">Enabled</span> <span class="p">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">btnGetData</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;Cancel operation&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">prgStatus</span><span class="p">.</span><span class="n">Value</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">prgStatus</span><span class="p">.</span><span class="n">Show</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">txtStatus</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;Retrieving ISS data...\r\n\r\n&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">oldSpaceLibrary</span><span class="p">.</span><span class="n">GetData</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">void</span> <span class="n">Worker_ProgressChanged</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">System</span><span class="p">.</span><span class="n">ComponentModel</span><span class="p">.</span><span class="n">ProgressChangedEventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">txtStatus</span><span class="p">.</span><span class="n">AppendText</span><span class="p">(</span><span class="s">$&#34;[{e.ProgressPercentage,3}%]: {e.UserState}\r\n&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">prgStatus</span><span class="p">.</span><span class="n">Value</span> <span class="p">=</span> <span class="n">e</span><span class="p">.</span><span class="n">ProgressPercentage</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">void</span> <span class="n">Worker_RunWorkerCompleted</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">System</span><span class="p">.</span><span class="n">ComponentModel</span><span class="p">.</span><span class="n">RunWorkerCompletedEventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">Error</span> <span class="p">!=</span> <span class="kc">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">txtStatus</span><span class="p">.</span><span class="n">AppendText</span><span class="p">(</span><span class="s">$&#34;Failed: {e.Error.Message}&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">Cancelled</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">txtStatus</span><span class="p">.</span><span class="n">AppendText</span><span class="p">(</span><span class="s">&#34;Cancelled!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">location</span> <span class="p">=</span> <span class="n">oldSpaceLibrary</span><span class="p">.</span><span class="n">ISSLocation</span><span class="p">.</span><span class="n">Position</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">astronauts</span> <span class="p">=</span> <span class="n">oldSpaceLibrary</span><span class="p">.</span><span class="n">ISSAstronauts</span><span class="p">.</span><span class="n">People</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">txtStatus</span><span class="p">.</span><span class="n">AppendText</span><span class="p">(</span><span class="s">$&#34;\r\nThe ISS is positioned over ({location.Latitude}, {location.Longitude}) with {astronauts.Count} astronauts aboard.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">btnGetData</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;Get Latest ISS Data&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">btnGetData</span><span class="p">.</span><span class="n">Enabled</span> <span class="p">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">prgStatus</span><span class="p">.</span><span class="n">Hide</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>If things run successfully, the user gets a nice message using the data returned from the two API calls.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/convert-backgroundworker-to-task-with-taskcompletionsource/call-bg-1.png"
    width="470"
      height="319"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/convert-backgroundworker-to-task-with-taskcompletionsource/call-bg-2.png"
    width="470"
      height="319"></figure>

<h2 class="relative group">Approach #2: Wrapping the BackgroundWorker in a Task
    <div id="approach-2-wrapping-the-backgroundworker-in-a-task" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#approach-2-wrapping-the-backgroundworker-in-a-task" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>As I said earlier, there&rsquo;s a class called <a href="https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcompletionsource-1"  target="_blank" rel="noreferrer">TaskCompletionSource</a> that allows us to take something that&rsquo;s already async and make it appear to the outside world as if it were a Task all along, hiding the details.</p>
<p>First, let&rsquo;s create a class (called SpaceTask) that turns the BackgroundWorker into a Task. It subscribes to the <code>RunWorkerCompleted</code> method and handles the same conditions as before (error, cancelled, success) but now it calls specific methods on the TaskCompletionSource instead of just updating the UI directly. If an exception is thrown, it sets an exception on the task. If the worker is cancelled, it marks the task as cancelled too.</p>
<p>The caller still just calls <code>GetData</code>, but instead of having to subscribe to other events to get the final result, they get a Task instead (the last line below). I added a couple properties to help the caller determine if the Task is still running and get updates on the progress of the task (using the aptly named <a href="https://learn.microsoft.com/en-us/dotnet/api/system.progress-1"  target="_blank" rel="noreferrer">Progress</a> class).</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="cs">/// &lt;summary&gt;</span>
</span></span><span class="line"><span class="cl"><span class="cs">/// Wrapping the BackgroundWorker in a Task, to hide the implementation details of the BGW</span>
</span></span><span class="line"><span class="cl"><span class="cs">/// &lt;/summary&gt;</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">SpaceTask</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="n">TaskCompletionSource</span><span class="p">&lt;</span><span class="n">Tuple</span><span class="p">&lt;</span><span class="n">ISSLocation</span><span class="p">,</span> <span class="n">ISSAstronauts</span><span class="p">&gt;&gt;</span> <span class="n">tcs</span> <span class="p">=</span> <span class="k">new</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">readonly</span> <span class="n">OldSpaceLibrary</span> <span class="n">oldSpaceLibrary</span> <span class="p">=</span> <span class="k">new</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">bool</span> <span class="n">IsRunning</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="kd">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">CancelTask</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">oldSpaceLibrary</span><span class="p">.</span><span class="n">Worker</span><span class="p">.</span><span class="n">CancelAsync</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">Tuple</span><span class="p">&lt;</span><span class="n">ISSLocation</span><span class="p">,</span> <span class="n">ISSAstronauts</span><span class="p">&gt;&gt;</span> <span class="n">GetData</span><span class="p">(</span><span class="n">IProgress</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="n">progress</span> <span class="p">=</span> <span class="kc">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">IsRunning</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">throw</span> <span class="k">new</span> <span class="n">Exception</span><span class="p">(</span><span class="s">&#34;Task is already running. Please wait until it&#39;s complete.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">IsRunning</span> <span class="p">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">oldSpaceLibrary</span><span class="p">.</span><span class="n">Worker</span><span class="p">.</span><span class="n">ProgressChanged</span> <span class="p">+=</span> <span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">progress</span><span class="p">?.</span><span class="n">Report</span><span class="p">(</span><span class="s">$&#34;[{e.ProgressPercentage,3}%]: {e.UserState}&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">oldSpaceLibrary</span><span class="p">.</span><span class="n">Worker</span><span class="p">.</span><span class="n">RunWorkerCompleted</span> <span class="p">+=</span> <span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">Error</span> <span class="p">!=</span> <span class="kc">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="n">tcs</span><span class="p">.</span><span class="n">SetException</span><span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">Error</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">Cancelled</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="n">tcs</span><span class="p">.</span><span class="n">SetCanceled</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">            <span class="k">else</span>
</span></span><span class="line"><span class="cl">                <span class="n">tcs</span><span class="p">.</span><span class="n">SetResult</span><span class="p">(</span><span class="n">Tuple</span><span class="p">.</span><span class="n">Create</span><span class="p">(</span><span class="n">oldSpaceLibrary</span><span class="p">.</span><span class="n">ISSLocation</span><span class="p">,</span> <span class="n">oldSpaceLibrary</span><span class="p">.</span><span class="n">ISSAstronauts</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="n">IsRunning</span> <span class="p">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">oldSpaceLibrary</span><span class="p">.</span><span class="n">GetData</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">tcs</span><span class="p">.</span><span class="n">Task</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>And here&rsquo;s how we might call the new SpaceTask class from anywhere in the application that&rsquo;s interested in ISS data. There&rsquo;s nothing in the code below that suggests we&rsquo;re still dealing with a BackgroundWorker. For all intents and purposes, it&rsquo;s just a Task. The underlying details are hidden by the <code>SpaceTask</code> class, nothing to see here, <em>thankyouverymuch</em>.</p>
<p>If someone&rsquo;s interested in updates, they can create a new <code>Progress&lt;T&gt;</code> handler (like I&rsquo;m doing below) and pass that to the other class. One of the interesting differences with Tasks is that cancellations and exceptions are handled more inline.. they don&rsquo;t require subscribing to additional events like the BackgroundWorker. If the Task is cancelled it throws a TaskCanceledException, if there&rsquo;s an error it throws that instead, and both can be caught and handled like I&rsquo;m doing below.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">SpaceTask</span> <span class="n">task</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">async</span> <span class="k">void</span> <span class="n">btnGetData_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">task</span><span class="p">?.</span><span class="n">IsRunning</span> <span class="p">??</span> <span class="kc">false</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">task</span><span class="p">.</span><span class="n">CancelTask</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">btnGetData</span><span class="p">.</span><span class="n">Enabled</span> <span class="p">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">btnGetData</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;Cancel operation&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">prgStatus</span><span class="p">.</span><span class="n">Value</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">prgStatus</span><span class="p">.</span><span class="n">Show</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">txtStatus</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;Retrieving ISS data...\r\n\r\n&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">try</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">progressHandler</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Progress</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;(</span><span class="n">statusUpdate</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">txtStatus</span><span class="p">.</span><span class="n">AppendText</span><span class="p">(</span><span class="s">$&#34;{statusUpdate}\r\n&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">task</span> <span class="p">=</span> <span class="k">new</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">data</span> <span class="p">=</span> <span class="k">await</span> <span class="n">task</span><span class="p">.</span><span class="n">GetData</span><span class="p">(</span><span class="n">progressHandler</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">location</span> <span class="p">=</span> <span class="n">data</span><span class="p">.</span><span class="n">Item1</span><span class="p">.</span><span class="n">Position</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">astronauts</span> <span class="p">=</span> <span class="n">data</span><span class="p">.</span><span class="n">Item2</span><span class="p">.</span><span class="n">People</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">txtStatus</span><span class="p">.</span><span class="n">AppendText</span><span class="p">(</span><span class="s">$&#34;\r\nThe ISS is positioned over ({location.Latitude}, {location.Longitude}) with {astronauts.Count} astronauts aboard.\r\n\r\n&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">catch</span> <span class="p">(</span><span class="n">TaskCanceledException</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">txtStatus</span><span class="p">.</span><span class="n">AppendText</span><span class="p">(</span><span class="s">&#34;Cancelled!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">txtStatus</span><span class="p">.</span><span class="n">AppendText</span><span class="p">(</span><span class="s">$&#34;Failed: {ex.Message}&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">btnGetData</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;Get Latest ISS Data (task)&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">btnGetData</span><span class="p">.</span><span class="n">Enabled</span> <span class="p">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">prgStatus</span><span class="p">.</span><span class="n">Hide</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/convert-backgroundworker-to-task-with-taskcompletionsource/bg-task-1.png"
    width="469"
      height="319"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/convert-backgroundworker-to-task-with-taskcompletionsource/bg-task-2.png"
    width="470"
      height="318"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/convert-backgroundworker-to-task-with-taskcompletionsource/bg-task-3.png"
    width="470"
      height="319"></figure>
<p><a href="https://github.com/grantwinney/Surviving-WinForms/tree/master/Threading/TaskCompletion"  target="_blank" rel="noreferrer">Grab the example from GitHub</a> and try it out yourself. Good luck!</p>
<p>If you just can&rsquo;t get enough of backgroundworkers and tasks, and comparisons between them, this series of posts by Stephen Cleary are still valid (even if they&rsquo;re a little dated). <em>(You&rsquo;ll see his name all over the place on stackoverflow, answering everyone&rsquo;s questions about threading in C#.)</em></p>
<p><a href="https://blog.stephencleary.com/2013/05/taskrun-vs-backgroundworker-intro.html"  target="_blank" rel="noreferrer">Task.Run vs BackgroundWorker: Intro</a></p>
<p>And here&rsquo;s a post from MS on their recommended async design pattern:
<a href="https://learn.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap"  target="_blank" rel="noreferrer">Task-based Async Pattern (TAP): Introduction and overview | Microsoft Learn</a></p>
]]></content:encoded><media:content url="https://grantwinney.com/convert-backgroundworker-to-task-with-taskcompletionsource/feature.webp" medium="image" type="image/webp"/></item><item><title>My experience migrating to MV3</title><link>https://grantwinney.com/my-experience-migrating-to-mv3/</link><pubDate>Sat, 19 Nov 2022 22:09:03 +0000</pubDate><guid>https://grantwinney.com/my-experience-migrating-to-mv3/</guid><description>I migrated my addons to MV3, and learned that version numbers increase, DRY is overrated, and 3 and 15 are probably important but I have no idea why. What I didn&amp;rsquo;t learn is how MV3 made my addon better.</description><content:encoded><![CDATA[<p>Over the summer <a href="https://grantwinney.com/what-is-manifest-v3-and-why-is-google-pestering-me"  target="_blank" rel="noreferrer">I wrote about Google&rsquo;s forced manifest v3 update</a>, and was up in the air about whether to bother figuring it out. Well, with only about 6 weeks left <em>(actually, things got</em> <a href="https://developer.chrome.com/docs/extensions/mv3/mv2-sunset/"  target="_blank" rel="noreferrer"><em>pushed out even more</em></a> <em>since the</em> <a href="https://web.archive.org/web/20210923221800/https://developer.chrome.com/docs/extensions/mv3/mv2-sunset/"  target="_blank" rel="noreferrer"><em>original article</em></a><em>),</em> I decided to give it another go, mostly because a good number of people have found <a href="/hide-comments-everywhere/" >Hide Comments Everywhere</a> to be helpful.</p>
<p>To briefly recap, <a href="https://developer.chrome.com/docs/extensions/mv3/intro/platform-vision/"  target="_blank" rel="noreferrer">Google is spearheading a change</a> to the way browser extensions are written, claiming it increases security and privacy. However, it&rsquo;ll be deterimental to the way privacy extensions like <a href="https://www.ghostery.com/blog/manifest-v3-the-ghostery-perspective"  target="_blank" rel="noreferrer">Ghostery</a> and <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=896897&amp;desc=2#c23"  target="_blank" rel="noreferrer">uBlock Origin</a> work, and the <a href="https://www.eff.org/deeplinks/2021/12/chrome-users-beware-manifest-v3-deceitful-and-threatening"  target="_blank" rel="noreferrer">Electronic Frontier Foundation</a> is calling it harmful. Since <a href="https://en.wikipedia.org/wiki/Chromium_%5C%28web_browser%5C%29#Active"  target="_blank" rel="noreferrer">nearly all the major browsers are built on top of Chromium</a> however, we&rsquo;re all along for the ride.</p>
<p>What it means is that every developer who writes an addon now needs to spend time figuring out how MV3 works, what changes are required, and make those changes <a href="https://developer.chrome.com/docs/extensions/mv3/mv2-sunset/"  target="_blank" rel="noreferrer">before June 2023</a> when Google unlists MV2 addons from their store. Don&rsquo;t be shocked if some of your favorite browser addons simply vanish next summer.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/my-experience-migrating-to-mv3/image.png"
    width="710"
      height="224"></figure>
<p>Google keeps extending the deadline</p>
<p>Firefox, which isn&rsquo;t built on Chromium, is attempting to <a href="https://blog.mozilla.org/addons/2022/05/18/manifest-v3-in-firefox-recap-next-steps/"  target="_blank" rel="noreferrer">support both</a> and will <a href="https://blog.mozilla.org/addons/2022/11/17/manifest-v3-signing-available-november-21-on-firefox-nightly/"  target="_blank" rel="noreferrer">begin accepting MV3 addons</a> in just a couple days, although they aren&rsquo;t requiring anyone to adopt the change. The question is, how manageable will that be in the long run?</p>
<p>The first thing I tried was just bumping up the version number. Maybe I&rsquo;d get lucky? Nope. I was greeted by a looong line of errors.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/my-experience-migrating-to-mv3/image-1.png"
    width="664"
      height="558"></figure>
<p>Yep, looks pretty good.</p>
<p>One by one, I moved down the list. <a href="https://developer.chrome.com/docs/extensions/mv3/mv3-migration/#action-api-unification"  target="_blank" rel="noreferrer">Browser_action was renamed to action</a> and browser_specific_settings is a Firefox thing which <a href="https://extensionworkshop.com/documentation/develop/extensions-and-the-add-on-id/#when-do-you-need-an-add-on-id"  target="_blank" rel="noreferrer"><em>might</em> be required in Firefox for MV3</a>. Requesting host URLs (like I do, to periodically grab updated css selectors to block on sites) got moved into their own section called <a href="https://developer.chrome.com/docs/extensions/mv3/mv3-migration/#host-permissions"  target="_blank" rel="noreferrer">host permissions</a>. All pretty straight-forward so far, and documented pretty well in a number of places. Here&rsquo;s a few I found along the way:</p>
<ul>
<li><a href="https://developer.chrome.com/docs/extensions/mv3/mv3-migration/#man-sw"  target="_blank" rel="noreferrer">Migrating to Manifest V3 - Chrome Developers</a></li>
<li><a href="https://blog.mozilla.org/addons/2022/10/31/begin-your-mv3-migration-by-implementing-new-features-today/"  target="_blank" rel="noreferrer">Begin your MV3 migration by implementing new features today | Mozilla</a></li>
</ul>
<p>That last error gave me pause. What does &ldquo;service work registration failed&rdquo; mean? <a href="https://developer.chrome.com/docs/extensions/mv3/mv3-migration/#man-sw"  target="_blank" rel="noreferrer">Service workers replaced background pages</a>, which isn&rsquo;t cryptic, but the error itself is so vague. <em>Why</em> did it fail? Well, reason 15 of course! Including the error number in there required a <a href="https://chromium-review.googlesource.com/c/chromium/src/&#43;/3805456"  target="_blank" rel="noreferrer">bug fix</a>, apparently, and oh boy what a good time that would&rsquo;ve been to toss some more descriptive text in there. I never did find a source for what the numbers mean, but while I was flailing around for a fix, they kept switching between 3 and 15.</p>
<p>The problem ended up being that my background script required <em>other</em> scripts, which I instructed the manifest file to load in tandem. That&rsquo;s not allowed with service workers.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">&#34;background&#34;</span><span class="err">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;scripts&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;js/third-party/jquery-3.6.0.min.js&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;js/third-party/axios.min.js&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;js/third-party/toastr.min.js&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;js/shared.js&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;js/background.js&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;persistent&#34;</span><span class="p">:</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="err">,</span></span></span></code></pre></div></div>
<p>You get to specify <em>one</em> file, which you can mark as a <a href="https://web.dev/es-modules-in-sw/#static-imports-only"  target="_blank" rel="noreferrer">module</a> in order to import code from other modules that export their functions. Or something. I&rsquo;m sure a web developer could explain all this, but I&rsquo;m not one, nor do I feel like learning about this weekend. I gave it the ol&rsquo; college try, it didn&rsquo;t go well, and I ended up copying whatever I needed from other files into the &ldquo;service worker&rdquo; file itself. For the more intrepid, <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules"  target="_blank" rel="noreferrer">maybe this&rsquo;ll help</a>. I decided DRY code wasn&rsquo;t worth my sanity.</p>
<p>One of the benefits of having to go through everything with a fine-tooth comb, though, was that I streamlined my background page / service worker. I realized I didn&rsquo;t need toastr, so I dropped that reference. jQuery is rarely a necessity, and wasn&rsquo;t in my service worker, so I dropped that reference too. I dropped Axios from the addon altogether, in favor of <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch"  target="_blank" rel="noreferrer">fetch</a>. Here&rsquo;s a nice little comparison that I found informative: <a href="https://blog.logrocket.com/axios-vs-fetch-best-http-requests/"  target="_blank" rel="noreferrer">Axios vs. fetch(): Which is best for making HTTP requests?</a></p>
<p>This code seems to work just fine with MV3, where I inject the stylesheet into the page, which had me scratching my head. I guess it&rsquo;s okay because it&rsquo;s in the content script, running within the context of the current page?</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">header</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">&#39;head&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="nx">header</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">header</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">style</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">document</span><span class="p">.</span><span class="nx">documentElement</span><span class="p">.</span><span class="nx">prepend</span><span class="p">(</span><span class="nx">style</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The reason for my concern is that one of the <a href="https://developer.chrome.com/docs/extensions/mv3/mv3-migration/#background-service-workers"  target="_blank" rel="noreferrer">differences between background pages and service workers</a> is that the latter doesn&rsquo;t have access to the DOM. I interpreted that as not having access to the DOM at all, but it seems I was mistaken. I hope.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/my-experience-migrating-to-mv3/image-6.png"
    width="536"
      height="245"></figure>
<p>In MV2, anyone could get out there and write an addon, and I feel like the complexities here might stymie a lot of people with neat ideas. Time will tell.</p>
<p>I already linked to some of these articles throughout this post, but here&rsquo;s a bunch of sites I found useful while trying to migrate. Oh, and <a href="https://github.com/grantwinney/hide-comments-everywhere/pull/137"  target="_blank" rel="noreferrer">my migration is complete</a>, uploaded, and was accepted the next day.</p>
<ul>
<li><a href="https://developer.chrome.com/docs/workbox/service-worker-overview/"  target="_blank" rel="noreferrer">Service worker overview - Chrome Developers</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules"  target="_blank" rel="noreferrer">JavaScript modules - JavaScript | MDN</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch"  target="_blank" rel="noreferrer">Using the Fetch API - Web APIs | MDN</a></li>
<li><a href="https://blog.logrocket.com/axios-vs-fetch-best-http-requests/"  target="_blank" rel="noreferrer">Axios vs. fetch(): Which is best for making HTTP requests? - LogRocket</a></li>
</ul>
<p>As for the Firefox store, <a href="https://blog.mozilla.org/addons/2022/11/17/manifest-v3-signing-available-november-21-on-firefox-nightly/"  target="_blank" rel="noreferrer">they don&rsquo;t begin accepting MV3 extensions until next week</a>, so I guess that&rsquo;s a story for another time. Here&rsquo;s some more links, from Mozilla:</p>
<ul>
<li><a href="https://blog.mozilla.org/addons/2019/09/03/mozillas-manifest-v3-faq/"  target="_blank" rel="noreferrer">Mozilla’s Manifest v3 FAQ | Mozilla Add-ons Community</a></li>
<li><a href="https://blog.mozilla.org/addons/2021/05/27/manifest-v3-update/"  target="_blank" rel="noreferrer">Manifest v3 update | Mozilla Add-ons Community</a></li>
<li><a href="https://blog.mozilla.org/addons/2022/05/18/manifest-v3-in-firefox-recap-next-steps/"  target="_blank" rel="noreferrer">Manifest v3 in Firefox: Recap &amp; Next Steps | Mozilla Add-ons Community</a></li>
<li><a href="https://blog.mozilla.org/addons/2022/06/08/manifest-v3-firefox-developer-preview-how-to-get-involved/"  target="_blank" rel="noreferrer">Manifest V3 Firefox Developer Preview — how to get involved | Mozilla</a></li>
</ul>
]]></content:encoded><media:content url="https://grantwinney.com/my-experience-migrating-to-mv3/feature.webp" medium="image" type="image/webp"/></item><item><title>Named arguments in C#</title><link>https://grantwinney.com/csharp-named-arguments/</link><pubDate>Wed, 12 Oct 2022 22:09:47 +0000</pubDate><guid>https://grantwinney.com/csharp-named-arguments/</guid><description>Named arguments in C#.. they&amp;rsquo;ve been around a long time, but does anyone use them? Let&amp;rsquo;s check out another feature that helps tame wild code.</description><content:encoded><![CDATA[<p>I&rsquo;ve recently been spending time <em>(for my benefit and hopefully yours too!)</em> reviewing some of the goodies C# has given us <a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history"  target="_blank" rel="noreferrer">over the years</a>. They mostly help make our code clearer and more concise, like <a href="https://grantwinney.com/local-functions-in-csharp-aka-nested-methods/"  target="_blank" rel="noreferrer">local functions</a> and <a href="https://grantwinney.com/using-string-interpolation-to-craft-readable-strings/"  target="_blank" rel="noreferrer">string interpolation</a>, <a href="https://grantwinney.com/null-conditional-and-null-coalescing-operators/"  target="_blank" rel="noreferrer">safe null handling</a> and the <a href="https://grantwinney.com/using-nameof-to-avoid-magic-strings/"  target="_blank" rel="noreferrer">nameof</a> operator. Others have been complete game changers, like LINQ and <a href="https://grantwinney.com/using-async-await-and-task-to-keep-the-winforms-ui-more-responsive/"  target="_blank" rel="noreferrer">async/await</a>. If you find yourself supporting a legacy app, as many of us do, I&rsquo;ve been writing a lot about <a href="https://grantwinney.com/tags/surviving-winforms/"  target="_blank" rel="noreferrer">surviving WinForms</a> in general. 😉</p>
<p>Today though, I&rsquo;m looking at a feature that&rsquo;s been around for quite awhile, and that&rsquo;s <a href="https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/named-and-optional-arguments"  target="_blank" rel="noreferrer">named arguments</a>. Even though they were introduced in C# 4.0, I don&rsquo;t see them being used much - not in the several old codebases I&rsquo;ve helped support, nor in online examples. I suppose in short examples online, and in newer code bases, you&rsquo;re not going to see methods with tons of optional arguments, but in legacy codebases.. wow. Developers over years and decades will add one more parameter.. then one more.. then two more, another one, etc., until some methods are being passed a dozen or more.</p>
<p>Before I go any further, one thing to mention. If you find yourself with a bunch of related, required parameters, you might try creating a class that pulls them all together.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">AddEmployee</span><span class="p">(</span><span class="kt">string</span> <span class="n">firstName</span><span class="p">,</span> <span class="kt">string</span> <span class="n">lastName</span><span class="p">,</span> <span class="n">DateTime</span> <span class="n">hireDate</span><span class="p">,</span> <span class="p">...)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// save employee</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Something like this, perhaps. It cleans things up a bit, especially if the app passes the values down through multiple &ldquo;business&rdquo; and &ldquo;database&rdquo; layers. Anything calling the AddEmployee method is still going to have to create an instance of the Employee class and fill in all the values, but at least you only have to list out the fields once.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">AddEmployee</span><span class="p">(</span><span class="n">Employee</span> <span class="n">employee</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// save employee</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">...</span>
</span></span><span class="line"><span class="cl"><span class="p">...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Employee</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">FirstName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">LastName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">DateTime</span> <span class="n">HireDate</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// etc</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>But what if they&rsquo;re not related? What if they&rsquo;re just a disparate collection of values that were added by a dozen people over two dozen years? I present to you &hellip;&hellip; a mess:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">ConfirmSave</span><span class="p">(</span><span class="kt">string</span> <span class="n">userName</span><span class="p">,</span> <span class="n">DateTime</span> <span class="n">hireDate</span><span class="p">,</span> <span class="n">DateTime</span><span class="p">?</span> <span class="n">termDate</span> <span class="p">=</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span> <span class="n">message</span> <span class="p">=</span> <span class="s">&#34;It might be unwise to save today. Continue?&#34;</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">isTuesday</span> <span class="p">=</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">isFullMoon</span> <span class="p">=</span> <span class="kc">false</span><span class="p">,</span> <span class="kt">decimal</span> <span class="n">pi</span> <span class="p">=</span> <span class="m">3.14</span><span class="n">m</span><span class="p">,</span> <span class="kt">int</span> <span class="n">three</span> <span class="p">=</span> <span class="m">4</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">justDoIt</span> <span class="p">=</span> <span class="kc">false</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">justDoIt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// db.Save(...);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">isTuesday</span> <span class="p">&amp;&amp;</span> <span class="p">!</span><span class="n">isFullMoon</span> <span class="p">&amp;&amp;</span> <span class="n">three</span> <span class="p">==</span> <span class="m">5</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">MessageBox</span><span class="p">.</span><span class="n">Show</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="s">&#34;Are you sure about this?&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">MessageBoxButtons</span><span class="p">.</span><span class="n">YesNo</span><span class="p">,</span> <span class="n">MessageBoxIcon</span><span class="p">.</span><span class="n">Question</span><span class="p">,</span> <span class="n">MessageBoxDefaultButton</span><span class="p">.</span><span class="n">Button2</span><span class="p">)</span> <span class="p">==</span> <span class="n">DialogResult</span><span class="p">.</span><span class="n">Yes</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// db.Save(...);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">pi</span> <span class="p">&lt;=</span> <span class="m">3.14</span><span class="n">m</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// db.Save(...);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>We&rsquo;ve got some required values in there, some optional ones with default values, and some that just make no sense at all. Why does three default to 4? Don&rsquo;t know, that was before my time. 🤔</p>
<p>In ye olden days, if you wanted to set three to 10 (oh gawd) then you&rsquo;d have to pass <em>something</em> to all the values before it too. Even though they&rsquo;re optional parameters, you couldn&rsquo;t just skip ahead to the the one optional parameter you were interested in.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// &lt; C# 4 - required to pass a value to all optional parameters leading up to the one you&#39;re interested in</span>
</span></span><span class="line"><span class="cl"><span class="n">ConfirmSave</span><span class="p">(</span><span class="n">txtUsername</span><span class="p">.</span><span class="n">Text</span><span class="p">,</span> <span class="n">dtpHireDate</span><span class="p">.</span><span class="n">Value</span><span class="p">,</span> <span class="n">TerminationDate</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="kc">false</span><span class="p">,</span> <span class="kc">false</span><span class="p">,</span> <span class="m">3.14</span><span class="n">m</span><span class="p">,</span> <span class="m">10</span><span class="p">);</span></span></span></code></pre></div></div>
<p>The risk is that you end up passing &ldquo;null&rdquo; to a string, like the &ldquo;message&rdquo; parameter above for instance, because hey you&rsquo;re not really interested in that one. Oh oops. In the method signature, it defined a default value for message if one was not provided. But you <em>did</em> provide a message&hellip; null.</p>
<p>And so the message, which was part of a prompt to the user, looks like this:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/csharp-named-arguments/empty-messagebox-prompt.png"
    width="202"
      height="152"></figure>
<p>As of C# 4.0 (ye not so but still fairly olden days), we can call out parameters we&rsquo;re interested in, and omit the optional parameters that we aren&rsquo;t. So you can pass in values for the mandatory ones first (which was required), then just set three to 5. Naturally.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// C# 4.0 - provide names for optional parameters, to pass values for only those you&#39;re actually interested in</span>
</span></span><span class="line"><span class="cl"><span class="c1">//        - positional parameters must come *before* any named optional parameters</span>
</span></span><span class="line"><span class="cl"><span class="n">ConfirmSave</span><span class="p">(</span><span class="n">txtUsername</span><span class="p">.</span><span class="n">Text</span><span class="p">,</span> <span class="n">dtpHireDate</span><span class="p">.</span><span class="n">Value</span><span class="p">,</span> <span class="n">three</span><span class="p">:</span> <span class="m">5</span><span class="p">);</span></span></span></code></pre></div></div>
<p>The other optional parameters retain their default values, like &ldquo;message&rdquo; having a valid yet somewhat ominous value, and pi being set to 3.14.</p>
<p>Later, C# 7.2 gave us a little update to this. As long as you call everything out by name, you can pass the parameters in any order. So now you can pass the optional parameter values in first, and then required ones, in whatever order your heart desires. Go nuts.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// C# 7.2 - named parameters can be followed by positional parameters</span>
</span></span><span class="line"><span class="cl"><span class="n">ConfirmSave</span><span class="p">(</span><span class="n">justDoIt</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="n">userName</span><span class="p">:</span> <span class="n">txtUsername</span><span class="p">.</span><span class="n">Text</span><span class="p">,</span> <span class="n">hireDate</span><span class="p">:</span> <span class="n">dtpHireDate</span><span class="p">.</span><span class="n">Value</span><span class="p">,</span> <span class="n">termDate</span><span class="p">:</span> <span class="n">TerminationDate</span><span class="p">);</span></span></span></code></pre></div></div>
<p>There&rsquo;s one other use for this that comes to mind, and that&rsquo;s for purposes of self-documentation. Imagine you had to work with a method that just accepts a bunch of strings, all of which will accept values that are difficult to differentiate at first glance. Something silly like this:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">AddMovie</span><span class="p">(</span><span class="kt">string</span> <span class="n">title</span><span class="p">,</span> <span class="kt">string</span> <span class="n">tagLine</span><span class="p">,</span> <span class="kt">string</span> <span class="n">releaseYear</span><span class="p">,</span> <span class="kt">string</span> <span class="n">productionYear</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span> <span class="n">consoleGameRelease</span><span class="p">,</span> <span class="kt">string</span> <span class="n">pcGameRelease</span><span class="p">,</span> <span class="kt">string</span> <span class="n">soundtrackRelease</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Which of these is easier to read? It&rsquo;s pretty typical to keep adding parameters on to the end and make them all optional, but with named parameters you don&rsquo;t even have to pass them all in in the same order.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">AddMovie</span><span class="p">(</span><span class="s">&#34;WarGames&#34;</span><span class="p">,</span> <span class="s">&#34;Is it a game, or is it real?&#34;</span><span class="p">,</span> <span class="s">&#34;1983&#34;</span><span class="p">,</span> <span class="s">&#34;1979&#34;</span><span class="p">,</span> <span class="s">&#34;1983&#34;</span><span class="p">,</span> <span class="s">&#34;1998&#34;</span><span class="p">,</span> <span class="s">&#34;1983&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">AddMovie</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">title</span><span class="p">:</span> <span class="s">&#34;WarGames&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">tagLine</span><span class="p">:</span> <span class="s">&#34;Is it a game, or is it real?&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">productionYear</span><span class="p">:</span> <span class="s">&#34;1979&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">releaseYear</span><span class="p">:</span> <span class="s">&#34;1983&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">soundtrackRelease</span><span class="p">:</span> <span class="s">&#34;1983&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">consoleGameRelease</span><span class="p">:</span> <span class="s">&#34;1984&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">pcGameRelease</span><span class="p">:</span> <span class="s">&#34;1998&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span></span></span></code></pre></div></div>
<p>As usual, it&rsquo;s not a golden hammer and ymmv. It <em>is</em> an interesting feature though, and probably one that gets under-utilized. If you find a use for it, especially if it solves some unique issue you or your team&rsquo;s having, I&rsquo;d love to hear about it below!</p>
<p>If you found this useful, and want to learn more about a variety of C# features, check out <a href="https://github.com/grantwinney/CSharpDotNetExamples"  target="_blank" rel="noreferrer">my GitHub repo</a>, where you&rsquo;ll find links to plenty more blog posts and practical examples.</p>
]]></content:encoded><media:content url="https://grantwinney.com/csharp-named-arguments/feature.webp" medium="image" type="image/webp"/></item><item><title>Local functions in C# (aka nested methods)</title><link>https://grantwinney.com/local-functions-in-csharp-aka-nested-methods/</link><pubDate>Sat, 08 Oct 2022 18:40:48 +0000</pubDate><guid>https://grantwinney.com/local-functions-in-csharp-aka-nested-methods/</guid><description>C# 7 introduced a new tool for the belt - local functions. Let&amp;rsquo;s take a look at what they are, how to use them, and why we might not want to.</description><content:encoded><![CDATA[<p>I&rsquo;ve recently been refreshing myself on some of the goodies we got with <a href="https://grantwinney.com/tags/c-6-0/"  target="_blank" rel="noreferrer">C# 6</a>, like <a href="https://grantwinney.com/null-conditional-and-null-coalescing-operators/"  target="_blank" rel="noreferrer">null safety operators</a> and <a href="https://grantwinney.com/using-string-interpolation-to-craft-readable-strings/"  target="_blank" rel="noreferrer">string interpolation</a>. I find a use for them from time to time, but I&rsquo;d bet there&rsquo;s a lot of people besides me who could use a refresher too&hellip; if they&rsquo;ve heard of them at all.</p>
<p>Everyone wants to learn, but there isn&rsquo;t always an opportunity for discovering the newest and hottest, especially in older apps. Sometimes we get so familiar with the old that it seems good enough, but little improvements help too, which is why I&rsquo;m doing these posts on <a href="https://grantwinney.com/tags/surviving-winforms/"  target="_blank" rel="noreferrer">surviving WinForms</a>.</p>
<p>Today I&rsquo;m digging into something we got in C# 7 - <a href="https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/local-functions"  target="_blank" rel="noreferrer">local functions</a>. These are functions that can be nested inside of <em>other</em> functions. Actually, it&rsquo;s an odd name choice since, unlike some languages, C# typically calls them methods. So why aren&rsquo;t they called nested methods? One of life&rsquo;s great mysteries, I suppose.</p>
<p>As I said, local functions are methods that are nested inside of other methods. Why would you want to do that? One reason might be to <a href="https://www.nickang.com/2017-12-11-what-is-dry-programming/"  target="_blank" rel="noreferrer">DRY</a> up code that&rsquo;s only needed by a single method. I&rsquo;ve seen code like this quite a bit, where the same check is being performed on a bunch of separate UI elements. It&rsquo;s repetitive, but there isn&rsquo;t really an obvious way to make it shorter.</p>
<p>Imagine a form loaded with text boxes for collecting information about employees. For some reason, the powers that be want certain fields to be set to &ldquo;N/A&rdquo; upon saving, if they&rsquo;re empty. Weird, but hey I don&rsquo;t make the rules&hellip;</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/local-functions-in-csharp-aka-nested-methods/image.png"
    width="604"
      height="321"></figure>
<p>Here&rsquo;s the code that checks every field individually and sets them to &ldquo;N/A&rdquo;:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnSave_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">txtTitle</span><span class="p">.</span><span class="n">Text</span> <span class="p">==</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">txtTitle</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;N/A&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">txtAddr2</span><span class="p">.</span><span class="n">Text</span> <span class="p">==</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">txtAddr2</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;N/A&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">txtAddr3</span><span class="p">.</span><span class="n">Text</span> <span class="p">==</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">txtAddr3</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;N/A&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">txtState</span><span class="p">.</span><span class="n">Text</span> <span class="p">==</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">txtState</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;N/A&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">txtProvince</span><span class="p">.</span><span class="n">Text</span> <span class="p">==</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">txtProvince</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;N/A&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">txtPrevEmp2</span><span class="p">.</span><span class="n">Text</span> <span class="p">==</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">txtPrevEmp2</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;N/A&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">txtPrevEmp3</span><span class="p">.</span><span class="n">Text</span> <span class="p">==</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">txtPrevEmp3</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;N/A&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">txtWorkRef2</span><span class="p">.</span><span class="n">Text</span> <span class="p">==</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">txtWorkRef2</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;N/A&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">txtWorkRef3</span><span class="p">.</span><span class="n">Text</span> <span class="p">==</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">txtWorkRef3</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;N/A&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">dbContext</span><span class="p">.</span><span class="n">Save</span><span class="p">(</span><span class="n">txtTitle</span><span class="p">.</span><span class="n">Text</span><span class="p">,</span> <span class="p">.....);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Local functions allow us to pull the similar code into a separate method that&rsquo;s <em>only</em> accessible by the method it&rsquo;s in, and we can shorten the code a bit. I think it&rsquo;s still plenty readable. What do you think?</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnSave_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">void</span> <span class="n">SetNAIfEmpty</span><span class="p">(</span><span class="n">TextBox</span> <span class="n">textBox</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">textBox</span><span class="p">.</span><span class="n">Text</span> <span class="p">==</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">textBox</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;N/A&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">SetNAIfEmpty</span><span class="p">(</span><span class="n">txtTitle</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">SetNAIfEmpty</span><span class="p">(</span><span class="n">txtAddr2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">SetNAIfEmpty</span><span class="p">(</span><span class="n">txtAddr3</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">SetNAIfEmpty</span><span class="p">(</span><span class="n">txtState</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">SetNAIfEmpty</span><span class="p">(</span><span class="n">txtProvince</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">SetNAIfEmpty</span><span class="p">(</span><span class="n">txtPrevEmp2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">SetNAIfEmpty</span><span class="p">(</span><span class="n">txtPrevEmp3</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">SetNAIfEmpty</span><span class="p">(</span><span class="n">txtWorkRef2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">SetNAIfEmpty</span><span class="p">(</span><span class="n">txtWorkRef3</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">dbContext</span><span class="p">.</span><span class="n">Save</span><span class="p">(</span><span class="n">txtTitle</span><span class="p">.</span><span class="n">Text</span><span class="p">,</span> <span class="p">.....);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Since the method is the same as any other method, we can change the signature to accept an array, and pass all the TextBox controls in at once. Notice that the &ldquo;local&rdquo; method can occur anywhere in its parent method.. it doesn&rsquo;t have to be at the top of it.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnSave_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">SetNAIfEmpty</span><span class="p">(</span><span class="n">txtTitle</span><span class="p">,</span> <span class="n">txtAddr2</span><span class="p">,</span> <span class="n">txtAddr3</span><span class="p">,</span> <span class="n">txtState</span><span class="p">,</span> <span class="n">txtProvince</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">txtPrevEmp2</span><span class="p">,</span> <span class="n">txtPrevEmp3</span><span class="p">,</span> <span class="n">txtWorkRef2</span><span class="p">,</span> <span class="n">txtWorkRef3</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">dbContext</span><span class="p">.</span><span class="n">Save</span><span class="p">(</span><span class="n">txtTitle</span><span class="p">.</span><span class="n">Text</span><span class="p">,</span> <span class="p">.....);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">void</span> <span class="n">SetNAIfEmpty</span><span class="p">(</span><span class="k">params</span> <span class="n">TextBox</span><span class="p">[]</span> <span class="n">textBoxes</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">textBox</span> <span class="k">in</span> <span class="n">textBoxes</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">textBox</span><span class="p">.</span><span class="n">Text</span> <span class="p">==</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="n">textBox</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;N/A&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Is that still readable? I&rsquo;m not so sure. It takes more time to figure out what the method&rsquo;s accomplishing, and I prefer clarity to brevity. Just because we <em>can</em> do something in fewer lines doesn&rsquo;t always mean we <em>should.</em> Like anything, local functions can be taken too far. <a href="https://ceopedia.org/index.php/Golden_hammer#Golden_hammer_in_computer_programming"  target="_blank" rel="noreferrer">Golden hammer</a> and all that.</p>
<p>And keep in mind there&rsquo;s always plenty of ways to solve a problem. Local functions aren&rsquo;t the only way to shorten the original code above. You could use the <code>Controls</code> collection and LINQ, for instance, to set every TextBox on the screen. Still pretty readable, but maybe not as flexible.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnSave_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">textBox</span> <span class="k">in</span> <span class="n">Controls</span><span class="p">.</span><span class="n">OfType</span><span class="p">&lt;</span><span class="n">TextBox</span><span class="p">&gt;().</span><span class="n">Where</span><span class="p">(</span><span class="n">tb</span> <span class="p">=&gt;</span> <span class="n">tb</span><span class="p">.</span><span class="n">Text</span> <span class="p">==</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="n">textBox</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;N/A&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Another interesting use for local functions is recursion, where you normally need to define a separate method that calls itself (recursively, some may say.. har har), and really has no other use within the class. Instead of leaving it accessible to everything in the class, you can define it locally so only the method that needs it can access it - local functions are always (implicitly) private.</p>
<p>For instance, if you had a small app that finds the factorial of a number, you could place the method that method that recursively calculates that factorial inside the button event method.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/local-functions-in-csharp-aka-nested-methods/image-1.png"
    width="604"
      height="149"></figure>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnCalcFactorial_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">txtFactorialResult</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="n">GetFactorial</span><span class="p">(</span><span class="n">Convert</span><span class="p">.</span><span class="n">ToInt32</span><span class="p">(</span><span class="n">txtFactorialStart</span><span class="p">.</span><span class="n">Text</span><span class="p">)).</span><span class="n">ToString</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">GetFactorial</span><span class="p">(</span><span class="kt">int</span> <span class="n">number</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">number</span> <span class="p">==</span> <span class="m">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="m">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">number</span> <span class="p">*</span> <span class="n">GetFactorial</span><span class="p">(</span><span class="n">number</span> <span class="p">-</span> <span class="m">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Or if you had a form that calculates how much someone will eventually repay on a loan, you might want to apply the same percentage to some starting number for x months.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/local-functions-in-csharp-aka-nested-methods/image-2.png"
    width="604"
      height="152"></figure>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnCalcTotalRepaid</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">totalRepaidAmount</span> <span class="p">=</span> <span class="n">GetTotalRepaidAmount</span><span class="p">(</span><span class="n">Convert</span><span class="p">.</span><span class="n">ToDecimal</span><span class="p">(</span><span class="n">txtOrigLoan</span><span class="p">.</span><span class="n">Text</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">Convert</span><span class="p">.</span><span class="n">ToDecimal</span><span class="p">(</span><span class="n">txtMonthlyInt</span><span class="p">.</span><span class="n">Text</span><span class="p">)</span> <span class="p">/</span> <span class="m">100</span><span class="p">,</span> <span class="n">Convert</span><span class="p">.</span><span class="n">ToInt32</span><span class="p">(</span><span class="n">txtNbrOfMonths</span><span class="p">.</span><span class="n">Text</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">txtTotalRepaid</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">$&#34;${totalRepaidAmount:0.00}&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">decimal</span> <span class="n">GetTotalRepaidAmount</span><span class="p">(</span><span class="kt">decimal</span> <span class="n">amount</span><span class="p">,</span> <span class="kt">decimal</span> <span class="n">interest</span><span class="p">,</span> <span class="kt">int</span> <span class="n">months</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">months</span> <span class="p">==</span> <span class="m">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">amount</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">GetTotalRepaidAmount</span><span class="p">(</span><span class="n">amount</span> <span class="p">*</span> <span class="p">(</span><span class="m">1</span> <span class="p">+</span> <span class="n">interest</span><span class="p">),</span> <span class="n">interest</span><span class="p">,</span> <span class="p">--</span><span class="n">months</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>In both cases, nothing else needs to call GetFactorial or GetTotalRepaidAmount, so why not have them as close to the method that <em>does</em> need each one? As the form grows and things get moved around and jumbled up by future devs, no one will have to hunt around for the methods - they&rsquo;re <em>right there</em>.</p>
<p>The caveat to this is that, since the method is accessible only within the method it&rsquo;s in, you can&rsquo;t test it. And the above two methods (GetFactorial and GetTotalRepaidAmont) would be great candidates for a <a href="https://docs.nunit.org/articles/nunit/writing-tests/attributes/testcase.html"  target="_blank" rel="noreferrer">TestCase</a> in NUnit (or <a href="https://exceptionnotfound.net/using-xunit-theory-and-inlinedata-to-test-c-extension-methods/"  target="_blank" rel="noreferrer">InlineData</a> in XUnit), where you could send maybe a half-dozen different values into the method and make sure you get the expected result. You could even move them into a controller and use <a href="https://grantwinney.com/its-possible-to-test-a-winforms-app-using-mvp/"  target="_blank" rel="noreferrer">MVP</a>.</p>
<p>So as I said, not a magic hammer, just another tool.. ymmv and all that.</p>
<p>One last thing, on the subject of taking things too far. Looking at these examples, did it occur to you that since the local function is a regular old method, you can do something else with it? Like, nest a local function <em>in</em> the local function?</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">HowAreYouFeelingToday</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;I&#39;m feeling {EvenOrOdd()} today.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">string</span> <span class="n">EvenOrOdd</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">PositiveOrNegative</span><span class="p">()</span> <span class="p">&gt;</span> <span class="m">0</span> <span class="p">?</span> <span class="s">&#34;even&#34;</span> <span class="p">:</span> <span class="s">&#34;odd&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kt">int</span> <span class="n">PositiveOrNegative</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">IsDayEven</span><span class="p">()</span> <span class="p">?</span> <span class="m">1</span> <span class="p">:</span> <span class="p">-</span><span class="m">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="kt">bool</span> <span class="n">IsDayEven</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">return</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span><span class="p">.</span><span class="n">DayOfYear</span> <span class="p">%</span> <span class="m">2</span> <span class="p">==</span> <span class="m">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>You&rsquo;re team is going to <em>love</em> you. Functions alllll the way down&hellip; 🐢</p>
<p>If you found this content useful, and want to learn more about a variety of C# features, check out <a href="https://github.com/grantwinney/CSharpDotNetExamples"  target="_blank" rel="noreferrer">this GitHub repo</a>, where you&rsquo;ll find links to plenty more blog posts and practical examples!</p>
]]></content:encoded><media:content url="https://grantwinney.com/local-functions-in-csharp-aka-nested-methods/feature.webp" medium="image" type="image/webp"/></item><item><title>Checking for null in C#, using the null conditional and null coalescing operators</title><link>https://grantwinney.com/null-conditional-and-null-coalescing-operators/</link><pubDate>Tue, 27 Sep 2022 01:13:39 +0000</pubDate><guid>https://grantwinney.com/null-conditional-and-null-coalescing-operators/</guid><description>Checking for nulls in C# is tedious, but C# 6 gave us the null-conditional operator. Let&amp;rsquo;s see what we can do with it!</description><content:encoded><![CDATA[<p>Boy, that&rsquo;s a catchy title. Sometimes they just roll off the tongue, ya know? 🙄</p>
<p>Last week, I wrote about <a href="https://grantwinney.com/using-string-interpolation-to-craft-readable-strings/"  target="_blank" rel="noreferrer">using string interpolation to craft readable strings</a>, and figured it might be worth investigating some of the other useful additions to C# over the last few years. So let&rsquo;s check out new ways (well, new compared to C#&rsquo;s age) to efficiently check for nulls.</p>
<p>Anyone who&rsquo;s spent some time in C# has had a run in with the dreaded <a href="https://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-and-how-do-i-fix-it"  target="_blank" rel="noreferrer">NullReferenceException</a>. The underlying cause isn&rsquo;t always obvious, but getting around it usually is - just check for nulls. Traditionally, the only way to safely use some deeply nested object was to check for null at every level, so a lot of older apps are littered with code like this:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">employee</span> <span class="p">!=</span> <span class="kc">null</span> <span class="p">&amp;&amp;</span> <span class="n">employee</span><span class="p">.</span><span class="n">Name</span> <span class="p">!=</span> <span class="kc">null</span> <span class="p">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">    <span class="n">employee</span><span class="p">.</span><span class="n">Department</span> <span class="p">!=</span> <span class="kc">null</span> <span class="p">&amp;&amp;</span> <span class="n">employee</span><span class="p">.</span><span class="n">Department</span><span class="p">.</span><span class="n">Manager</span> <span class="p">!=</span> <span class="kc">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">MessageBox</span><span class="p">.</span><span class="n">Show</span><span class="p">(</span><span class="n">employee</span><span class="p">.</span><span class="n">Name</span> <span class="p">+</span> <span class="s">&#34; reports to &#34;</span> <span class="p">+</span> <span class="n">employee</span><span class="p">.</span><span class="n">Department</span><span class="p">.</span><span class="n">Manager</span> <span class="p">+</span> <span class="s">&#34;.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>That is just so long-winded and <em>boring.</em> We shouldn&rsquo;t have to type all that repetitive code out, and we don&rsquo;t. C# 6 gave us a new tool - the null-conditional operator.</p>
<blockquote><p>The code in this article is available on <a href="https://github.com/grantwinney/Surviving-WinForms/tree/master/ClarityConciseness/NullHandlingOperators"  target="_blank" rel="noreferrer">GitHub</a>, if you&rsquo;d like to use it in your own projects or just follow along while you read.</p>
</blockquote><p>First, let&rsquo;s define a few nested classes to use for examples, and then a company with a couple departments and employees to experiment with.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Company</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Uri</span> <span class="n">URL</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">DateTime</span> <span class="n">Founded</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">IList</span><span class="p">&lt;</span><span class="n">Department</span><span class="p">&gt;</span> <span class="n">Departments</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Department</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">IList</span><span class="p">&lt;</span><span class="n">Employee</span><span class="p">&gt;</span> <span class="n">Employees</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Employee</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Employee</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">JobTitle</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">DateTime</span><span class="p">?</span> <span class="n">HireDate</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="n">Company</span> <span class="n">DefineCompany</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="k">new</span> <span class="n">Company</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;SpaceX&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">URL</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Uri</span><span class="p">(</span><span class="s">&#34;https://www.spacex.com&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">Founded</span> <span class="p">=</span> <span class="k">new</span> <span class="n">DateTime</span><span class="p">(</span><span class="m">2002</span><span class="p">,</span> <span class="m">03</span><span class="p">,</span> <span class="m">14</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">Departments</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Department</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">new</span> <span class="n">Department</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Starship Engineering&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">Employees</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Employee</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="k">new</span> <span class="n">Employee</span>
</span></span><span class="line"><span class="cl">                    <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Geordi La Forge&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">JobTitle</span> <span class="p">=</span> <span class="s">&#34;Lead Flight Control Tech&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">HireDate</span> <span class="p">=</span> <span class="k">new</span> <span class="n">DateTime</span><span class="p">(</span><span class="m">2011</span><span class="p">,</span><span class="m">1</span><span class="p">,</span><span class="m">1</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="k">new</span> <span class="n">Employee</span>
</span></span><span class="line"><span class="cl">                    <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Montgomery Scott&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">JobTitle</span> <span class="p">=</span> <span class="s">&#34;Sr Propulsion Engineer&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">HireDate</span> <span class="p">=</span> <span class="k">new</span> <span class="n">DateTime</span><span class="p">(</span><span class="m">2022</span><span class="p">,</span><span class="m">2</span><span class="p">,</span><span class="m">2</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="k">new</span> <span class="n">Employee</span>
</span></span><span class="line"><span class="cl">                    <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;B&#39;Elanna Torres&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="k">new</span> <span class="n">Department</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Space Car Retrieval&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h2 class="relative group">Null Conditional operator
    <div id="null-conditional-operator" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#null-conditional-operator" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators#null-conditional-operators--and-"  target="_blank" rel="noreferrer">null-conditional operator</a> allows you to call a deeply-nested class member, where anything in the chain of objects might be null, and it returns null instead of throwing an exception.</p>
<p>In the above code, for example, the Company has a name. But what if it didn&rsquo;t, and you tried to get the length of it for some reason? It would throw an exception if you didn&rsquo;t check for null first. Null conditional <code>??</code> operator to the rescue!</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// Returns a valid company name</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">validCompanyNameLength</span> <span class="p">=</span> <span class="n">validCompany</span><span class="p">.</span><span class="n">Name</span><span class="p">.</span><span class="n">Length</span><span class="p">;</span>     <span class="c1">// 6 (SpaceX)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// If Name is null, then it short-circuits the rest of the line and returns</span>
</span></span><span class="line"><span class="cl"><span class="c1">// null, instead of throwing an exception when Length is called</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">invalidCompanyNameLength</span> <span class="p">=</span> <span class="n">emptyCompany</span><span class="p">.</span><span class="n">Name</span><span class="p">?.</span><span class="n">Length</span><span class="p">;</span>  <span class="c1">// null</span></span></span></code></pre></div></div>
<p>By adding a single <code>?</code> to the second line in the right place, it stores null instead of throwing an exception. One caveat is that, even though <code>Length</code> returns an integer, the variable on the left is actually an <code>int?</code>, since it needs to be able to store null.</p>
<p>Here&rsquo;s another example, where one company has a URL, but the other does not. Since URL is null on the second line, accessing Host would normally throw an exception.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">validUrlHost</span> <span class="p">=</span> <span class="n">validCompany</span><span class="p">.</span><span class="n">URL</span><span class="p">.</span><span class="n">Host</span><span class="p">;</span>     <span class="c1">// www.spacex.com</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">invalidUrlHost</span> <span class="p">=</span> <span class="n">emptyCompany</span><span class="p">.</span><span class="n">URL</span><span class="p">?.</span><span class="n">Host</span><span class="p">;</span>  <span class="c1">// null</span></span></span></code></pre></div></div>
<p>You only have to use them where you&rsquo;re worried about nulls too. If you&rsquo;ve defined your classes in such a way that <em>if</em> there&rsquo;s a department, then it <em>will</em> have a collection of employees (maybe an empty one), then you can just use the null conditional operator in the one place you&rsquo;re worried about.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">engineers</span> <span class="p">=</span> <span class="n">validCompany</span><span class="p">.</span><span class="n">Departments</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="n">Employees</span><span class="p">.</span><span class="n">Count</span><span class="p">();</span>      <span class="c1">// 3</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">invalidCount</span> <span class="p">=</span> <span class="n">emptyCompany</span><span class="p">.</span><span class="n">Departments</span><span class="p">?[</span><span class="m">0</span><span class="p">].</span><span class="n">Employees</span><span class="p">.</span><span class="n">Count</span><span class="p">();</span>  <span class="c1">// null</span></span></span></code></pre></div></div>
<p><strong>Note #1:</strong> The operator always applies to the variable right <em>before</em> it. So above, if Departments is null then you&rsquo;re safe. But if Departments is instantiated but empty, then trying to access the first element from the collection will still throw a different exception.</p>
<p><strong>Note #2:</strong> Use these where it makes sense. I happen to think that, if there&rsquo;s no reasonable explanation for a certain variable to ever be null, then it&rsquo;s probably better to let it throw an exception so you can debug it, rather than aggressively preventing NullReferenceException everywhere and giving things default values where it doesn&rsquo;t make sense.</p>

<h2 class="relative group">Null Coalescing operator
    <div id="null-coalescing-operator" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#null-coalescing-operator" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Using the null coalescing operator (which we&rsquo;ve had for a long time) in tandem with the null conditional operator gives you even more power, but it&rsquo;s still concise enough for a single line. It lets you define what the default value should be when a value is null.</p>
<p>For example, you can replace this:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">string</span> <span class="n">companyName</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">companyName</span> <span class="p">!=</span> <span class="kc">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">companyName</span> <span class="p">=</span> <span class="n">company</span><span class="p">.</span><span class="n">Name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="n">companyName</span> <span class="p">=</span> <span class="s">&#34;unknown&#34;</span><span class="p">;</span></span></span></code></pre></div></div>
<p>With this:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">companyName</span> <span class="p">=</span> <span class="n">company</span><span class="p">.</span><span class="n">Name</span> <span class="p">!=</span> <span class="kc">null</span> <span class="p">?</span> <span class="n">company</span><span class="p">.</span><span class="n">Name</span> <span class="p">?</span> <span class="s">&#34;unknown&#34;</span><span class="p">;</span></span></span></code></pre></div></div>
<p>And finally, with this:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">companyName</span> <span class="p">=</span> <span class="n">company</span><span class="p">.</span><span class="n">Name</span> <span class="p">??</span> <span class="s">&#34;unknown&#34;</span><span class="p">;</span></span></span></code></pre></div></div>
<p>Revisiting the earlier examples, you can use both together to avoid an exception <em>and</em> to decide what the value should be when it&rsquo;s null&hellip;</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;{validCompany.Name}&#39;s website is {validCompany.URL.Host}.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// SpaceX&#39;s website is www.spacex.com.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">anotherInvalidCompany</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Company</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;ACME&#34;</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;{anotherInvalidCompany.Name}&#39;s website is {anotherInvalidCompany.URL?.Host ?? &#34;</span><span class="n">unknown</span><span class="s">&#34;}.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// ACME&#39;s website is unknown.</span></span></span></code></pre></div></div>
<p>If you want to count the number of employees, but some departments won&rsquo;t have any, then use the null coalescing operator to just say there&rsquo;s 0 employees.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">dept</span> <span class="k">in</span> <span class="n">validCompany</span><span class="p">.</span><span class="n">Departments</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;{dept.Name} currently has {dept.Employees?.Count() ?? 0} employee(s).&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Starship Engineering currently has 3 employee(s).</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Space Car Retrieval currently has 0 employee(s).</span></span></span></code></pre></div></div>
<p>Here&rsquo;s one more example, where some employees don&rsquo;t have a hire date. Not sure why that would be, but out in space you&rsquo;ve got bigger fish to fry than recording every alien who joins the crew. Or something.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">emp</span> <span class="k">in</span> <span class="n">validCompany</span><span class="p">.</span><span class="n">Departments</span><span class="p">.</span><span class="n">SelectMany</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">Employees</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;{emp.Name} was hired on {emp.HireDate?.ToString(&#34;</span><span class="n">d</span><span class="s">&#34;) ?? &#34;</span><span class="n">an</span> <span class="n">unknown</span> <span class="n">date</span><span class="s">&#34;}.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Geordi La Forge was hired on 1/1/2011.</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Montgomery Scott was hired on 2/2/2022.</span>
</span></span><span class="line"><span class="cl"><span class="c1">// B&#39;Elanna Torres was hired on an unknown date.</span></span></span></code></pre></div></div>
<p>And that example from the beginning? It becomes this:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// Before</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">employee</span> <span class="p">!=</span> <span class="kc">null</span> <span class="p">&amp;&amp;</span> <span class="n">employee</span><span class="p">.</span><span class="n">Name</span> <span class="p">!=</span> <span class="kc">null</span> <span class="p">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">    <span class="n">employee</span><span class="p">.</span><span class="n">Department</span> <span class="p">!=</span> <span class="kc">null</span> <span class="p">&amp;&amp;</span> <span class="n">employee</span><span class="p">.</span><span class="n">Department</span><span class="p">.</span><span class="n">Manager</span> <span class="p">!=</span> <span class="kc">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">MessageBox</span><span class="p">.</span><span class="n">Show</span><span class="p">(</span><span class="n">employee</span><span class="p">.</span><span class="n">Name</span> <span class="p">+</span> <span class="s">&#34; reports to &#34;</span> <span class="p">+</span> <span class="n">employee</span><span class="p">.</span><span class="n">Department</span><span class="p">.</span><span class="n">Manager</span> <span class="p">+</span> <span class="s">&#34;.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Now</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">employee</span><span class="p">?.</span><span class="n">Name</span> <span class="p">!=</span> <span class="kc">null</span> <span class="p">&amp;&amp;</span> <span class="n">employee</span><span class="p">?.</span><span class="n">Department</span><span class="p">?.</span><span class="n">Manager</span> <span class="p">!=</span> <span class="kc">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">MessageBox</span><span class="p">.</span><span class="n">Show</span><span class="p">(</span><span class="s">$&#34;{employee.Name} reports to {employee.Department.Manager}.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>If you found this content useful, and want to learn more about a variety of C# features, check out my <a href="https://github.com/grantwinney/CSharpDotNetExamples"  target="_blank" rel="noreferrer">CSharpDotNetExamples repo on GitHub</a>, where you&rsquo;ll find links to plenty more blog posts and practical examples!</p>
]]></content:encoded><media:content url="https://grantwinney.com/null-conditional-and-null-coalescing-operators/feature.webp" medium="image" type="image/webp"/></item><item><title>Using string interpolation to craft readable strings in C#</title><link>https://grantwinney.com/using-string-interpolation-to-craft-readable-strings/</link><pubDate>Mon, 19 Sep 2022 10:30:18 +0000</pubDate><guid>https://grantwinney.com/using-string-interpolation-to-craft-readable-strings/</guid><description>The longer I write software, the more I come to appreciate clear code. String interpolation in C# is just one more way to help us do that.</description><content:encoded><![CDATA[<p>The longer I&rsquo;ve been writing software, the more I&rsquo;ve come to appreciate clear code. One of the toughest challenges in software development is understanding someone else&rsquo;s code - or your own after a few months, heh. If you&rsquo;re supporting an older app, you&rsquo;re spending as much (or more) time understanding what someone wrote 20 years ago than writing new code. Anything that makes it easier is a welcome thing!</p>
<p>When C# 6.0 was released around 2015, it introduced a new feature called string interpolation. Interestingly, string interpolation was introduced in JavaScript with ES6 the same year. If you haven&rsquo;t heard of it before (not unreasonable, since it can take years to stumble on a new feature, especially if you&rsquo;re working in an older app that doesn&rsquo;t use it), it helps us build strings in a much more readable fashion than what we had before.</p>
<p>It always helps to look at some examples, so here&rsquo;s a little WinForms app that collects a few pieces of info about a user and displays a short message.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-string-interpolation-to-craft-readable-strings/image-6.png"
    width="293"
      height="372"></figure>
<p>Lets take a quick look at what came before, so we can appreciate what we&rsquo;ve got now!</p>

<h2 class="relative group">The + operator
    <div id="the--operator" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the--operator" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The most basic way of building a string, and what we&rsquo;ve always had in C#, is to simply add everything together. It&rsquo;s choppy looking, it&rsquo;s tough to tell what the full message will look like at runtime, and it encourages errors like forgetting an extra space somewhere, so that allthewordsruntogether. Oops.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="c1">// string concatenation</span>
</span></span><span class="line"><span class="cl"><span class="c1">// https://docs.microsoft.com/en-us/dotnet/csharp/how-to/concatenate-multiple-strings</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">message</span> <span class="p">=</span> <span class="n">txtName</span><span class="p">.</span><span class="n">Text</span> <span class="p">+</span> <span class="s">&#34;, whose birthday is &#34;</span> <span class="p">+</span> <span class="n">dtpBirthday</span><span class="p">.</span><span class="n">Value</span><span class="p">.</span><span class="n">ToString</span><span class="p">(</span><span class="s">&#34;d&#34;</span><span class="p">)</span> <span class="p">+</span> <span class="s">&#34; and favorite number is &#34;</span><span class="p">+</span> <span class="n">numLuckyNumber</span><span class="p">.</span><span class="n">Value</span> 
</span></span><span class="line"><span class="cl">    <span class="p">+</span> <span class="s">&#34;, is &#34;</span> <span class="p">+</span> <span class="p">(</span><span class="n">rdoYes</span><span class="p">.</span><span class="n">Checked</span> <span class="p">?</span> <span class="s">&#34;tolerating&#34;</span> <span class="p">:</span> <span class="s">&#34;dreading&#34;</span><span class="p">)</span> <span class="p">+</span> <span class="s">&#34; WinForms, hanging on with a steady flow of &#34;</span> <span class="p">+</span> <span class="n">cboBeverage</span><span class="p">.</span><span class="n">Text</span><span class="p">.</span><span class="n">ToLower</span><span class="p">()</span> <span class="p">+</span> <span class="s">&#34;.&#34;</span><span class="p">;</span></span></span></code></pre></div></div>

<h2 class="relative group">String.Format
    <div id="stringformat" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#stringformat" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The static <a href="https://docs.microsoft.com/en-us/dotnet/api/system.string.format"  target="_blank" rel="noreferrer">string.Format</a> method improved things, letting us create a single string with placeholders and formatting, so it&rsquo;s a lot easier to envision how it&rsquo;ll look. In the example below, it&rsquo;s easier to see what the string will look like at runtime.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="c1">// string format</span>
</span></span><span class="line"><span class="cl"><span class="c1">// https://docs.microsoft.com/en-us/dotnet/api/system.string.format</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">message</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Format</span><span class="p">(</span><span class="s">&#34;{0}, whose birthday is {1:d} and favorite number is {2}, is {3} WinForms, hanging on with a steady flow of {4}.&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">txtName</span><span class="p">.</span><span class="n">Text</span><span class="p">,</span> <span class="n">dtpBirthday</span><span class="p">.</span><span class="n">Value</span><span class="p">,</span> <span class="n">numLuckyNumber</span><span class="p">.</span><span class="n">Value</span><span class="p">,</span> <span class="n">rdoYes</span><span class="p">.</span><span class="n">Checked</span> <span class="p">?</span> <span class="s">&#34;tolerating&#34;</span> <span class="p">:</span> <span class="s">&#34;dreading&#34;</span><span class="p">,</span> <span class="n">cboBeverage</span><span class="p">.</span><span class="n">Text</span><span class="p">.</span><span class="n">ToLower</span><span class="p">());</span></span></span></code></pre></div></div>
<p>Plus, you can store the formatted string for later use (in a variable inside a class, or even in a separate file to read out later), and then use it wherever you need it. You can&rsquo;t do that with string interpolation.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">fmtStr</span> <span class="p">=</span> <span class="s">&#34;{0}, whose birthday is {1:d} and favorite number is {2}, is {3} WinForms, hanging on with a steady flow of {4}.&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">message1</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Format</span><span class="p">(</span><span class="n">fmtStr</span><span class="p">,</span> <span class="n">txtName</span><span class="p">.</span><span class="n">Text</span><span class="p">,</span> <span class="n">dtpBirthday</span><span class="p">.</span><span class="n">Value</span><span class="p">,</span> <span class="n">numLuckyNumber</span><span class="p">.</span><span class="n">Value</span><span class="p">,</span> <span class="n">rdoYes</span><span class="p">.</span><span class="n">Checked</span> <span class="p">?</span> <span class="s">&#34;tolerating&#34;</span> <span class="p">:</span> <span class="s">&#34;dreading&#34;</span><span class="p">,</span> <span class="n">cboBeverage</span><span class="p">.</span><span class="n">Text</span><span class="p">.</span><span class="n">ToLower</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">message2</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Format</span><span class="p">(</span><span class="n">fmtStr</span><span class="p">,</span> <span class="s">&#34;Bob&#34;</span><span class="p">,</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="s">&#34;loving&#34;</span><span class="p">,</span> <span class="s">&#34;coffee&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">message3</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Format</span><span class="p">(</span><span class="n">fmtStr</span><span class="p">,</span> <span class="s">&#34;Marcus Antoninus&#34;</span><span class="p">,</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span><span class="p">.</span><span class="n">AddYears</span><span class="p">(-</span><span class="m">1900</span><span class="p">),</span> <span class="m">5</span><span class="p">,</span> <span class="s">&#34;unsure about&#34;</span><span class="p">,</span> <span class="s">&#34;wine&#34;</span><span class="p">);</span></span></span></code></pre></div></div>
<p>Still, the values are listed <em>after</em> the string, so you have to scan back and forth to see what goes where. It&rsquo;s also really easy to rearrange the placeholders, or insert new ones, and end up replacing a placeholder with the wrong value when something gets out of order.</p>

<h2 class="relative group">String Interpolation
    <div id="string-interpolation" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#string-interpolation" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://docs.microsoft.com/en-us/dotnet/csharp/tutorials/string-interpolation"  target="_blank" rel="noreferrer">String interpolation</a> was introduced in <a href="https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-60"  target="_blank" rel="noreferrer">.NET 6.0</a>, increasing the readability of crafting strings even more. Notice how you can evaluate code like ternary operators inline with the rest of the string. You can read the string and it looks like it will for the user - well, to a developer&rsquo;s eyes anyway.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="c1">// string interpolation</span>
</span></span><span class="line"><span class="cl"><span class="c1">// https://docs.microsoft.com/en-us/dotnet/csharp/tutorials/string-interpolation</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">message</span> <span class="p">=</span> <span class="s">$&#34;{txtName.Text}, whose birthday is {dtpBirthday.Value:d} and favorite number is {numLuckyNumber.Value}, &#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">+</span> <span class="s">$&#34;is {(rdoYes.Checked ? &#34;</span><span class="n">tolerating</span><span class="s">&#34; : &#34;</span><span class="n">dreading</span><span class="s">&#34;)} WinForms, hanging on with a steady flow of {cboBeverage.Text.ToLower()}.&#34;</span><span class="p">;</span></span></span></code></pre></div></div>
<p>You can use the verbatim symbol if you need to, as well, such as to ignore special characters like backslashes.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">message</span> <span class="p">=</span> <span class="s">$@&#34;Storing {txtName.Text}&#39;s speeches in &#34;&#34;c:\users\{string.Join(&#34;&#34;, txtName.Text.Split(&#39; &#39;)).ToLower()}&#34;&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Output: Storing Abraham Lincoln&#39;s speeches in &#34;c:\users\abrahamlincoln&#34;</span></span></span></code></pre></div></div>
<p>Of course, anything can be taken to an extreme and become unreadable again. If you add a bunch of nested ternary operators just to try and keep everything in one string, it&rsquo;d be better to just define them <em>before</em> the string.</p>
<p>If you want to learn even more about strings, check out Microsoft&rsquo;s documentation on <a href="https://docs.microsoft.com/en-us/dotnet/csharp/tutorials/exploration/interpolated-strings-local"  target="_blank" rel="noreferrer">using string interpolation</a> and <a href="https://learn.microsoft.com/en-us/dotnet/csharp/how-to/concatenate-multiple-strings"  target="_blank" rel="noreferrer">more ways to concatenate strings</a>.</p>
<p>And if you want to learn more about a variety of C# features, check out <a href="https://github.com/grantwinney/CSharpDotNetExamples"  target="_blank" rel="noreferrer">my GitHub repo</a>, where you&rsquo;ll find links to plenty more blog posts and practical examples.</p>
]]></content:encoded><media:content url="https://grantwinney.com/using-string-interpolation-to-craft-readable-strings/feature.webp" medium="image" type="image/webp"/></item><item><title>6 space-related APIs to check out ahead of the Artemis I launch</title><link>https://grantwinney.com/6-space-related-apis-to-check-out-ahead-of-the-artemis-i-launch/</link><pubDate>Sun, 28 Aug 2022 18:23:48 +0000</pubDate><guid>https://grantwinney.com/6-space-related-apis-to-check-out-ahead-of-the-artemis-i-launch/</guid><description>The week of NASA launching Artemis I is a good time to check a few of the many APIs that make tons of raw space data accessible for anyone to use.</description><content:encoded><![CDATA[<p>NASA is set to launch the first of a series of Orion rockets that will eventually take us back to the moon for the first time in 50 years. It seems the goal this time around is much more than visiting the moon. I won&rsquo;t say &ldquo;just&rdquo; visiting the moon because, c&rsquo;mon, visiting the moon is still mind-blowing!</p>
<p>There&rsquo;s talk of building another space station, The Gateway, to orbit the moon. It&rsquo;ll be similar to ISS but able to move easier to conduct different kinds of research around the moon and designed to be less reliant on Earth. There&rsquo;s talk of a lunar base too, and of using everything they learn to go even further, on to Mars.</p>
<p>It&rsquo;s amazing, considering a little over a hundred years ago we were barely off the ground with the first airplanes. Walking and living on the moon was the stuff of dreams and sci-fi.</p>
<p>Given that the launch is just around the corner, I figured it as good a time as any to explore some of the APIs that allow us to access all kinds of data about space and the people who work and live there. But first&hellip;</p>
<ul>
<li><a href="https://grantwinney.com/what-is-an-api/"  target="_blank" rel="noreferrer">Read this</a> if you&rsquo;re unfamiliar with APIs.</li>
<li>Get <a href="https://www.getpostman.com/"  target="_blank" rel="noreferrer">Postman</a> to make your life easier when accessing APIs.</li>
<li><a href="https://grantwinney.com/tags/api/"  target="_blank" rel="noreferrer">Check out my previous posts</a> if you&rsquo;d like to learn about other interesting APIs.</li>
</ul>

<h2 class="relative group">ISS Notify API
    <div id="iss-notify-api" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#iss-notify-api" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>This API, which I&rsquo;ve written about before, allows you to find the current location of the ISS with one simple call. It&rsquo;s tiny but useful.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">GET http://api.open-notify.org/iss-now.json</code></pre></div>
<p>Couldn&rsquo;t you imagine having a large map of the Earth overlaid with a matrix of LEDs, where you could watch the progress of the ISS as it flies around the planet once every 90 minutes? Maybe using RGB LEDs, turning them yellow where it happens to be daytime, then turning one red for the ISS. Hmm&hellip;</p>
<p><a href="https://grantwinney.com/what-is-iss-notify-api/"  target="_blank" rel="noreferrer">Learn About the ISS and its Crew with the ISS Notify API</a></p>

<h2 class="relative group">NASA API
    <div id="nasa-api" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#nasa-api" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Then there&rsquo;s the official NASA API, with all kinds of interesting endpoints. In the post I wrote a few years ago, I explored the photo of the day and viewing images from the various rovers we&rsquo;ve sent to Mars. <em>(Incidentally,</em> <a href="https://www.nasa.gov/feature/curiosity-celebrates-10-years-on-mars"  target="_blank" rel="noreferrer"><em>the Curiosity rover just hit 10 years</em></a> <em>and it&rsquo;s still kicking!)</em></p>
<p><a href="https://grantwinney.com/what-is-nasa-api/"  target="_blank" rel="noreferrer">View the Mars Rover, Landsat Images, and More with the NASA API</a></p>
<p>When I wrote about this before, the Perseverance wasn&rsquo;t around yet (it launched in 2020 and landed in Feb 2021), so you can check out the images coming back from that rover too.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">https://api.nasa.gov/mars-photos/api/v1/rovers/perseverance/photos?sol=531&amp;api_key=your-api-key</code></pre></div>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/6-space-related-apis-to-check-out-ahead-of-the-artemis-i-launch/image-21.png"
    width="1200"
      height="896"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/6-space-related-apis-to-check-out-ahead-of-the-artemis-i-launch/image-22.png"
    width="1200"
      height="902"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/6-space-related-apis-to-check-out-ahead-of-the-artemis-i-launch/image-23.png"
    width="1200"
      height="902"></figure>
<p>There&rsquo;s a lot of other endpoints, some of which I don&rsquo;t fully understand, but here&rsquo;s one that returns the weather on Mars as reported by <a href="https://mars.nasa.gov/insight/"  target="_blank" rel="noreferrer">Insight</a>.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">https://api.nasa.gov/insight_weather/?api_key=your-api-key&amp;feedtype=json&amp;ver=1.0</code></pre></div>

<h2 class="relative group">MAAS2 API
    <div id="maas2-api" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#maas2-api" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>It&rsquo;s interesting to think how everything we use is one layer upon another, and whatever we create with those layers is a potential layer for someone else to use. It&rsquo;s true for software, and everything else we use too.</p>
<p>The Curiosity rover includes a component called the <a href="https://cab.inta-csic.es/proyectos/mision-msl-rems/"  target="_blank" rel="noreferrer">REMS</a> environmental station, which records the weather on Mars. That data&rsquo;s transmitted back to Earth, and the MAAS2 API uses it to display the Martian weather for any SOL (a Martian day). Then you can use it for whatever project you dream up.</p>
<p>The request is really simple: <em>(there used to be a Swagger page, but it&rsquo;s gone)</em></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="err">GET</span> <span class="err">https:</span><span class="c1">//api.maas2.apollorion.com
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="mi">200</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">3251</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;terrestrial_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2022-03-24T00:00:00.000Z&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;ls&#34;</span><span class="p">:</span> <span class="mi">195</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;season&#34;</span><span class="p">:</span> <span class="s2">&#34;Month 7&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;min_temp&#34;</span><span class="p">:</span> <span class="mi">-68</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;max_temp&#34;</span><span class="p">:</span> <span class="mi">-5</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;pressure&#34;</span><span class="p">:</span> <span class="mi">760</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;pressure_string&#34;</span><span class="p">:</span> <span class="s2">&#34;Higher&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;abs_humidity&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;wind_speed&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;atmo_opacity&#34;</span><span class="p">:</span> <span class="s2">&#34;Sunny&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;sunrise&#34;</span><span class="p">:</span> <span class="s2">&#34;05:18&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;sunset&#34;</span><span class="p">:</span> <span class="s2">&#34;17:22&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;local_uv_irradiance_index&#34;</span><span class="p">:</span> <span class="s2">&#34;Moderate&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;min_gts_temp&#34;</span><span class="p">:</span> <span class="mi">-66</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;max_gts_temp&#34;</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;wind_direction&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;sol&#34;</span><span class="p">:</span> <span class="mi">3423</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;unitOfMeasure&#34;</span><span class="p">:</span> <span class="s2">&#34;Celsius&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;TZ_Data&#34;</span><span class="p">:</span> <span class="s2">&#34;America/Port_of_Spain&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h2 class="relative group">SpaceX API (unofficial)
    <div id="spacex-api-unofficial" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#spacex-api-unofficial" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>They state plainly that this is <em>not</em> an official SpaceX API, but it does make accessible a lot of SpaceX data. You don&rsquo;t need an API key, but they limit any single IP address to 50 requests per second - generous until a bunch of people behind one address all try hitting it at once, like maybe in a classroom setting at a school.</p>
<p>Click on the following link, then the &ldquo;docs&rdquo; folder, and scroll to the bottom of the page for a brief description of each available endpoint. Click on any of those endpoints to drill down into the details. There&rsquo;s examples of how to use each one.</p>
<p><a href="https://github.com/r-spacex/SpaceX-API"  target="_blank" rel="noreferrer">r-spacex/SpaceX-API</a></p>
<p>You can get data about the Starlink satellites, which currently returns 160,000 lines of JSON, implying there are thousands of Starlink satellites. I figured there were like maybe a few hundreds currently, but nope&hellip; <em><a href="https://starlinkinsider.com/starlink-launch-statistics/"  target="_blank" rel="noreferrer">thousands</a></em>, with tens of thousands more planned. 😲</p>
<p>If you already know the ID, you can grab the data for just that one. All kinds of interesting info in here, like when the satellite decays (which I assume is when it burns up in the atmosphere), where it is, how high it is and how fast it&rsquo;s going, etc.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="err">GET</span> <span class="err">https:</span><span class="c1">//api.spacexdata.com/v4/starlink/5eed7714096e590006985634
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;spaceTrack&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;CCSDS_OMM_VERS&#34;</span><span class="p">:</span> <span class="s2">&#34;2.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;COMMENT&#34;</span><span class="p">:</span> <span class="s2">&#34;GENERATED VIA SPACE-TRACK.ORG API&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;CREATION_DATE&#34;</span><span class="p">:</span> <span class="s2">&#34;2022-08-27T03:09:42&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;ORIGINATOR&#34;</span><span class="p">:</span> <span class="s2">&#34;18 SPCS&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;OBJECT_NAME&#34;</span><span class="p">:</span> <span class="s2">&#34;STARLINK-24&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;OBJECT_ID&#34;</span><span class="p">:</span> <span class="s2">&#34;2019-029D&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;CENTER_NAME&#34;</span><span class="p">:</span> <span class="s2">&#34;EARTH&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;REF_FRAME&#34;</span><span class="p">:</span> <span class="s2">&#34;TEME&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;TIME_SYSTEM&#34;</span><span class="p">:</span> <span class="s2">&#34;UTC&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;MEAN_ELEMENT_THEORY&#34;</span><span class="p">:</span> <span class="s2">&#34;SGP4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;EPOCH&#34;</span><span class="p">:</span> <span class="s2">&#34;2022-08-26T17:54:37.802304&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;MEAN_MOTION&#34;</span><span class="p">:</span> <span class="mf">15.4627316</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;ECCENTRICITY&#34;</span><span class="p">:</span> <span class="mf">0.0004341</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;INCLINATION&#34;</span><span class="p">:</span> <span class="mf">53.0023</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;RA_OF_ASC_NODE&#34;</span><span class="p">:</span> <span class="mf">137.5737</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;ARG_OF_PERICENTER&#34;</span><span class="p">:</span> <span class="mf">182.2789</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;MEAN_ANOMALY&#34;</span><span class="p">:</span> <span class="mf">177.8198</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;EPHEMERIS_TYPE&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;CLASSIFICATION_TYPE&#34;</span><span class="p">:</span> <span class="s2">&#34;U&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;NORAD_CAT_ID&#34;</span><span class="p">:</span> <span class="mi">44238</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;ELEMENT_SET_NO&#34;</span><span class="p">:</span> <span class="mi">999</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;REV_AT_EPOCH&#34;</span><span class="p">:</span> <span class="mi">17906</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;BSTAR&#34;</span><span class="p">:</span> <span class="mf">0.0013509</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;MEAN_MOTION_DOT&#34;</span><span class="p">:</span> <span class="mf">0.00067781</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;MEAN_MOTION_DDOT&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;SEMIMAJOR_AXIS&#34;</span><span class="p">:</span> <span class="mf">6805.777</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;PERIOD&#34;</span><span class="p">:</span> <span class="mf">93.127</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;APOAPSIS&#34;</span><span class="p">:</span> <span class="mf">430.596</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;PERIAPSIS&#34;</span><span class="p">:</span> <span class="mf">424.687</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;OBJECT_TYPE&#34;</span><span class="p">:</span> <span class="s2">&#34;PAYLOAD&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;RCS_SIZE&#34;</span><span class="p">:</span> <span class="s2">&#34;LARGE&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;COUNTRY_CODE&#34;</span><span class="p">:</span> <span class="s2">&#34;US&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;LAUNCH_DATE&#34;</span><span class="p">:</span> <span class="s2">&#34;2019-05-24&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;SITE&#34;</span><span class="p">:</span> <span class="s2">&#34;AFETR&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;DECAY_DATE&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;DECAYED&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;FILE&#34;</span><span class="p">:</span> <span class="mi">3548620</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;GP_ID&#34;</span><span class="p">:</span> <span class="mi">211075775</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;TLE_LINE0&#34;</span><span class="p">:</span> <span class="s2">&#34;0 STARLINK-24&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;TLE_LINE1&#34;</span><span class="p">:</span> <span class="s2">&#34;1 44238U 19029D   22238.74627086  .00067781  00000-0  13509-2 0  9998&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;TLE_LINE2&#34;</span><span class="p">:</span> <span class="s2">&#34;2 44238  53.0023 137.5737 0004341 182.2789 177.8198 15.46273160179067&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;launch&#34;</span><span class="p">:</span> <span class="s2">&#34;5eb87d30ffd86e000604b378&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;v0.9&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;height_km&#34;</span><span class="p">:</span> <span class="mf">430.358249647551</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;latitude&#34;</span><span class="p">:</span> <span class="mf">11.896846595007474</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;longitude&#34;</span><span class="p">:</span> <span class="mf">-21.623815638425082</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;velocity_kms&#34;</span><span class="p">:</span> <span class="mf">7.653935367803395</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;5eed7714096e590006985634&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Other endpoints provide data about the rockets, their payloads and crews, and even the ships (i.e tugboats) that are involved in the process too&hellip; or you can just check out the current location of the car that Musk launched into space. <em>(sigh)</em></p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">https://api.spacexdata.com/v4/roadster</code></pre></div>

<h2 class="relative group">Solar System OpenData API
    <div id="solar-system-opendata-api" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#solar-system-opendata-api" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The Solar System OpenData API provides access to information about all kinds of celestial bodies - where they&rsquo;re at, their physical characteristics, etc. It doesn&rsquo;t seem to require an API key either, and I don&rsquo;t see anything about rate limiting.</p>
<p><a href="https://api.le-systeme-solaire.net/en/"  target="_blank" rel="noreferrer">Solar system OpenData</a></p>
<p>There&rsquo;s a very helpful description at the bottom of the page, with all the parameters you can send in and all the data you can expect to get back. They&rsquo;ve got a Swagger page setup <a href="https://api.le-systeme-solaire.net/swagger/"  target="_blank" rel="noreferrer">here</a> too, which is convenient for taking things for a spin. (If you&rsquo;re not familiar with it, you can <a href="https://swagger.io/docs/specification/2-0/what-is-swagger/"  target="_blank" rel="noreferrer">read more about Swagger here</a>, but it&rsquo;s basically a way to automate clean documentation for your API that also lets people try out the endpoints.)</p>
<p>You can pull back data on all celestial bodies at once with a simple call to <code>/bodies</code>, or limit the response by attaching an id to the end like &ldquo;lune&rdquo; (French for the moon).</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="err">GET</span> <span class="err">https:</span><span class="c1">//api.le-systeme-solaire.net/rest/bodies/lune
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;lune&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;La Lune&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;englishName&#34;</span><span class="p">:</span> <span class="s2">&#34;Moon&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;isPlanet&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;moons&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;semimajorAxis&#34;</span><span class="p">:</span> <span class="mi">384400</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;perihelion&#34;</span><span class="p">:</span> <span class="mi">363300</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;aphelion&#34;</span><span class="p">:</span> <span class="mi">405500</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;eccentricity&#34;</span><span class="p">:</span> <span class="mf">0.05490</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;inclination&#34;</span><span class="p">:</span> <span class="mf">5.14500</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;mass&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;massValue&#34;</span><span class="p">:</span> <span class="mf">7.34600</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;massExponent&#34;</span><span class="p">:</span> <span class="mi">22</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;vol&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;volValue&#34;</span><span class="p">:</span> <span class="mf">2.19680</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;volExponent&#34;</span><span class="p">:</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;density&#34;</span><span class="p">:</span> <span class="mf">3.34400</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;gravity&#34;</span><span class="p">:</span> <span class="mf">1.62000</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;escape&#34;</span><span class="p">:</span> <span class="mf">2380.00000</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;meanRadius&#34;</span><span class="p">:</span> <span class="mf">1737.00000</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;equaRadius&#34;</span><span class="p">:</span> <span class="mf">1738.10000</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;polarRadius&#34;</span><span class="p">:</span> <span class="mf">1736.00000</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;flattening&#34;</span><span class="p">:</span> <span class="mf">0.00120</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;dimension&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;sideralOrbit&#34;</span><span class="p">:</span> <span class="mf">27.32170</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;sideralRotation&#34;</span><span class="p">:</span> <span class="mf">655.72800</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;aroundPlanet&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;planet&#34;</span><span class="p">:</span> <span class="s2">&#34;terre&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;rel&#34;</span><span class="p">:</span> <span class="s2">&#34;https://api.le-systeme-solaire.net/rest/bodies/terre&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;discoveredBy&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;discoveryDate&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;alternativeName&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;axialTilt&#34;</span><span class="p">:</span> <span class="mf">6.68</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;avgTemp&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;mainAnomaly&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;argPeriapsis&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;longAscNode&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;bodyType&#34;</span><span class="p">:</span> <span class="s2">&#34;Moon&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>You can customize the data coming back too, like limiting it to just the name and whether it&rsquo;s a planet.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="err">GET</span> <span class="err">https:</span><span class="c1">//api.le-systeme-solaire.net/rest/bodies/lune?data=isPlanet,englishName
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;englishName&#34;</span><span class="p">:</span> <span class="s2">&#34;Moon&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;isPlanet&#34;</span><span class="p">:</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>This is where the Swagger page really comes in handy. Click &ldquo;Try it out&rdquo; next to an endpoint, play around with different combinations of data, and hit &ldquo;Execute&rdquo; to see the results and (especially helpful) the exact API call that you can use in your app or whatever.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/6-space-related-apis-to-check-out-ahead-of-the-artemis-i-launch/image-24.png"
    width="682"
      height="343"></figure>

<h2 class="relative group">RocketLaunch.Live
    <div id="rocketlaunchlive" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#rocketlaunchlive" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>One more and then I think I&rsquo;m API&rsquo;d out for the day. :)</p>
<p>The <a href="https://www.rocketlaunch.live/api"  target="_blank" rel="noreferrer">Rocket Launch API</a> provides, shockingly, information about upcoming rocket launches. You need an API key for it, although I think they&rsquo;re free, but there&rsquo;s one call you can make without a key that returns the next 5 launches. And look what&rsquo;s up next&hellip; the Artemis I.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="err">GET</span> <span class="err">https:</span><span class="c1">//fdo.rocketlaunch.live/json/launches/next/5
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;valid_auth&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;count&#34;</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;limit&#34;</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;total&#34;</span><span class="p">:</span> <span class="mi">117</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;last_page&#34;</span><span class="p">:</span> <span class="mi">24</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;result&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">38</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;cospar_id&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;sort_date&#34;</span><span class="p">:</span> <span class="s2">&#34;1661776380&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Artemis I (EM-1)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;provider&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;NASA&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;slug&#34;</span><span class="p">:</span> <span class="s2">&#34;nasa&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;vehicle&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">15</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;SLS&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;company_id&#34;</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;slug&#34;</span><span class="p">:</span> <span class="s2">&#34;sls&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;pad&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">36</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;LC-39B&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">61</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Kennedy Space Center&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;state&#34;</span><span class="p">:</span> <span class="s2">&#34;FL&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;statename&#34;</span><span class="p">:</span> <span class="s2">&#34;Florida&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;country&#34;</span><span class="p">:</span> <span class="s2">&#34;United States&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;slug&#34;</span><span class="p">:</span> <span class="s2">&#34;kennedy-space-center&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;missions&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">35</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Artemis I (EM-1)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;description&#34;</span><span class="p">:</span> <span class="s2">&#34;On its debut flight, the Space Launch System (SLS) will send an uncrewed Orion Multi-Purpose Crew Vehicle on a four to six-week mission to the Moon and back, including 6 days in a retrograde lunar orbit.&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;mission_description&#34;</span><span class="p">:</span> <span class="s2">&#34;On its debut flight, the Space Launch System (SLS) will send an uncrewed Orion Multi-Purpose Crew Vehicle on a four to six-week mission to the Moon and back, including 6 days in a retrograde lunar orbit.&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;launch_description&#34;</span><span class="p">:</span> <span class="s2">&#34;A NASA SLS rocket will launch the Artemis I (EM-1) mission on Monday, August 29, 2022 at 12:33 PM (UTC).&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;win_open&#34;</span><span class="p">:</span> <span class="s2">&#34;2022-08-29T12:33Z&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;t0&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;win_close&#34;</span><span class="p">:</span> <span class="s2">&#34;2022-08-29T14:33Z&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;est_date&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;month&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;day&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;year&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;quarter&#34;</span><span class="p">:</span> <span class="kc">null</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;date_str&#34;</span><span class="p">:</span> <span class="s2">&#34;Aug 29&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;tags&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">48</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;Lunar Orbit&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">91</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;Series: NASA Artemis&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">23</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;Test Flight&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">21</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;Uncrewed&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">14</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;Vehicle Debut&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;slug&#34;</span><span class="p">:</span> <span class="s2">&#34;em-1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;weather_summary&#34;</span><span class="p">:</span> <span class="s2">&#34;Humid and Mostly Cloudy\nTemp: 80.89F\nWind: 5.54mph\n&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;weather_temp&#34;</span><span class="p">:</span> <span class="mf">80.89</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;weather_condition&#34;</span><span class="p">:</span> <span class="s2">&#34;Humid and Mostly Cloudy&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;weather_wind_mph&#34;</span><span class="p">:</span> <span class="mf">5.54</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;weather_icon&#34;</span><span class="p">:</span> <span class="s2">&#34;wi-day-cloudy&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;weather_updated&#34;</span><span class="p">:</span> <span class="s2">&#34;2022-08-28T12:00:19+00:00&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;quicktext&#34;</span><span class="p">:</span> <span class="s2">&#34;SLS - Artemis I (EM-1) - Mon Aug 29, 2022 12:33:00 UTC (L-21:53:59) - https://rocketlaunch.live/launch/em-1 for info/stream&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;media&#34;</span><span class="p">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;result&#34;</span><span class="p">:</span> <span class="mi">-1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;suborbital&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;modified&#34;</span><span class="p">:</span> <span class="s2">&#34;2022-08-27T14:57:20+00:00&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span></span></span></code></pre></div></div>
<p>So many APIs, so little time. Hopefully this gives you a small taste of what&rsquo;s out there though.</p>
<p>I know I&rsquo;ll be tuning in to watch the launch on NASA&rsquo;s live stream. Just think.. if all goes well, we&rsquo;ll be on our way to colonizing the moon and then Mars! Amazing&hellip;</p>
]]></content:encoded><media:content url="https://grantwinney.com/6-space-related-apis-to-check-out-ahead-of-the-artemis-i-launch/feature.webp" medium="image" type="image/webp"/></item><item><title>Displaying an IIS hosted site in CEFSharp</title><link>https://grantwinney.com/displaying-an-iis-hosted-site-in-cefsharp/</link><pubDate>Tue, 16 Aug 2022 03:56:47 +0000</pubDate><guid>https://grantwinney.com/displaying-an-iis-hosted-site-in-cefsharp/</guid><description>Thanks to CEFSharp, we can finally bring WinForms to the web! That didn&amp;rsquo;t sound right. Okay, let&amp;rsquo;s just look at hosting a site in IIS and showing it.</description><content:encoded><![CDATA[<p>A few weeks ago I shared how you can use <a href="http://cefsharp.github.io/"  target="_blank" rel="noreferrer">CEFSharp</a> to display an html page. I called it hosting, but uh, it was really just displaying a single html page that was baked into the project itself. Yeah, cutting edge, I know. It&rsquo;s WinForms, the bar is low folks.</p>
<p><a href="https://grantwinney.com/hosting-a-simple-webpage-in-winforms-with-cefsharp/"  target="_blank" rel="noreferrer">Host a simple webpage in WinForms with CefSharp</a></p>
<p>This time we&rsquo;ll host the same page in IIS and see how CEFSharp can still interact with it. I won&rsquo;t give a whole overview of IIS, but if you want to try it yourself, here&rsquo;s a few steps.</p>
<blockquote><p>The code in this article is available on <a href="https://github.com/grantwinney/Surviving-WinForms/tree/master/Web/CEFSharp/BasicCefSharpIIS"  target="_blank" rel="noreferrer">GitHub</a>, if you&rsquo;d like to use it in your own projects or just follow along while you read.</p>
</blockquote><p>Enable IIS in Windows Features, if it&rsquo;s not already.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/displaying-an-iis-hosted-site-in-cefsharp/image-6.png"
    width="562"
      height="394"></figure>
<p>Copy the <code>BasicCefSharp_Site</code> folder from <a href="https://github.com/grantwinney/Surviving-WinForms/tree/master/Web/CEFSharp/BasicCefSharpIIS"  target="_blank" rel="noreferrer">my sample code</a> into the <code>c:\inetpub\wwwroot</code> directory. Reusing that location will make your life easier, since there&rsquo;s some security on the <code>wwwroot</code> folder that you won&rsquo;t have to recreate.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/displaying-an-iis-hosted-site-in-cefsharp/image-14.png"
    width="513"
      height="233"></figure>
<p>In IIS, right-click sites and add a new website. Nothing matters in here, other than pointing to the correct location on disk, and changing the port if you don&rsquo;t want to interrupt something else already running locally on the default port 80.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/displaying-an-iis-hosted-site-in-cefsharp/image-12.png"
    width="587"
      height="676"></figure>
<p>Open the new website in your browser and you should see the sample page in all its amazing hypertext markup glory.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/displaying-an-iis-hosted-site-in-cefsharp/image-15.png"
    width="321"
      height="140"></figure>
<p>Fire up the WinForms project, and give it a go. You&rsquo;ll need to change line 27 if you picked a different port. Otherwise, it behaves like it did <a href="https://grantwinney.com/hosting-a-simple-webpage-in-winforms-with-cefsharp/"  target="_blank" rel="noreferrer">last time</a>, except now you&rsquo;re interacting with an actual website running locally, and not just a one-off html page.</p>
<p>I couldn&rsquo;t help monkeying with it a little bit though, adding an address bar that subscribes to the CEFSharp control&rsquo;s <code>AddressChanged</code> event handler to display the current URL. Oh, and it now has <em>twice</em> as many hypertextual markedup pages!</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/displaying-an-iis-hosted-site-in-cefsharp/cefsharpiis-1.gif"
    width="686"
      height="283"></figure>
]]></content:encoded><media:content url="https://grantwinney.com/displaying-an-iis-hosted-site-in-cefsharp/feature.webp" medium="image" type="image/webp"/></item><item><title>Enjoying the wins, accepting the losses</title><link>https://grantwinney.com/enjoying-the-wins-accepting-the-losses/</link><pubDate>Tue, 09 Aug 2022 13:07:49 +0000</pubDate><guid>https://grantwinney.com/enjoying-the-wins-accepting-the-losses/</guid><description>When our code isn&amp;rsquo;t clicking, negativity can quickly overshadow all the positive. That&amp;rsquo;s when we need to remember our victories!</description><content:encoded><![CDATA[<p>Sometimes, when I try to code something, pouring extra hours of effort into it, I can just <em>feel</em> how close all the pieces are to falling into place. Then I realize I&rsquo;m going in circles, spending more time than I meant to, and a solution, whatever it might be, has escaped me at the moment. <a href="https://www.delftstack.com/howto/git/git-remove-uncommitted-changes/#use-git-checkout-to-remove-uncommitted-changes-in-git"  target="_blank" rel="noreferrer">Git checkout</a>, try again later.</p>
<p>Other times, like this last weekend, the extra hours pay off. I needed to make some adjustments to our test environment to support QA efforts, but it included some significant refactoring of one of our solutions. A couple times I hit a wall and had my trigger finger hovering over the &ldquo;git checkout&rdquo; command.</p>
<p>Then I had a conversation with a coworker, who helped me realize that I had already solved the problem and something else was preventing it from working. Voila, problem solved! What a great feeling, knowing the gamble paid off, and I reworked something in a way that&rsquo;ll benefit the entire team moving forward.</p>
<p>The trick now is to hang on to that memory, to get me through the next thing that doesn&rsquo;t work out so well. I used to think there were things that were beyond my ability to understand, things that I just couldn&rsquo;t figure out. Now I realize there are things that take me a short time to understand, and things that take a long time. Maybe more time than I have right now.</p>
<p>Even when I do call it quits and abandon the attempt, it&rsquo;s seldom a waste. It casts light on things that were dark and brings with it a certain understanding that&rsquo;ll apply to other types of problems too. And to paraphrase <a href="https://www.goodreads.com/quotes/8287-i-have-not-failed-i-ve-just-found-10-000-ways-that"  target="_blank" rel="noreferrer">Thomas Edison</a>, I&rsquo;ve learned one way <em>not</em> to fix it. Life goes on; next time will be better.</p>
]]></content:encoded><media:content url="https://grantwinney.com/enjoying-the-wins-accepting-the-losses/feature.webp" medium="image" type="image/webp"/></item><item><title>Host a simple webpage in WinForms with CefSharp</title><link>https://grantwinney.com/hosting-a-simple-webpage-in-winforms-with-cefsharp/</link><pubDate>Tue, 28 Jun 2022 23:07:09 +0000</pubDate><guid>https://grantwinney.com/hosting-a-simple-webpage-in-winforms-with-cefsharp/</guid><description>WinForms and the web. Like oil and water, they don&amp;rsquo;t mix well. But with CEFSharp, they mix a LOT better. Let&amp;rsquo;s check it out.</description><content:encoded><![CDATA[<p>WinForms and the web. Like oil and water, they don&rsquo;t mix well. The web is the future, hands down. If I had an idea for an app that I wanted to share with the world, I&rsquo;d make it a website, not a WinForms app. Who wants to worry about distribution and piracy and having to support nigh infinite number of slightly different machines it might run on? But WinForms isn&rsquo;t going away any time soon either.</p>
<p>Most companies with legacy WinForms apps, representing hundreds of IT personnel who spent decades (<em>hundreds of thousands</em> of hours of business decisions and codified logic, testing and support, documentation and a proven track record), are not going to cut over to a website overnight. Not when those apps continue to perform and bring in a profit.</p>
<p>What if you wanted to start slowly introducing some webby stuff over time though? You could build new features as a website and host that <em>inside</em> the app, slowly replacing more and more of the old UI with the new. Like a wasp laying an egg in a tarantula, the website slowly devours the WinForms code around it. &hellip; Feel free to use that in your argument when it comes time to justify things later.</p>

<h2 class="relative group">Adding CefSharp to a project
    <div id="adding-cefsharp-to-a-project" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#adding-cefsharp-to-a-project" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There&rsquo;s a library called <a href="https://bitbucket.org/chromiumembedded/cef/src/master/"  target="_blank" rel="noreferrer">CEF</a> that makes it possible to embed a Chromium browser in an application, and <em>another</em> library called <a href="http://cefsharp.github.io/"  target="_blank" rel="noreferrer">CefSharp</a> that wraps the first one to use with the .NET Framework. With that combo, we can host HTML content inside WinForms. I&rsquo;ve used CefSharp before, and it&rsquo;s really powerful once you get the hang of it. Like everything in life, there&rsquo;s a learning curve. Time to dig in.</p>
<p>Fire up a new WinForms app, then search for CefSharp in the NuGet gallery and add the one for WinForms.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/hosting-a-simple-webpage-in-winforms-with-cefsharp/image-27.png"
    width="1058"
      height="619"></figure>
<p>After a few moments and some output in the bottom pane, you&rsquo;ll see a few references on the right and a couple new design components in the toolbox on the left. If you&rsquo;re one of those people who customizes everything, then check for output on the top, references on the left, and the components on your other monitor.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/hosting-a-simple-webpage-in-winforms-with-cefsharp/image-29.png"
    width="322"
      height="132"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/hosting-a-simple-webpage-in-winforms-with-cefsharp/image-28.png"
    width="247"
      height="155"></figure>
<p>Add a new file to your project, choose &ldquo;text file&rdquo;, and name it &ldquo;sample.html&rdquo;. Throw some html in there, and maybe some JavaScript. Anything will do. There are few absolute rules in web development, and having 20 ways to do any one thing is something I <em>love</em> as a developer. It&rsquo;s just.. it&rsquo;s great. 😢</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/hosting-a-simple-webpage-in-winforms-with-cefsharp/image-30.png"
    width="786"
      height="443"></figure>
<p>Here&rsquo;s what mine looks like:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>Sample Webpage<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">style</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/css&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">div</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">vertical-align</span><span class="p">:</span> <span class="kc">bottom</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="nt">label</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">float</span><span class="p">:</span> <span class="kc">left</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">width</span><span class="p">:</span> <span class="mi">100</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="nt">input</span><span class="o">,</span> <span class="nt">select</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">margin-bottom</span><span class="p">:</span> <span class="mi">10</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">width</span><span class="p">:</span> <span class="mi">200</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="kd">function</span> <span class="nx">buttonPress</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">alert</span><span class="p">(</span><span class="s1">&#39;heyyy&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">form</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">label</span><span class="p">&gt;</span>Name:<span class="p">&lt;/</span><span class="nt">label</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">input</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;name&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">input</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;send&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;button&#34;</span> <span class="na">value</span><span class="o">=</span><span class="s">&#34;&amp;#171;&#34;</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;width:30px; vertical-align: bottom;&#34;</span> <span class="na">onClick</span><span class="o">=</span><span class="s">&#34;buttonPress()&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">label</span><span class="p">&gt;</span>Occupation:<span class="p">&lt;/</span><span class="nt">label</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">select</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;occupation&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;</span><span class="nt">option</span><span class="p">&gt;</span>--- SELECT ONE ---<span class="p">&lt;/</span><span class="nt">option</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;</span><span class="nt">option</span><span class="p">&gt;</span>Brand Evangelist<span class="p">&lt;/</span><span class="nt">option</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;</span><span class="nt">option</span><span class="p">&gt;</span>Dynamic Web Mystic<span class="p">&lt;/</span><span class="nt">option</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;</span><span class="nt">option</span><span class="p">&gt;</span>Emergent Media Maker<span class="p">&lt;/</span><span class="nt">option</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;</span><span class="nt">option</span><span class="p">&gt;</span>Global Talent Supplier<span class="p">&lt;/</span><span class="nt">option</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;</span><span class="nt">option</span><span class="p">&gt;</span>Happiness Mindset Exec<span class="p">&lt;/</span><span class="nt">option</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;</span><span class="nt">option</span><span class="p">&gt;</span>Head of Mobility Dude<span class="p">&lt;/</span><span class="nt">option</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;</span><span class="nt">option</span><span class="p">&gt;</span>Innovation Sherpa<span class="p">&lt;/</span><span class="nt">option</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;</span><span class="nt">option</span><span class="p">&gt;</span>Information Advisor<span class="p">&lt;/</span><span class="nt">option</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;</span><span class="nt">option</span><span class="p">&gt;</span>Zen Cloud Deputy<span class="p">&lt;/</span><span class="nt">option</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;/</span><span class="nt">select</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">label</span><span class="p">&gt;</span>Graduation:<span class="p">&lt;/</span><span class="nt">label</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">input</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;graduation&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;datetime-local&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">form</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p>After you&rsquo;ve got your fancy new page:</p>
<ul>
<li>Change the &ldquo;Copy to output directory&rdquo; property to &ldquo;Copy always&rdquo; for the file.</li>
<li>Open the Form and drop a ChromiumWebBrowser control on there.</li>
<li>In the Form constructor, load your new page.</li>
</ul>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="n">Form1</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">InitializeComponent</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">chromiumWebBrowser1</span><span class="p">.</span><span class="n">LoadUrl</span><span class="p">(</span><span class="s">$@&#34;{Application.StartupPath}\sample.html&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>What do you see when you fire it up? Does it load the page after a few moments? My sample&rsquo;s a little elaborate, with a few fields on the Form, and a few matching fields in the website too, and here&rsquo;s what it looks like. Because next&hellip; we&rsquo;re gonna take a peek at those handlers.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="JavaScript Alert"
    src="/hosting-a-simple-webpage-in-winforms-with-cefsharp/image-31.png"
    width="721"
      height="359"></figure>

<h2 class="relative group">What are CefSharp handlers?
    <div id="what-are-cefsharp-handlers" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-are-cefsharp-handlers" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>You can see the <a href="https://github.com/cefsharp/CefSharp/wiki/General-Usage#handlers"  target="_blank" rel="noreferrer">full list here</a>, but handlers let you control from your WinForms app all kinds of things - navigation, page loading, the context menu, file dialogs, keyboard events, etc. In their own words:</p>
<blockquote><p>These are simple events that expose a small percentage of the underlying handlers that <code>CEF</code> provides. Implementing these handlers will provide you access to the underlying events and callbacks that are the foundation of CEF.</p>
</blockquote>
<h2 class="relative group">Using handlers to pass data back and forth
    <div id="using-handlers-to-pass-data-back-and-forth" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#using-handlers-to-pass-data-back-and-forth" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Hosting a webpage inside a WinForms is just.. it&rsquo;s.. a little underwhelming tbh. I mean, why not just open it in a web browser unless there&rsquo;s a <em>reason</em> for it living in the WinForms app? And there&rsquo;s only a reason if the two can actually interact somehow. Let&rsquo;s look at a couple ways to do that - the first is handlers.</p>
<p>You have to create a class that implements one of their handler interfaces and its required methods, and then defines what the methods should do. For example, you could create your own keyboard handler class that implements <a href="https://cefsharp.github.io/api/102.0.x/html/T_CefSharp_IKeyboardHandler.htm"  target="_blank" rel="noreferrer">IKeyboardHandler</a>, which (shocker) lets you intercept keyboard events.</p>
<p>The methods can do all kinds of stuff, but many of them have to return true or false, to indicate whether or not the event was handled. In my example, I&rsquo;m passing the Form controls to the class too, so I can update them when a key is pressed on the webpage.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">partial</span> <span class="k">class</span> <span class="nc">Form1</span> <span class="p">:</span> <span class="n">Form</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Form1</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">InitializeComponent</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">chromiumWebBrowser1</span><span class="p">.</span><span class="n">KeyboardHandler</span> <span class="p">=</span> <span class="k">new</span> <span class="n">FancyKeyboardHandler</span><span class="p">(</span><span class="n">txtName</span><span class="p">,</span> <span class="n">dtpGraduation</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">chromiumWebBrowser1</span><span class="p">.</span><span class="n">LoadUrl</span><span class="p">(</span><span class="s">$@&#34;{Application.StartupPath}\sample.html&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">FancyKeyboardHandler</span> <span class="p">:</span> <span class="n">IKeyboardHandler</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">readonly</span> <span class="n">TextBox</span> <span class="n">txtName</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">readonly</span> <span class="n">DateTimePicker</span> <span class="n">dtpGraduation</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">FancyKeyboardHandler</span><span class="p">(</span><span class="n">TextBox</span> <span class="n">txtName</span><span class="p">,</span> <span class="n">DateTimePicker</span> <span class="n">dtpGraduation</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="n">txtName</span> <span class="p">=</span> <span class="n">txtName</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="n">dtpGraduation</span> <span class="p">=</span> <span class="n">dtpGraduation</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">bool</span> <span class="n">OnKeyEvent</span><span class="p">(</span><span class="n">IWebBrowser</span> <span class="n">chromiumWebBrowser</span><span class="p">,</span> <span class="n">IBrowser</span> <span class="n">browser</span><span class="p">,</span> <span class="n">KeyType</span> <span class="n">type</span><span class="p">,</span> <span class="kt">int</span> <span class="n">windowsKeyCode</span><span class="p">,</span> <span class="kt">int</span> <span class="n">nativeKeyCode</span><span class="p">,</span> <span class="n">CefEventFlags</span> <span class="n">modifiers</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">isSystemKey</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">chromiumWebBrowser</span><span class="p">.</span><span class="n">EvaluateScriptAsync</span><span class="p">(</span><span class="s">&#34;document.getElementById(&#39;name&#39;).value;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                            <span class="p">.</span><span class="n">ContinueWith</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">txtName</span><span class="p">.</span><span class="n">Invoke</span><span class="p">(</span><span class="k">new</span> <span class="n">Action</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="n">txtName</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="n">Convert</span><span class="p">.</span><span class="n">ToString</span><span class="p">(</span><span class="n">x</span><span class="p">.</span><span class="n">Result</span><span class="p">.</span><span class="n">Result</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                            <span class="p">})));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">chromiumWebBrowser</span><span class="p">.</span><span class="n">EvaluateScriptAsync</span><span class="p">(</span><span class="s">&#34;document.getElementById(&#39;graduation&#39;).value;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                            <span class="p">.</span><span class="n">ContinueWith</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">dtpGraduation</span><span class="p">.</span><span class="n">Invoke</span><span class="p">(</span><span class="k">new</span> <span class="n">Action</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="n">dtpGraduation</span><span class="p">.</span><span class="n">Value</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">TryParse</span><span class="p">(</span><span class="n">x</span><span class="p">.</span><span class="n">Result</span><span class="p">.</span><span class="n">Result</span><span class="p">.</span><span class="n">ToString</span><span class="p">(),</span> <span class="k">out</span> <span class="kt">var</span> <span class="n">gradDate</span><span class="p">)</span> <span class="p">?</span> <span class="n">gradDate</span> <span class="p">:</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                            <span class="p">})));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">bool</span> <span class="n">OnPreKeyEvent</span><span class="p">(</span><span class="n">IWebBrowser</span> <span class="n">chromiumWebBrowser</span><span class="p">,</span> <span class="n">IBrowser</span> <span class="n">browser</span><span class="p">,</span> <span class="n">KeyType</span> <span class="n">type</span><span class="p">,</span> <span class="kt">int</span> <span class="n">windowsKeyCode</span><span class="p">,</span> <span class="kt">int</span> <span class="n">nativeKeyCode</span><span class="p">,</span> <span class="n">CefEventFlags</span> <span class="n">modifiers</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">isSystemKey</span><span class="p">,</span> <span class="k">ref</span> <span class="kt">bool</span> <span class="n">isKeyboardShortcut</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// If you return true here, the keyboard event is considered &#39;handled&#39;, and OnKeyEvent will not fire.</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Although I think handlers are really handy (ugh), sometimes they aren&rsquo;t quite enough. What about tracking mouse events to capture changes in the webpage? What about going the other direction, and using events in the WinForms UI to change the webpage?</p>

<h2 class="relative group">Executing JavaScript and intercepting data
    <div id="executing-javascript-and-intercepting-data" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#executing-javascript-and-intercepting-data" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There&rsquo;s a few other features in CefSharp that let us dial things up to 11.</p>
<ul>
<li>The <code>ExecuteScriptAsync</code> function lets you define a bit of JavaScript to execute against the webpage, performing some action like setting a value. You could even <em>get</em> a value, and send it back to your WinForms app.</li>
<li>The <code>CefSharp.PostMessage</code> function is what lets you send data back. You call it from within your JavaScript, as if it were any other valid JS command. But.. then you need to intercept that somehow.</li>
<li>And that&rsquo;s where the <code>JavascriptMessageReceived</code> event handler comes in, watching for those posted messages so you can unwrap them.</li>
</ul>
<p>Let&rsquo;s expand on the previous example to include these too. Here&rsquo;s what the updated code looks like.</p>
<p>By watching the <code>LoadingStateChanged</code> event, we know when the page is done loading and all the elements are available. When it&rsquo;s loaded, we can attach a script to watch for values changing on the website, and send those back to the WinForms.</p>
<p>If you want to send values from several different elements on the webpage, and be able to tell which one was sent, JSON is a pretty good solution. At least, it&rsquo;s the one I used below. That comes through the C# side of things as some kind of dynamic <a href="https://docs.microsoft.com/en-us/dotnet/api/system.dynamic.expandoobject?view=net-6.0"  target="_blank" rel="noreferrer">ExpandoObject</a> thing, which happens to implement <code>IDictionary&lt;string, object&gt;</code> so it&rsquo;s trivial to parse the JSON and get the values back out.</p>
<p>Check out the event handlers like <code>cbxOccupation_SelectedIndexChanged</code> below too. It&rsquo;s also trivial to pass data from WinForms back to the website via JavaScript.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">partial</span> <span class="k">class</span> <span class="nc">Form1</span> <span class="p">:</span> <span class="n">Form</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Form1</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">InitializeComponent</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">chromiumWebBrowser1</span><span class="p">.</span><span class="n">KeyboardHandler</span> <span class="p">=</span> <span class="k">new</span> <span class="n">FancyKeyboardHandler</span><span class="p">(</span><span class="n">txtName</span><span class="p">,</span> <span class="n">dtpGraduation</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="n">chromiumWebBrowser1</span><span class="p">.</span><span class="n">LoadingStateChanged</span> <span class="p">+=</span> <span class="n">chromiumWebBrowser1_LoadingStateChanged</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">chromiumWebBrowser1</span><span class="p">.</span><span class="n">JavascriptMessageReceived</span> <span class="p">+=</span> <span class="n">chromiumWebBrowser1_JavascriptMessageReceived</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">chromiumWebBrowser1</span><span class="p">.</span><span class="n">LoadUrl</span><span class="p">(</span><span class="s">$@&#34;{Application.StartupPath}\sample.html&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">void</span> <span class="n">chromiumWebBrowser1_LoadingStateChanged</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">LoadingStateChangedEventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(!</span><span class="n">e</span><span class="p">.</span><span class="n">IsLoading</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">chromiumWebBrowser1</span><span class="p">.</span><span class="n">ExecuteScriptAsync</span><span class="p">(</span><span class="s">@&#34;
</span></span></span><span class="line"><span class="cl"><span class="s">                let occupation = document.getElementById(&#39;occupation&#39;);
</span></span></span><span class="line"><span class="cl"><span class="s">                occupation.addEventListener(&#39;change&#39;, (e) =&gt; {
</span></span></span><span class="line"><span class="cl"><span class="s">                    CefSharp.PostMessage({occupation: occupation.options[occupation.selectedIndex].text});
</span></span></span><span class="line"><span class="cl"><span class="s">                });
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">                let gradDate = document.getElementById(&#39;graduation&#39;);
</span></span></span><span class="line"><span class="cl"><span class="s">                gradDate.addEventListener(&#39;input&#39;, (e) =&gt; {
</span></span></span><span class="line"><span class="cl"><span class="s">                    CefSharp.PostMessage({gradDate: gradDate.value});
</span></span></span><span class="line"><span class="cl"><span class="s">                });
</span></span></span><span class="line"><span class="cl"><span class="s">            &#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">void</span> <span class="n">chromiumWebBrowser1_JavascriptMessageReceived</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">JavascriptMessageReceivedEventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">message</span> <span class="p">=</span> <span class="p">((</span><span class="n">IDictionary</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">object</span><span class="p">&gt;)</span><span class="n">e</span><span class="p">.</span><span class="n">Message</span><span class="p">).</span><span class="n">Single</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="k">switch</span> <span class="p">(</span><span class="n">message</span><span class="p">.</span><span class="n">Key</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">case</span> <span class="s">&#34;gradDate&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">dtpGraduation</span><span class="p">.</span><span class="n">Invoke</span><span class="p">(</span><span class="k">new</span> <span class="n">Action</span><span class="p">(()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">                    <span class="n">dtpGraduation</span><span class="p">.</span><span class="n">Value</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">TryParse</span><span class="p">(</span><span class="n">message</span><span class="p">.</span><span class="n">Value</span><span class="p">.</span><span class="n">ToString</span><span class="p">(),</span> <span class="k">out</span> <span class="kt">var</span> <span class="n">gradDate</span><span class="p">)</span> <span class="p">?</span> <span class="n">gradDate</span> <span class="p">:</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span>
</span></span><span class="line"><span class="cl">                <span class="p">));</span>
</span></span><span class="line"><span class="cl">                <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">case</span> <span class="s">&#34;occupation&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">cbxOccupation</span><span class="p">.</span><span class="n">Invoke</span><span class="p">(</span><span class="k">new</span> <span class="n">Action</span><span class="p">(()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="k">if</span> <span class="p">(</span><span class="n">message</span><span class="p">.</span><span class="n">Value</span><span class="p">.</span><span class="n">ToString</span><span class="p">()</span> <span class="p">!=</span> <span class="s">&#34;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                        <span class="n">cbxOccupation</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="n">message</span><span class="p">.</span><span class="n">Value</span><span class="p">.</span><span class="n">ToString</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">                    <span class="k">else</span>
</span></span><span class="line"><span class="cl">                        <span class="n">cbxOccupation</span><span class="p">.</span><span class="n">SelectedIndex</span> <span class="p">=</span> <span class="p">-</span><span class="m">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="p">}));</span>
</span></span><span class="line"><span class="cl">                <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">void</span> <span class="n">cbxOccupation_SelectedIndexChanged</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">chromiumWebBrowser1</span><span class="p">.</span><span class="n">ExecuteScriptAsync</span><span class="p">(</span><span class="s">$&#34;document.getElementById(&#39;occupation&#39;).value = &#39;{cbxOccupation.Text}&#39;;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">void</span> <span class="n">dtpGraduation_ValueChanged</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">chromiumWebBrowser1</span><span class="p">.</span><span class="n">ExecuteScriptAsync</span><span class="p">(</span><span class="s">$&#34;document.getElementById(&#39;graduation&#39;).value = &#39;{dtpGraduation.Value:yyyy-MM-ddThh:mm}&#39;;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">void</span> <span class="n">txtName_TextChanged</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">chromiumWebBrowser1</span><span class="p">.</span><span class="n">ExecuteScriptAsync</span><span class="p">(</span><span class="s">$&#34;document.getElementById(&#39;name&#39;).value = &#39;{txtName.Text}&#39;;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">FancyKeyboardHandler</span> <span class="p">:</span> <span class="n">IKeyboardHandler</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">readonly</span> <span class="n">TextBox</span> <span class="n">txtName</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">readonly</span> <span class="n">DateTimePicker</span> <span class="n">dtpGraduation</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">FancyKeyboardHandler</span><span class="p">(</span><span class="n">TextBox</span> <span class="n">txtName</span><span class="p">,</span> <span class="n">DateTimePicker</span> <span class="n">dtpGraduation</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="n">txtName</span> <span class="p">=</span> <span class="n">txtName</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="n">dtpGraduation</span> <span class="p">=</span> <span class="n">dtpGraduation</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">bool</span> <span class="n">OnKeyEvent</span><span class="p">(</span><span class="n">IWebBrowser</span> <span class="n">chromiumWebBrowser</span><span class="p">,</span> <span class="n">IBrowser</span> <span class="n">browser</span><span class="p">,</span> <span class="n">KeyType</span> <span class="n">type</span><span class="p">,</span> <span class="kt">int</span> <span class="n">windowsKeyCode</span><span class="p">,</span> <span class="kt">int</span> <span class="n">nativeKeyCode</span><span class="p">,</span> <span class="n">CefEventFlags</span> <span class="n">modifiers</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">isSystemKey</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">chromiumWebBrowser</span><span class="p">.</span><span class="n">EvaluateScriptAsync</span><span class="p">(</span><span class="s">&#34;document.getElementById(&#39;name&#39;).value;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                            <span class="p">.</span><span class="n">ContinueWith</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">txtName</span><span class="p">.</span><span class="n">Invoke</span><span class="p">(</span><span class="k">new</span> <span class="n">Action</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="n">txtName</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="n">Convert</span><span class="p">.</span><span class="n">ToString</span><span class="p">(</span><span class="n">x</span><span class="p">.</span><span class="n">Result</span><span class="p">.</span><span class="n">Result</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                            <span class="p">})));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">chromiumWebBrowser</span><span class="p">.</span><span class="n">EvaluateScriptAsync</span><span class="p">(</span><span class="s">&#34;document.getElementById(&#39;graduation&#39;).value;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                            <span class="p">.</span><span class="n">ContinueWith</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">dtpGraduation</span><span class="p">.</span><span class="n">Invoke</span><span class="p">(</span><span class="k">new</span> <span class="n">Action</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="n">dtpGraduation</span><span class="p">.</span><span class="n">Value</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">TryParse</span><span class="p">(</span><span class="n">x</span><span class="p">.</span><span class="n">Result</span><span class="p">.</span><span class="n">Result</span><span class="p">.</span><span class="n">ToString</span><span class="p">(),</span> <span class="k">out</span> <span class="kt">var</span> <span class="n">gradDate</span><span class="p">)</span> <span class="p">?</span> <span class="n">gradDate</span> <span class="p">:</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                            <span class="p">})));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">bool</span> <span class="n">OnPreKeyEvent</span><span class="p">(</span><span class="n">IWebBrowser</span> <span class="n">chromiumWebBrowser</span><span class="p">,</span> <span class="n">IBrowser</span> <span class="n">browser</span><span class="p">,</span> <span class="n">KeyType</span> <span class="n">type</span><span class="p">,</span> <span class="kt">int</span> <span class="n">windowsKeyCode</span><span class="p">,</span> <span class="kt">int</span> <span class="n">nativeKeyCode</span><span class="p">,</span> <span class="n">CefEventFlags</span> <span class="n">modifiers</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">isSystemKey</span><span class="p">,</span> <span class="k">ref</span> <span class="kt">bool</span> <span class="n">isKeyboardShortcut</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// If you return true here, the keyboard event is considered &#39;handled&#39;, and OnKeyEvent will not fire.</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h2 class="relative group">What&rsquo;s it looks like?
    <div id="whats-it-looks-like" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whats-it-looks-like" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>It&rsquo;s pretty neat I think. Update the fields in the WinForms UI or in the website, and the data is sync&rsquo;d up between the two. The CefSharp repo has <a href="https://github.com/cefsharp/CefSharp.MinimalExample/tree/master/CefSharp.MinimalExample.WinForms"  target="_blank" rel="noreferrer">more examples</a>, if you want to check out what else is possible. They have a lot in their wiki too.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/hosting-a-simple-webpage-in-winforms-with-cefsharp/simplecef.gif"
    width="686"
      height="282"></figure>
<p>This was a really brief (admittedly contrived!) example of what CefSharp can do, but hopefully it gets you started. I&rsquo;m thinking of checking out a couple other, more real-world, scenarios in future posts. It&rsquo;d be interesting to show a webpage that wasn&rsquo;t just included in the project itself (maybe from this blog?) and interact with it. Or maybe hosting a website in IIS and showing CefSharp interacting with that too? The sky&rsquo;s the limit.</p>
<p>If you find a use for CefSharp, and this post gave you a jump start, please leave a comment below! I&rsquo;d be interested in hearing more about what you&rsquo;re trying to do. Good luck!</p>
]]></content:encoded><media:content url="https://grantwinney.com/hosting-a-simple-webpage-in-winforms-with-cefsharp/feature.webp" medium="image" type="image/webp"/></item><item><title>What is Manifest V3 and why is Google pestering me about it?</title><link>https://grantwinney.com/what-is-manifest-v3-and-why-is-google-pestering-me/</link><pubDate>Mon, 06 Jun 2022 11:30:43 +0000</pubDate><guid>https://grantwinney.com/what-is-manifest-v3-and-why-is-google-pestering-me/</guid><description/><content:encoded><![CDATA[<p>If you&rsquo;ve ever dug into the underpinnings of a browser extension, or maybe even <a href="https://grantwinney.com/making-your-first-chrome-extension"  target="_blank" rel="noreferrer">created one yourself</a>, you&rsquo;ve seen the manifest.json file that acts as a sort of usage guide for an extension. Not the kind of usage guide most people would want to read, but it&rsquo;s vital for browsers.</p>
<p>The manifest file tells them what name and version to display, who the author is, what permissions to request access to, which icons to display, what CSS and JS files to load and when. It&rsquo;s important, but once you get the hang of it, pretty simple to implement. You create it and move on, only ever reopening it to bump the version when you&rsquo;ve got something new to publish, and maybe requesting a new permission.</p>
<p>For quite awhile now, whenever I visit the Chrome dashboard, I get a notice at the top about migrating to Manifest V3 (aka MV3). Fair enough.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-manifest-v3-and-why-is-google-pestering-me/image.png"
    width="868"
      height="197"></figure>
<p>Apparently though that notice wasn&rsquo;t enough to make developers care, because they&rsquo;ve sent emails with links to blog posts, and now when I load an extension to debug it, it immediately reports an error. Hm, is it related to the issue I wanted to debug? Nope, just Chromium abusing functionality to send me more reminders.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-manifest-v3-and-why-is-google-pestering-me/image-3.png"
    width="869"
      height="477"></figure>
<p>What they&rsquo;re badgering extension authors about though is <em>much</em> more than just a change in format for a single file. Google is spearheading major changes to how extensions are written and interact with the browser, in the name of privacy and security, and those changes are coming soon. As of writing this post, no one can publish new extensions using version 2 (MV2) anymore. In 6 months, everyone has to update if they want to continue hosting their extension in the Chrome web store and having it work in Chromium-based browsers.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-manifest-v3-and-why-is-google-pestering-me/image-4.png"
    width="845"
      height="276"></figure>
<p>Source: <a href="https://developer.chrome.com/docs/extensions/mv3/mv2-sunset/"  target="_blank" rel="noreferrer">Manifest V2 support timeline</a></p>

<h2 class="relative group">What does MV3 bring to the table?
    <div id="what-does-mv3-bring-to-the-table" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-does-mv3-bring-to-the-table" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The very first thing <a href="https://blog.chromium.org/2020/12/manifest-v3-now-available-on-m88-beta.html"  target="_blank" rel="noreferrer">Google</a> mentions is disallowing remotely hosted code. That&rsquo;s not a bad thing. In fact, it&rsquo;s incredibly and obviously good. Imagine the havoc I could wreak if I wrote an extension with one file in it that simply downloads and executes a dozen other files. The web store sees nothing suspicious, because there&rsquo;s barely anything <em>to</em> see. Then you install my extension, and it&rsquo;s anyone&rsquo;s guess what all those external files are actually doing, and they could be changing daily and downloading and executing yet more files.</p>
<p>My gut instinct is that, like a politician trying to peddle a piece of legislation by touting the one good thing in it everyone can agree on, they&rsquo;re leading with this and then slipping in a handful of smaller and more contentious stuff. Let&rsquo;s check it out.</p>

<h3 class="relative group">Service Workers
    <div id="service-workers" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#service-workers" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>No more background pages.</p>
<blockquote><p>First, we are introducing <a href="https://developers.google.com/web/fundamentals/primers/service-workers"  target="_blank" rel="noreferrer">service workers</a> as a replacement for background pages. Unlike persistent background pages, which remain active in the background and consume system resources regardless of whether the extension is actively using them, service workers are ephemeral. This ephemerality allows Chrome to lower overall system resource utilization since the browser can start up and tear down service workers as needed.</p>
</blockquote><p>This is interesting. It&rsquo;s already possible to tell a background page to not &ldquo;remain active in the background and consume system resources&rdquo;, simply by setting the &ldquo;persistent&rdquo; flag to false in the manifest.json. They recommend it all over their documentation. Easy-peasy.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">&#34;background&#34;</span><span class="err">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;scripts&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;js/third-party/jquery-3.6.0.min.js&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;js/third-party/axios.min.js&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;js/third-party/toastr.min.js&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;js/shared.js&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;js/background.js&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;persistent&#34;</span><span class="p">:</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="err">,</span></span></span></code></pre></div></div>
<p>It doesn&rsquo;t sound like this change offers a performance boost for end users, but for developers it&rsquo;ll be a hassle. Why not just default the persistent flag to false when not specified? Or add a new permission that asks the user to approve running the extension &ldquo;persistently&rdquo;? Admittedly, the former might just lead to a flurry of updates setting it back to <code>true</code>, while the latter might lead to end-user confusion.</p>
<p>I think most extensions are from people who, like me, had an idea to share and cranked something out in a weekend or two, but now I&rsquo;ve got to learn about <a href="https://developer.chrome.com/docs/workbox/service-worker-overview/"  target="_blank" rel="noreferrer">what service workers are</a>, <a href="https://developer.chrome.com/docs/extensions/mv3/migrating_to_service_workers/"  target="_blank" rel="noreferrer">how to migrate a background page to a service worker</a>, and apparently about <a href="https://web.dev/es-modules-in-sw/"  target="_blank" rel="noreferrer">ES modules in service workers</a> too because I have some shared code in a separate file that now needs to be imported instead of just listed out in the order they should be loaded (as in the snippet above). Best scenario, I spend a week or two learning new concepts and in the end, if all goes well, end users notice absolutely no change and don&rsquo;t get some weird error that breaks things.</p>

<h3 class="relative group">Declarative APIs
    <div id="declarative-apis" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#declarative-apis" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>A new set of APIs that allow an extension to affect the page without being handed its entire contents.</p>
<blockquote><p>The <code>declarativeNetRequest</code> API is an example of how Chrome is working to enable extensions, including ad blockers, to continue delivering their core functionality without requiring the extension to have access to potentially sensitive user data. This will allow many of the powerful extensions in our ecosystem to continue to provide a seamless user experience while still respecting user privacy.</p>
</blockquote><p>The above didn&rsquo;t mean much to me, other than making me smile (or is it a smirk?) at the idea that an ad company, whose <a href="https://abc.xyz/investor/static/pdf/20210203_alphabet_10K.pdf?cache=b44182d"  target="_blank" rel="noreferrer">SEC filing</a> includes a section called &ldquo;how we make money&rdquo; that&rsquo;s <em>all</em> about advertising, would claim to be working to enable ad blocker extensions. Maybe I&rsquo;m just cynical, but that seems like a huge conflict of interest.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-manifest-v3-and-why-is-google-pestering-me/image-5.png"
    width="814"
      height="455"></figure>
<p>Their blog post titled <a href="https://blog.chromium.org/2019/06/web-request-and-declarative-net-request.html"  target="_blank" rel="noreferrer"><em>Web Request and Declarative Net Request: Explaining the impact on Extensions in Manifest V3</em></a> has a couple useful diagrams <em>(shown below)</em> that make the changes clearer. In MV2, the browser hands over the whole page to an extension to modify as needed, but in V3 the browser will allow an extension to define what it wants to do under certain circumstances by setting up rules, and then apply those rules. No more handing over the whole page.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-manifest-v3-and-why-is-google-pestering-me/image-6.png"
    width="1600"
      height="1200"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-manifest-v3-and-why-is-google-pestering-me/image-7.png"
    width="1600"
      height="1200"></figure>
<p>Source: <a href="https://blog.chromium.org/2019/06/web-request-and-declarative-net-request.html"  target="_blank" rel="noreferrer"><em>Web Request and Declarative Net Request: Explaining the impact on Extensions in Manifest V3</em></a></p>
<p>That seems pretty smart at first glance, like a decoupling of concerns, but it leaves me wondering just what kind of rules can I create? How flexible are they? Is it possible to block content (ads, trackers, etc) being loaded by known third party sites? Do they each have to be listed in a separate internal &ldquo;rule&rdquo;, changes requiring new uploads to the store, as opposed to <a href="https://help.getadblock.com/support/solutions/articles/6000066909-introduction-to-filter-lists/"  target="_blank" rel="noreferrer">external block lists</a> that can be modified frequently by anyone who wishes to contribute? Per some other <a href="https://developer.chrome.com/docs/extensions/reference/declarativeNetRequest/"  target="_blank" rel="noreferrer">docs</a>, it <em>seems</em> like there&rsquo;ll be a way in the <a href="https://developer.chrome.com/docs/extensions/reference/declarativeNetRequest/#method-updateDynamicRules"  target="_blank" rel="noreferrer">updateDynamicRules</a> method&hellip; maybe.</p>
<p>In the case of <a href="/hide-comments-everywhere/" >Hide Comments Everywhere</a>, I need to block any elements of a page that show comments. These might be hosted by a third party like Disqus, but more frequently they&rsquo;re just embedded in the page and selectable by using a <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors"  target="_blank" rel="noreferrer">CSS selector</a>. Will that still work? Maybe, maybe not.</p>

<h2 class="relative group">Does MV3 make things worse for us?
    <div id="does-mv3-make-things-worse-for-us" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#does-mv3-make-things-worse-for-us" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you&rsquo;ve gotten this far and it&rsquo;s not clear from above, I&rsquo;m no expert in this. I&rsquo;ve got some answers, and more questions. I&rsquo;ve written a few addons, I know a few things, and I know enough to know I don&rsquo;t know much. There are some addons, however, that I use frequently and would really miss if they weren&rsquo;t around. Here&rsquo;s what they have to say.</p>

<h3 class="relative group">Ghostery: It&rsquo;s a &ldquo;detrimental step back&rdquo; and &ldquo;ultimately user hostile&rdquo;
    <div id="ghostery-its-a-detrimental-step-back-and-ultimately-user-hostile" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#ghostery-its-a-detrimental-step-back-and-ultimately-user-hostile" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Ghostery is a popular ad and tracker blocker, and clearly going through hell with the MV3 changes.</p>
<blockquote><p>With enforcement of Manifest V3, Google dramatically limits capabilities of browser extensions. It removes access to powerful APIs that allowed us to provide innovation in privacy protection. Being subjected to those constraints, we have to re-invent the way our extensions operate. Intended or not, Manifest V3 takes choice away from users, exposing them to new threats. Manifest V3 is ultimately user hostile.</p>
</blockquote><p>That&rsquo;s the tl;dr version, but the rest of their post is a fascinating read and I&rsquo;d suggest checking it out. The section <em>&ldquo;no incremental updates&rdquo;</em> makes me think maybe my addon is screwed in its current state, although they mention dynamicRules as a possible workaround too (for their case though, it&rsquo;s far too limited).</p>
<blockquote><p>At Ghostery, we publish updates to our ad blocking list daily. Yet Manifest V3, which was designed to reduce extension review times, would force us to release a new extension version each time we update the block list. Some updates are allowed in a form of <code>dynamicRules</code>, but the number of entries is limited to 5,000 and it has to be shared with user controls, which makes it unusable.</p>
<p>Manifest V3&rsquo;s idea was to simplify WebExtensions to speed up the review process. We will put that to test - we plan to release updates to our extension as often as needed, every single day if required. We hope that reviewers will be able to keep up with it.</p>
</blockquote><p>They close with:</p>
<blockquote><p>Manifest V3 is an opinionated specification; it enforces technical limitations with the goal of improving user experience. That looks good on paper, but the reality is quite different.</p>
<p>Manifest V3 is a detrimental step back for internet privacy. Instead of reinventing the wheel, we would prefer to focus on finding new ways to prevent tracking. This is after all what browser extensions are and should be, a playing field for innovation and the express lane for browser enhancement.</p>
</blockquote><p><em>Source:</em> <a href="https://www.ghostery.com/blog/manifest-v3-the-ghostery-perspective"  target="_blank" rel="noreferrer"><em>Manifest V3: The Ghostery perspective</em></a></p>

<h3 class="relative group">EFF: It&rsquo;s &ldquo;outright harmful to privacy efforts&rdquo;
    <div id="eff-its-outright-harmful-to-privacy-efforts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#eff-its-outright-harmful-to-privacy-efforts" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The Electronic Frontier Foundation, author of privacy extensions like Privacy Badger and HTTPS Everywhere, has nothing kind to say about it either.</p>
<blockquote><p>It will restrict the capabilities of web extensions—especially those that are designed to monitor, modify, and compute alongside the conversation your browser has with the websites you visit. Under the new specifications, extensions like these– like some privacy-protective tracker blockers– will have greatly reduced capabilities. Google’s efforts to limit that access is concerning, especially considering that <a href="https://spreadprivacy.com/biggest-tracker-networks/"  target="_blank" rel="noreferrer">Google has trackers installed on 75% of the top one million websites</a>.</p>
</blockquote><p><em>Source:</em> <a href="https://www.eff.org/deeplinks/2021/12/chrome-users-beware-manifest-v3-deceitful-and-threatening"  target="_blank" rel="noreferrer"><em>Chrome Users Beware: Manifest V3 is Deceitful and Threatening</em></a></p>

<h3 class="relative group">uBlock Origin: It&rsquo;s &ldquo;no more than the implementation of one specific filtering engine, and a rather limited one&rdquo;
    <div id="ublock-origin-its-no-more-than-the-implementation-of-one-specific-filtering-engine-and-a-rather-limited-one" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#ublock-origin-its-no-more-than-the-implementation-of-one-specific-filtering-engine-and-a-rather-limited-one" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>I&rsquo;ve used uBlock Origin for a long time. There&rsquo;s a post from a few years ago in the Chromium forum where its author, Raymond Hill, expressed concern that <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=896897&amp;desc=2#c23"  target="_blank" rel="noreferrer">the new changes will kill off his addons</a>.</p>
<blockquote><p>If this (quite limited) declarativeNetRequest API ends up being the only way content blockers can accomplish their duty, this essentially means that two content blockers I have maintained for years, uBlock Origin (&ldquo;uBO&rdquo;) and uMatrix, can no longer exist.</p>
</blockquote><p>Somewhat ironically, the thread was closed on April Fools Day this year.</p>

<h2 class="relative group">What other options are there?
    <div id="what-other-options-are-there" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-other-options-are-there" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>So if you&rsquo;re looking for alternatives, what options are there?</p>

<h3 class="relative group">Focus on Firefox support and avoid Chromium browsers
    <div id="focus-on-firefox-support-and-avoid-chromium-browsers" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#focus-on-firefox-support-and-avoid-chromium-browsers" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>If you&rsquo;re looking to totally avoid MV3, then any <a href="https://en.wikipedia.org/wiki/Chromium_%5C%28web_browser%5C%29#Active"  target="_blank" rel="noreferrer">Chromium-based web browser</a> is out. That includes Brave, <a href="https://learn.microsoft.com/en-us/microsoft-edge/extensions-chromium/developer-guide/manifest-v3"  target="_blank" rel="noreferrer">Edge</a>, Opera, and at least a couple dozen others. That&rsquo;s not realistic if your business model depends on it, but then at least you&rsquo;re getting paid to figure this stuff out for your customers. As for the rest of us, it&rsquo;s a pretty lousy situation.</p>
<p><a href="https://blog.mozilla.org/addons/2022/05/18/manifest-v3-in-firefox-recap-next-steps/"  target="_blank" rel="noreferrer">Mozilla is supporting MV3 in Firefox</a>, in acknowledgement that Chromium-based browsers are a huge share of the market and no developer wants to maintain two versions of their extension. They&rsquo;re not tossing out MV2, using a more reasonable approach than Google is capable of. <em>(Read more about their approach in their</em> <a href="https://extensionworkshop.com/documentation/develop/manifest-v3-migration-guide/"  target="_blank" rel="noreferrer"><em>Manifest V3 migration guide</em></a><em>.)</em></p>
<blockquote><p>Mozilla will maintain support for blocking WebRequest in MV3. To maximize compatibility with other browsers, we will also ship support for declarativeNetRequest. We will continue to work with content blockers and other key consumers of this API to identify current and future alternatives where appropriate. Content blocking is one of the most important use cases for extensions, and we are committed to ensuring that Firefox users have access to the best privacy tools available.</p>
</blockquote><p>It seems like that should pay off for them. Anyone willing to learn MV3 will be able to cross-upload to the Firefox store. Anyone unwilling can continue with MV2, still upload to the Firefox store, and encourage friends and family to use Firefox. I like that Mozilla admits what Google will not - that rewriting extensions is going to <em>suck</em> and everyone knows it.</p>
<blockquote><p>We’ve found Service Workers can’t fully support <a href="https://github.com/w3c/webextensions/issues/72"  target="_blank" rel="noreferrer">various use cases</a> we consider important, especially around DOM-related features and APIs. Additionally, the worker environment is not as familiar to regular web developers, and our developer community has expressed that completely rewriting extensions can be tedious for thousands of independent developers of <a href="https://addons.mozilla.org/en-US/firefox/extensions/"  target="_blank" rel="noreferrer">existing extensions</a>.</p>
</blockquote>
<h3 class="relative group">Start with MV2 on Firefox and gradually learn MV3
    <div id="start-with-mv2-on-firefox-and-gradually-learn-mv3" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#start-with-mv2-on-firefox-and-gradually-learn-mv3" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Since Firefox is supporting both, you could write an extension with MV2, for which there are tons of articles and resources to support it, then convert it at your leisure. When it&rsquo;s fully MV3, upload it to Google&rsquo;s store. True, it means double the effort in the long run, but it&rsquo;s an option for new extensions or existing extensions with low adoption in the Chrome web store where starting over later wouldn&rsquo;t be a big deal.</p>

<h3 class="relative group">Take five and get outside 🌞
    <div id="take-five-and-get-outside-" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#take-five-and-get-outside-" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>It&rsquo;s nearly summer, so grab your beverage of choice and go hang outside for awhile. Okay, not a great longterm plan, and I hope that doesn&rsquo;t come off glibly, but the view right now from my swinging chair is warm sunlight streaming through green leaves, while a gentle breeze blows cottonwood fluff around like it&rsquo;s snowing. It&rsquo;s easy at the moment to forget about one company&rsquo;s arrogant behavior.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-manifest-v3-and-why-is-google-pestering-me/lazyday.jpg"
    width="1600"
      height="1200"></figure>
<p>If you do decide to go for it, here&rsquo;s some solid places to start:</p>
<ul>
<li><a href="https://developer.chrome.com/docs/extensions/mv3/intro/"  target="_blank" rel="noreferrer">Welcome to Manifest V3</a> (intro from Google)</li>
<li><a href="https://blog.chromium.org/2019/06/web-request-and-declarative-net-request.html"  target="_blank" rel="noreferrer">Web Request and Declarative Net Request: Explaining the impact on Extensions in Manifest V3</a> (blog post from Google)</li>
<li><a href="https://developer.chrome.com/docs/extensions/reference/declarativeNetRequest/"  target="_blank" rel="noreferrer">chrome.declarativeNetRequest</a> (dev docs from Google)</li>
<li><a href="https://groups.google.com/a/chromium.org/g/chromium-extensions?pli=1"  target="_blank" rel="noreferrer">Chromium Extensions - Google Groups</a> (forums to share stories and tears)</li>
<li><a href="https://brawl.vivaldi.net/2021/12/15/thoughts-about-manifest-v3/"  target="_blank" rel="noreferrer">Thoughts on Manifest v3 | IT &amp; Stuff</a> (someone who went through the conversion and made it work, but it sounds painful)</li>
</ul>
<p>As for me, I haven&rsquo;t decided what I&rsquo;ll do yet. I&rsquo;ll keep my humble little extensions on Firefox and see how things shake out. Most of us have a lot on our plates, jobs to perform and families to support. For me, getting familiar with a host of new concepts to appease Google isn&rsquo;t on my priority list <em>at all,</em> and I&rsquo;d wager a lot of smaller yet useful extensions will simply cease to work come 2023.</p>
<p><strong>Edit (Nov 2022):</strong> <a href="https://grantwinney.com/my-experience-migrating-to-mv3/"  target="_blank" rel="noreferrer">I decided to give it a go</a>. It was truly life-changing.</p>
]]></content:encoded><media:content url="https://grantwinney.com/what-is-manifest-v3-and-why-is-google-pestering-me/feature.webp" medium="image" type="image/webp"/></item><item><title>Beware the bite of the refactor bug</title><link>https://grantwinney.com/beware-the-refactor-bug/</link><pubDate>Sat, 22 Jan 2022 22:45:01 +0000</pubDate><guid>https://grantwinney.com/beware-the-refactor-bug/</guid><description>Refactoring code is part of the dev life, and can even help keep the code healthy, but going too far can do more harm than good. Ever after a decade of writing code, I still have to remind myself from time to time!</description><content:encoded><![CDATA[<p>It&rsquo;s been an interesting couple of weeks. After a break during Christmas, burning through unused PTO, I came back ready to extend some logic in an old screen. In other words, another day in monolithic WinForms paradise.🍹 And then I proceeded to commit one of the cardinal sins of development - rewriting too much at once. Never go full rewrite.</p>
<p>Every time I&rsquo;m about to update some part of a system, my brain starts playing tug o&rsquo; war. Part of me wants to touch as little as possible to make things work, while another part wants to make things &ldquo;better&rdquo;. Better can be dangerously ambiguous, but I figured I had a good reason. Most of the code was tightly coupled to UI elements, the bane of automated testing, so I tried rewriting it using <a href="https://grantwinney.com/its-possible-to-test-a-winforms-app-using-mvp/"  target="_blank" rel="noreferrer">an MVC pattern</a> before adding <em>more</em> code that would just add to the problem. It was only 2000 lines of code after all. What could go wrong?</p>
<p>Bear in mind that the absolute <em>best case scenario</em> after any huge refactor is that everything works <em>exactly</em> as before and goes completely unnoticed by the end-user. While being applauded by your fellow devs, of course. Let&rsquo;s be honest now, we&rsquo;re hoping for a little praise when we make ourselves code martyrs, right? Riiiight..</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Next week, when someone digs into my refactored code"
    src="/beware-the-refactor-bug/abstrusegoose-432.png"
    width="744"
      height="612"></figure>
<p>There&rsquo;s a certain wisdom in touching as little as possible. Imagine a hundred different people working on an (old yet vital) building over the years, applying patches and stopgaps, retaining walls and scaffolding. A piece of coat wire hanger here, and a few mysterious yet deliberately placed nails there. You don&rsquo;t know the reason for them all, and it might be ugly as sin, but there&rsquo;s a reason for each one of them. It&rsquo;s similar with code - just ripping those modifications down and rearranging everything is asking for a world of hurt. Code that&rsquo;s working is, well, working&hellip; and every line was added to solve some problem or fix some bug.</p>
<p>There&rsquo;s nothing wrong with leaving things better than you found them though. What works for the boy scouts certainly can work for developers, but there&rsquo;s a middle ground. As a boy scout, we&rsquo;d pitch in help at the mess hall during summer camp. We did <em>not</em> burn the mess hall to the ground and try to build a better one. I mean, unless they had asked us to. We did appreciate a good bonfire. 🔥</p>
<p>After some time well wasted, I remembered that I was <a href="https://grantwinney.com/were-all-contractors/"  target="_blank" rel="noreferrer">hired to maintain and extend</a> an app, for a particular set of time, as long as it&rsquo;s working for all parties involved. I was not hired to treat the app like my pet project. I <a href="https://grantwinney.com/sunk-costs-timeboxing-asking-for-help/"  target="_blank" rel="noreferrer">cut my losses</a>, left the code of yesteryear&rsquo;s developers alone, and just extended the code. I put a few things in the controller where they&rsquo;d be testable, and made peace with the fact that some things would have to remain in the view.</p>
<p>It wasn&rsquo;t all wasted time though. Most of our work as developers is <em>not</em> in coding, but in <em>thinking</em> about coding. We plan, we discuss, we come to understand what we&rsquo;re building on. Writing code, especially now as a team lead, is maybe 25% of my day after discussions with project managers, planning with the team, noodling it over in my own head, rinse and repeat. Sometimes tearing something down a few times is what it takes to understand how things work. Thank you git revert!</p>
]]></content:encoded><media:content url="https://grantwinney.com/beware-the-refactor-bug/feature.webp" medium="image" type="image/webp"/></item><item><title>Mocking MessageBox (or any static class) in WinForms</title><link>https://grantwinney.com/mocking-messagebox-in-winforms/</link><pubDate>Fri, 07 Jan 2022 04:12:17 +0000</pubDate><guid>https://grantwinney.com/mocking-messagebox-in-winforms/</guid><description>Unit testing a WinForms app is an uphill battle in the best of times, before you add in classes like MessageBox. Let&amp;rsquo;s make the best of it.</description><content:encoded><![CDATA[<p>Trying to integrate testing into a WinForms app can be an uphill battle, especially if it&rsquo;s a legacy app with most of the logic tightly coupled to the UI in the code-behind files of hundreds of forms. It doesn&rsquo;t help when you toss in portions of the .NET Framework that were designed in very test-unfriendly ways.</p>

<h2 class="relative group">The problem
    <div id="the-problem" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-problem" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Take the MessageBox class for instance. This is normally what you see, peppered throughout WinForms apps everywhere.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">partial</span> <span class="k">class</span> <span class="nc">Form1</span> <span class="p">:</span> <span class="n">Form</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Form1</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">InitializeComponent</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">void</span> <span class="n">button1_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">MessageBox</span><span class="p">.</span><span class="n">Show</span><span class="p">(</span><span class="s">&#34;This is just a test of the emergency broadcast system.&#34;</span><span class="p">,</span> <span class="s">&#34;Fancy App&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>It&rsquo;s not testable, at least not with unit tests. The moment you call a method that uses it (I made the button click event public), your test will actually popup a MessageBox. Probably not what you wanted.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/mocking-messagebox-in-winforms/image-3.png"
    width="832"
      height="389"></figure>

<h2 class="relative group">So it&rsquo;s gonna be like that huh&hellip;
    <div id="so-its-gonna-be-like-that-huh" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#so-its-gonna-be-like-that-huh" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Unfortunately, the MessageBox class doesn&rsquo;t make it easy for us. Every method is static, so it implements no interface, and you can&rsquo;t mock it. There&rsquo;s no way to extend it with your own class and create an interface for it either. They shut the door on that by giving the class a private constructor.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/mocking-messagebox-in-winforms/image-1.png"
    width="839"
      height="505"></figure>
<p>You can try it anyway to see what I mean.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/mocking-messagebox-in-winforms/image.png"
    width="564"
      height="111"></figure>

<h2 class="relative group">Wrapping the MessageBox class to mock it
    <div id="wrapping-the-messagebox-class-to-mock-it" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#wrapping-the-messagebox-class-to-mock-it" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The only reasonable option left is to create our own message box class, wrap each of the static methods in normal instance methods, and create an interface from that. So let&rsquo;s take a look at how we might do that.</p>
<blockquote><p>The code in this article is available on <a href="https://github.com/grantwinney/Surviving-WinForms/tree/master/Testing/MockingMessageBox"  target="_blank" rel="noreferrer">GitHub</a>, if you&rsquo;d like to follow along or use it in your own projects.</p>
</blockquote><p>The first thing to do is just create your own message box class and name it something similar, like MessagePrompt or just Message. I won&rsquo;t paste the whole class but here&rsquo;s a portion of what it might look like.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">MessagePrompt</span> <span class="p">:</span> <span class="n">IMessagePrompt</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">DialogResult</span> <span class="n">Show</span><span class="p">(</span><span class="kt">string</span> <span class="n">text</span><span class="p">,</span> <span class="kt">string</span> <span class="n">caption</span><span class="p">,</span> <span class="n">MessageBoxButtons</span> <span class="n">buttons</span><span class="p">,</span> <span class="n">MessageBoxIcon</span> <span class="n">icon</span><span class="p">,</span> <span class="n">MessageBoxDefaultButton</span> <span class="n">defaultButton</span><span class="p">,</span> <span class="n">MessageBoxOptions</span> <span class="n">options</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">displayHelpButton</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">=&gt;</span> <span class="n">MessageBox</span><span class="p">.</span><span class="n">Show</span><span class="p">(</span><span class="n">text</span><span class="p">,</span> <span class="n">caption</span><span class="p">,</span> <span class="n">buttons</span><span class="p">,</span> <span class="n">icon</span><span class="p">,</span> <span class="n">defaultButton</span><span class="p">,</span> <span class="n">options</span><span class="p">,</span> <span class="n">displayHelpButton</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">DialogResult</span> <span class="n">Show</span><span class="p">(</span><span class="n">IWin32Window</span> <span class="n">owner</span><span class="p">,</span> <span class="kt">string</span> <span class="n">text</span><span class="p">,</span> <span class="kt">string</span> <span class="n">caption</span><span class="p">,</span> <span class="n">MessageBoxButtons</span> <span class="n">buttons</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">=&gt;</span> <span class="n">MessageBox</span><span class="p">.</span><span class="n">Show</span><span class="p">(</span><span class="n">owner</span><span class="p">,</span> <span class="n">text</span><span class="p">,</span> <span class="n">caption</span><span class="p">,</span> <span class="n">buttons</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">DialogResult</span> <span class="n">Show</span><span class="p">(</span><span class="n">IWin32Window</span> <span class="n">owner</span><span class="p">,</span> <span class="kt">string</span> <span class="n">text</span><span class="p">,</span> <span class="kt">string</span> <span class="n">caption</span><span class="p">,</span> <span class="n">MessageBoxButtons</span> <span class="n">buttons</span><span class="p">,</span> <span class="n">MessageBoxIcon</span> <span class="n">icon</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">=&gt;</span> <span class="n">MessageBox</span><span class="p">.</span><span class="n">Show</span><span class="p">(</span><span class="n">owner</span><span class="p">,</span> <span class="n">text</span><span class="p">,</span> <span class="n">caption</span><span class="p">,</span> <span class="n">buttons</span><span class="p">,</span> <span class="n">icon</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span></span></span></code></pre></div></div>
<p>Then you&rsquo;ll extract an interface from that. In Visual Studio, that&rsquo;s as simple as <code>ctrl + .</code> and choose &ldquo;extract interface&rdquo; from the dropdown.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">interface</span> <span class="nc">IMessagePrompt</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">DialogResult</span> <span class="n">Show</span><span class="p">(</span><span class="n">IWin32Window</span> <span class="n">owner</span><span class="p">,</span> <span class="kt">string</span> <span class="n">text</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">DialogResult</span> <span class="n">Show</span><span class="p">(</span><span class="n">IWin32Window</span> <span class="n">owner</span><span class="p">,</span> <span class="kt">string</span> <span class="n">text</span><span class="p">,</span> <span class="kt">string</span> <span class="n">caption</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">DialogResult</span> <span class="n">Show</span><span class="p">(</span><span class="n">IWin32Window</span> <span class="n">owner</span><span class="p">,</span> <span class="kt">string</span> <span class="n">text</span><span class="p">,</span> <span class="kt">string</span> <span class="n">caption</span><span class="p">,</span> <span class="n">MessageBoxButtons</span> <span class="n">buttons</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span></span></span></code></pre></div></div>
<p>The rest of how you use it is up to you, but I put together a quick little sample out on GitHub if you want to check it out. It uses Unity for dependency injection (to connect the interface to the concrete class at runtime), and NUnit and Moq to do some light testing.</p>
<p>I kept the implementation as simple as possible too; the kind of thing you might want to do if you&rsquo;re adding the first unit test to a big old WinForms app. I&rsquo;d much rather rework things to use an MVP type of format though, but that&rsquo;s not always possible right away. If you want to read a little more about MVP, <a href="https://grantwinney.com/its-possible-to-test-a-winforms-app-using-mvp"  target="_blank" rel="noreferrer">check this out</a>.</p>
<p>As for this project, I just added a second constructor that accepted the interface&hellip;</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">partial</span> <span class="k">class</span> <span class="nc">Form1</span> <span class="p">:</span> <span class="n">Form</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">readonly</span> <span class="n">IMessagePrompt</span> <span class="n">messagePrompt</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Form1</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">InitializeComponent</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">messagePrompt</span> <span class="p">=</span> <span class="n">DependencyInjector</span><span class="p">.</span><span class="n">Retrieve</span><span class="p">&lt;</span><span class="n">IMessagePrompt</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Form1</span><span class="p">(</span><span class="n">IMessagePrompt</span> <span class="n">msgPrompt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">InitializeComponent</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">messagePrompt</span> <span class="p">=</span> <span class="n">msgPrompt</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">void</span> <span class="n">btnShowMessage_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">ShowBroadcastMessage</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">ShowBroadcastMessage</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">messagePrompt</span><span class="p">.</span><span class="n">Show</span><span class="p">(</span><span class="s">&#34;This is just a test of the emergency broadcast system.&#34;</span><span class="p">,</span> <span class="s">&#34;Fancy App&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>&hellip; and then mocked one up and injected it from the test suite. Although I suppose &ldquo;suite&rdquo; is a loose term for a single test. :)</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Tests</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Form1</span> <span class="n">form</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">Mock</span><span class="p">&lt;</span><span class="n">IMessagePrompt</span><span class="p">&gt;</span> <span class="n">messagePrompt</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [SetUp]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">Setup</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">messagePrompt</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Mock</span><span class="p">&lt;</span><span class="n">IMessagePrompt</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl">        <span class="n">form</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Form1</span><span class="p">(</span><span class="n">messagePrompt</span><span class="p">.</span><span class="n">Object</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [Test]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">Test1</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">form</span><span class="p">.</span><span class="n">ShowBroadcastMessage</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">messagePrompt</span><span class="p">.</span><span class="n">Verify</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">Show</span><span class="p">(</span><span class="n">It</span><span class="p">.</span><span class="n">IsAny</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;(),</span> <span class="s">&#34;Fancy App&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
]]></content:encoded><media:content url="https://grantwinney.com/mocking-messagebox-in-winforms/feature.webp" medium="image" type="image/webp"/></item><item><title>Using nameof to avoid magic strings in C#</title><link>https://grantwinney.com/using-nameof-to-avoid-magic-strings/</link><pubDate>Thu, 30 Dec 2021 01:53:29 +0000</pubDate><guid>https://grantwinney.com/using-nameof-to-avoid-magic-strings/</guid><description>There&amp;rsquo;s a lot of ways to make our code work for us. Let&amp;rsquo;s check out using the nameof operator to avoid magic strings.</description><content:encoded><![CDATA[<p>Having magic strings in your code is definitely something to watch out for. What&rsquo;s a magic string? It&rsquo;s any string containing a value that might change, like an application setting, a timeout value, a method or class name, etc.</p>
<p>Imagine a single text field being validated in some WinForms app, and if the user inputs a value that&rsquo;s too short, it does 2 things - displays a message and logs a message for debugging purposes. Then someone comes along and says the username needs to be at least 12 characters. What&rsquo;re the odds it gets updated in the first instance, but not in the messages and logs? Those are magic strings.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">txtUsername_Validating</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">CancelEventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">txtUsername</span><span class="p">.</span><span class="n">Text</span><span class="p">.</span><span class="n">Length</span> <span class="p">&lt;</span> <span class="m">10</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">MessageBox</span><span class="p">.</span><span class="n">Show</span><span class="p">(</span><span class="s">&#34;Your username must be at least 10 characters. Please try again&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">logger</span><span class="p">.</span><span class="n">Trace</span><span class="p">(</span><span class="s">&#34;Username validation failed; value was less than 10 characters.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The fix is super easy, if not always easy to remember - just pull the value out into a single variable. To make life even easier, the next step might be to pull it out into some kind of application settings or other configuration file that can be changed without needing to recompile the entire app. But one step at a time&hellip;</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">const</span> <span class="kt">int</span> <span class="n">minUsernameLength</span> <span class="p">=</span> <span class="m">10</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">txtUsername_Validating</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">CancelEventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">txtUsername</span><span class="p">.</span><span class="n">Text</span><span class="p">.</span><span class="n">Length</span> <span class="p">&lt;</span> <span class="n">minUsernameLength</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">MessageBox</span><span class="p">.</span><span class="n">Show</span><span class="p">(</span><span class="s">$&#34;Your username must be at least {minUsernameLength} characters. Please try again&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">logger</span><span class="p">.</span><span class="n">Trace</span><span class="p">(</span><span class="s">$&#34;Username validation failed; value was less than {minUsernameLength} characters.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h2 class="relative group">What is the nameof expression?
    <div id="what-is-the-nameof-expression" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-is-the-nameof-expression" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There are different kinds of values that might need to be pulled out of magic strings. Some of them are business rule kind of logic, like above. Others are related more to our code, and concerns the devs more than end-users. Not everything has to be pulled out into a configuration file, but there are other ways to make our lives easier.</p>
<blockquote><p>The code in this article is available on <a href="https://github.com/grantwinney/Surviving-WinForms/tree/master/AntiPatterns/MagicStrings/NameOfVersusMagicStrings"  target="_blank" rel="noreferrer">GitHub</a>, if you&rsquo;d like to follow along.</p>
</blockquote><p>Introduced in C# 6, the <code>nameof</code> expression returns the name of the variable, type, or member (a method or class for example) that&rsquo;s passed to it. But the value you&rsquo;re passing to <code>nameof</code> is the method group, so if the name of that method changes, you&rsquo;ll actually get a compiler error!</p>
<p>This can be wildly helpful, but I think it&rsquo;s pretty underrated. Let&rsquo;s check out a few examples to see how it can make life a little easier for us.</p>

<h2 class="relative group">Using nameof in trace logging
    <div id="using-nameof-in-trace-logging" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#using-nameof-in-trace-logging" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Let&rsquo;s say you&rsquo;re working in a legacy app that&rsquo;s prone to some pretty weird bugs. Yep, stretches the imagination. So you have trace logging all over the place, and when a bug comes up you can crank things up to 11 in a test environment to get a better idea of what&rsquo;s failing and where.</p>
<p>You don&rsquo;t necessarily have a stack trace to log, so you want to write the name of the method in the call to LogTrace. But if there&rsquo;s a refactor, and someone changes some method names, what are the odds they&rsquo;ll miss your trace log messages. Pretty good.</p>
<p>Well, you can avoid that by using <code>nameof</code>.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnSave_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">logger</span><span class="p">.</span><span class="n">Trace</span><span class="p">(</span><span class="s">$&#34;Entering {nameof(btnSave_Click)} event.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">try</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">logger</span><span class="p">.</span><span class="n">Trace</span><span class="p">(</span><span class="s">$&#34;Attempting to save username in {nameof(btnSave_Click)} event.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">db</span> <span class="p">=</span> <span class="k">new</span> <span class="n">DatabaseLayer</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">db</span><span class="p">.</span><span class="n">SaveUsername</span><span class="p">(</span><span class="n">UserId</span><span class="p">,</span> <span class="n">Username</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">logger</span><span class="p">.</span><span class="n">Trace</span><span class="p">(</span><span class="s">$&#34;Save succeeded in {nameof(btnSave_Click)} event.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">logger</span><span class="p">.</span><span class="n">Error</span><span class="p">(</span><span class="n">ex</span><span class="p">,</span> <span class="s">$&#34;Save failed in {nameof(btnSave_Click)} event.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">MessageBox</span><span class="p">.</span><span class="n">Show</span><span class="p">(</span><span class="s">&#34;Save failed. :(&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">finally</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">logger</span><span class="p">.</span><span class="n">Trace</span><span class="p">(</span><span class="s">$&#34;Exiting {nameof(btnSave_Click)} event.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Then not only will you get a compiler error that <em>forces</em> you to update those messages if the method name changes, but doing a &ldquo;rename&rdquo; through Visual Studio will highlight all those instances in the log messages and rename them at the same time.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-nameof-to-avoid-magic-strings/vs-rename-event-method.png"
    width="763"
      height="452"></figure>
<p>Try out the code yourself and check out the logs.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">2021-12-28 21:25:25.4810  Entering btnSave_Click event.
2021-12-28 21:25:25.4810  Attempting to save username in btnSave_Click event.
2021-12-28 21:25:25.5824  Save failed in btnSave_Click event.System.ArgumentOutOfRangeException: UserId cannot be empty (Parameter &#39;txtUserId&#39;)
   at NameOfVersusMagicStrings.Form1.get_UserId() in C:\Users\Grant\Code\SurvivingWinForms\AntiPatterns\MagicStrings\NameOfVersusMagicStrings\NameOfVersusMagicStrings\Form1.cs:line 21
   at NameOfVersusMagicStrings.Form1.btnSave_Click(Object sender, EventArgs e) in C:\Users\Grant\Code\SurvivingWinForms\AntiPatterns\MagicStrings\NameOfVersusMagicStrings\NameOfVersusMagicStrings\Form1.cs:line 40
2021-12-28 21:25:57.2668  Exiting btnSave_Click event.</code></pre></div>

<h2 class="relative group">Using nameof in the Obsolete attribute
    <div id="using-nameof-in-the-obsolete-attribute" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#using-nameof-in-the-obsolete-attribute" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Let&rsquo;s check out another example.</p>
<p>When you mark a method as obsolete, it makes sense to redirect other developers to the newer one, but that&rsquo;s susceptible to the same problem as above. If the name of the newer method changes, your note may tell other devs to use something that doesn&rsquo;t exist anymore.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">DatabaseLayer</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="na">    [Obsolete(&#34;Use the &#34; + nameof(SaveOrUpdate) + &#34; method&#34;)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">SaveUsername</span><span class="p">(</span><span class="kt">string</span> <span class="n">userId</span><span class="p">,</span> <span class="kt">string</span> <span class="n">username</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">throw</span> <span class="k">new</span> <span class="n">NotImplementedException</span><span class="p">(</span><span class="s">$&#34;Told you {nameof(SaveUsername)} was obsolete... use {nameof(SaveOrUpdate)}!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">SaveOrUpdate</span><span class="p">(</span><span class="kt">string</span> <span class="n">userId</span><span class="p">,</span> <span class="kt">string</span> <span class="n">username</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Beep boop, values being saved to the database...</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>By using <code>nameof</code>, renaming the other method will cause a compilation error and force you to go changing the name everywhere.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-nameof-to-avoid-magic-strings/nameof-rename-warning.png"
    width="763"
      height="283"></figure>
<p>With C# 10, you&rsquo;re supposed to be able to use string interpolation in the Obsolete attribute itself, like this. But I tried it, and when I add the <code>$</code> to the string, it still marks the method as deprecated, but it ignores the message and the &ldquo;error&rdquo; flag when it&rsquo;s set to true. Hopefully they get that fixed.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="na">[Obsolete($&#34;Use the {nameof(SaveOrUpdate)} method&#34;)]</span></span></span></code></pre></div></div>

<h2 class="relative group">Using nameof in a property accessor
    <div id="using-nameof-in-a-property-accessor" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#using-nameof-in-a-property-accessor" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I think you can see where this is going, so we&rsquo;ll just check out one more example.</p>
<p>If you wanted to, you could even use <code>nameof</code> from within a property. I&rsquo;ve never done this, but <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/nameof"  target="_blank" rel="noreferrer">Microsoft</a> has an example in their docs where they throw an exception when trying to set an invalid value (like below).</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kt">string</span> <span class="n">UserId</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">get</span> <span class="p">=&gt;</span> <span class="n">txtUserId</span><span class="p">.</span><span class="n">Text</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">set</span> <span class="p">=&gt;</span> <span class="n">txtUserId</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="kt">int</span><span class="p">.</span><span class="n">TryParse</span><span class="p">(</span><span class="k">value</span><span class="p">,</span> <span class="k">out</span> <span class="kt">int</span> <span class="n">userId</span><span class="p">)</span> <span class="p">&amp;&amp;</span> <span class="n">userId</span> <span class="p">&gt;</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl">               <span class="p">?</span> <span class="k">value</span>
</span></span><span class="line"><span class="cl">               <span class="p">:</span> <span class="k">throw</span> <span class="k">new</span> <span class="n">ArgumentOutOfRangeException</span><span class="p">(</span><span class="n">nameof</span><span class="p">(</span><span class="k">value</span><span class="p">),</span> <span class="s">$&#34;The {nameof(UserId)} must be a positive number.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="n">i</span></span></span></code></pre></div></div>
<p>You could turn around and display that message to the user, or maybe just log it and display something a little more friendly.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-nameof-to-avoid-magic-strings/using-nameof-in-exception-msg.png"
    width="527"
      height="300"></figure>
<p>I&rsquo;m sure there&rsquo;s a lot more places you could use this too. If you come up with any good ones (or any bad ones, lol) let me know in a comment below!</p>
<p>If you found this content useful, and want to learn more about a variety of C# features, check out <a href="https://github.com/grantwinney/CSharpDotNetExamples"  target="_blank" rel="noreferrer">my GitHub repo</a>, where you&rsquo;ll find links to plenty more blog posts and practical examples.</p>
]]></content:encoded><media:content url="https://grantwinney.com/using-nameof-to-avoid-magic-strings/feature.webp" medium="image" type="image/webp"/></item><item><title>Sunk costs, timeboxing, and asking for help</title><link>https://grantwinney.com/sunk-costs-timeboxing-asking-for-help/</link><pubDate>Mon, 20 Dec 2021 12:00:00 +0000</pubDate><guid>https://grantwinney.com/sunk-costs-timeboxing-asking-for-help/</guid><description>One of the biggest struggles I have with programming is knowing when to ask for help. A little struggle is necessary for growth, but when am I just wasting time when I could be learning from others?</description><content:encoded><![CDATA[<p>One of the biggest struggles I have with programming (or anything I&rsquo;m trying to learn) is knowing when to ask for help. Ask too soon, and I might miss an opportunity to grow in self-reliance and self-confidence. A little struggle is necessary for growth. But ask too late, and I might just be wasting time and missing the opportunity to learn from others.</p>
<p>Last week, I was updating some of our DevOps builds for a new environment (if that&rsquo;s a new term for you, <a href="https://azure.microsoft.com/en-us/overview/what-is-devops"  target="_blank" rel="noreferrer">read more here</a>), when one of our team&rsquo;s jobs to deploy some code to a server began failing. I thought maybe the wrong version of the code was being deployed, but that wasn&rsquo;t it. I redeployed a couple times, then started digging into the logs on the server.</p>
<p>After several hours and no answers, I reached out to someone else on the team. She asked where the job was deploying the code to. Was it pointing to the new environment?</p>
<p>No. No it was not. 😐</p>
<p>The code I tried redeploying several times would&rsquo;ve fixed the original issue, but unfortunately it was being deployed to the <em>old</em> environment. It had taken me longer to explain what I&rsquo;d tried than it did for my coworker to find the thing I&rsquo;d missed!</p>
<p>And that&rsquo;s the struggle. How do you know when to reach out? It&rsquo;s so easy to feel like you&rsquo;ve already invested time in finding a solution <a href="https://thedecisionlab.com/biases/the-sunk-cost-fallacy/"  target="_blank" rel="noreferrer">so you can&rsquo;t stop now</a>. Just one more thing on google to try, just one more hour (then one more, and one more) and you&rsquo;ll have a solution. Almost there&hellip;</p>
<p>The best thing I&rsquo;ve tried so far (when I remember to do it!) is to <a href="https://www.agilealliance.org/glossary/timebox"  target="_blank" rel="noreferrer">time-box my efforts</a>. If it seems likely that you&rsquo;ll find a solution in an hour, and still reasonable that it might even take a few hours, then make a deal with yourself to re-evaluate where things are at after 3 or 4 hours. If you&rsquo;ve exhausted the obvious things to try, or you&rsquo;re just going in circles, it&rsquo;s time to tag someone else in. Sometimes, putting your thoughts in order is enough to realize what you missed. And when that&rsquo;s not enough, a second opinion never hurts.</p>
]]></content:encoded><media:content url="https://grantwinney.com/sunk-costs-timeboxing-asking-for-help/feature.webp" medium="image" type="image/webp"/></item><item><title>A more helpful exception box for WinForms apps</title><link>https://grantwinney.com/the-helpful-exception-box/</link><pubDate>Tue, 07 Dec 2021 15:37:03 +0000</pubDate><guid>https://grantwinney.com/the-helpful-exception-box/</guid><description>If you&amp;rsquo;re in a legacy codebase with a centralized &amp;ldquo;message box&amp;rdquo; form, why not add some features that make it work for you? 😏</description><content:encoded><![CDATA[<p>DeI saw a suggestion like a week or two ago that had me cracking up, and I can&rsquo;t for the life of me remember <em>where</em> I saw it. Maybe LinkedIn, maybe Twitter.. I didn&rsquo;t mark it, so it&rsquo;s buried deep in my timeline never to be seen again. But the gist of it was someone asking whether just slapping a button on an error prompt that led straight to stack overflow was a legit way to help the end user.</p>
<blockquote><p>If you&rsquo;d like to follow along with the code in this post, it&rsquo;s available on <a href="https://github.com/grantwinney/Surviving-WinForms/tree/master/Debugging/Misc/MessageBoxForDevs"  target="_blank" rel="noreferrer">GitHub</a>.</p>
</blockquote><p>After I laughed about it for a few seconds, I stopped to think&hellip; is it really that crazy? Maybe. It&rsquo;s not something I&rsquo;d ever want to present to the <em>user,</em> but what about just showing it to the developers or QA? Wouldn&rsquo;t it be convenient, when an error pops up, to just have a link that takes you right to the place you were probably (admit it) about to search anyway?</p>
<p>And so I present.. an exceptional exception box.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/the-helpful-exception-box/image-1.png"
    width="568"
      height="380"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/the-helpful-exception-box/image-4.png"
    width="745"
      height="490"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/the-helpful-exception-box/image-3.png"
    width="931"
      height="490"></figure>
<p>Most of the codebases I&rsquo;ve worked in are old. I&rsquo;d wager most of the codebases out there <em>anywhere</em> are old. Most companies that&rsquo;ve been around a couple decades have a homegrown project or two, with lots of layers, written by lots of devs. And at some point someone always adds their own message box class that encapsulates the C# <a href="https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.messagebox?view=windowsdesktop-6.0"  target="_blank" rel="noreferrer">MessageBox</a>, with helpful methods like ShowInfo and ShowError that standardize which buttons and icons are shown for each type of prompt, and in what order, etc.</p>
<p>If you happen to be in that boat, you can take advantage of that to create some friendly links that take you right to StackOverflow, MSDN, or wherever else you happen to find the answers to your coding woes. Add some <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/preprocessor-directives#conditional-compilation"  target="_blank" rel="noreferrer">preprocessor directives</a> to make sure the buttons don&rsquo;t show in production, a couple more buttons to open the full stack trace for easy viewing, and you&rsquo;re on your way to making your own special mark in that legacy codebase. 😂</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">partial</span> <span class="k">class</span> <span class="nc">ExceptionalBox</span> <span class="p">:</span> <span class="n">Form</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">readonly</span> <span class="n">Exception</span> <span class="n">exception</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">ExceptionalBox</span><span class="p">(</span><span class="n">Exception</span> <span class="n">exception</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">:</span> <span class="k">this</span><span class="p">(</span><span class="n">exception</span><span class="p">.</span><span class="n">Message</span><span class="p">,</span> <span class="n">exception</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">ExceptionalBox</span><span class="p">(</span><span class="kt">string</span> <span class="n">userFriendlyMessage</span><span class="p">,</span> <span class="n">Exception</span> <span class="n">exception</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">InitializeComponent</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">        <span class="n">lblMessage</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="n">userFriendlyMessage</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="n">exception</span> <span class="p">=</span> <span class="n">exception</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#if</span> <span class="n">DEBUG</span> <span class="p">||</span> <span class="n">DEVELOPMENT</span> <span class="p">||</span> <span class="n">STAGING</span>
</span></span><span class="line"><span class="cl">        <span class="n">btnSOS</span><span class="p">.</span><span class="n">Visible</span> <span class="p">=</span> <span class="n">btnMS</span><span class="p">.</span><span class="n">Visible</span> <span class="p">=</span> <span class="n">btnCopy</span><span class="p">.</span><span class="n">Visible</span> <span class="p">=</span> <span class="n">btnNote</span><span class="p">.</span><span class="n">Visible</span> <span class="p">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="cp">#endif</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">void</span> <span class="n">btnSOS_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Process</span><span class="p">.</span><span class="n">Start</span><span class="p">(</span><span class="k">new</span> <span class="n">ProcessStartInfo</span><span class="p">(</span><span class="s">$&#34;https://stackoverflow.com/search?q={exception.GetType()}+{exception?.Message.Replace(&#39; &#39;, &#39;+&#39;)}&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">UseShellExecute</span> <span class="p">=</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">Verb</span> <span class="p">=</span> <span class="s">&#34;open&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">void</span> <span class="n">btnMS_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Process</span><span class="p">.</span><span class="n">Start</span><span class="p">(</span><span class="k">new</span> <span class="n">ProcessStartInfo</span><span class="p">(</span><span class="s">$&#34;https://docs.microsoft.com/en-us/search/?terms={exception.GetType()}+{exception?.Message.Replace(&#34;</span> <span class="s">&#34;, &#34;</span><span class="p">%</span><span class="m">20</span><span class="s">&#34;)}&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">UseShellExecute</span> <span class="p">=</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">Verb</span> <span class="p">=</span> <span class="s">&#34;open&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">void</span> <span class="n">btnCopy_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Clipboard</span><span class="p">.</span><span class="n">SetText</span><span class="p">(</span><span class="n">exception</span><span class="p">.</span><span class="n">ToString</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">void</span> <span class="n">btnNote_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">tempFile</span> <span class="p">=</span> <span class="s">$&#34;{Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString())}.txt&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">File</span><span class="p">.</span><span class="n">WriteAllText</span><span class="p">(</span><span class="n">tempFile</span><span class="p">,</span> <span class="n">exception</span><span class="p">.</span><span class="n">ToString</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">        <span class="n">Process</span><span class="p">.</span><span class="n">Start</span><span class="p">(</span><span class="s">&#34;notepad.exe&#34;</span><span class="p">,</span> <span class="n">tempFile</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
]]></content:encoded><media:content url="https://grantwinney.com/the-helpful-exception-box/feature.webp" medium="image" type="image/webp"/></item><item><title>Every software dev is a contractor</title><link>https://grantwinney.com/were-all-contractors/</link><pubDate>Tue, 19 Oct 2021 03:37:36 +0000</pubDate><guid>https://grantwinney.com/were-all-contractors/</guid><description>Seeing my full-time job as a long-term contract has helped me improve and contribute, without taking things personally or falling into complacency.</description><content:encoded><![CDATA[<p>The work we do for any company in any role, even as full-time employees, is still just contract work, when you really think about it. It may be a very <em>long</em> term contract - years or even decades - but eventually the contract ends. We pass the baton and someone else runs with it.</p>
<p>Keeping that in mind has subtly changed my outlook and approach.</p>

<h2 class="relative group">This is not your baby
    <div id="this-is-not-your-baby" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#this-is-not-your-baby" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Whatever work I happen to be doing, it&rsquo;s not mine. Someone&rsquo;s paying me for my knowledge and experience, to make <em>their</em> vision a reality. And that can actually be a freeing thought.</p>
<p>Imagine hiring someone to lay a patio for you. They spend weeks arguing with you and their fellow patio installers, about the shape and design, using new cutting-edge materials, pouring cement from front to back and top to bottom (lol). They finally pour it out, break it up and repour it, destroy it and rebuild it, again and again. And the whole time, they&rsquo;re charging you for their time and materials. Absurd, right?</p>
<p>I&rsquo;ve known developers who do that. They demand the product be written (or rewritten) in some new language or framework, obsess over it and chastise the rest of the team for not getting on board, and ultimately produce little. The business didn&rsquo;t ask for it. The customers don&rsquo;t understand it. But someone chose to make it their personal hill to die on.</p>
<p>Yes, it&rsquo;s good to take professional pride in the work we do. Yes, it&rsquo;s good to leave things cleaner than we found them. Yes, it&rsquo;s good to be diligent about how we do what we do, so the product is reliable to use and easy to build on.</p>
<p>But we&rsquo;re doing the same thing as that fussy patio installer when we see the product as our own and take every feature and flaw personally. Each of us is just a single runner in a long relay race.</p>

<h2 class="relative group">This is not your home
    <div id="this-is-not-your-home" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#this-is-not-your-home" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I&rsquo;ve also known coworkers and friends who say they wish their company would just fire them so they&rsquo;d be forced to look elsewhere. Imagine that. Staying at the wrong place so long that you simultaneously can&rsquo;t stand it one more day, but can&rsquo;t fathom doing anything else unless someone forces your hand.</p>
<p>I&rsquo;ve never gotten to that point, of just hoping to be fired, but I have fallen into a lull and gone on autopilot for periods of time. Even if I&rsquo;m not growing or the circumstances are less than ideal, at least it&rsquo;s familiar. So instead of challenging myself, I ride it out. Sound familiar?</p>
<p>When I was doing contract work for the first time last year, I kept meticulous notes about what I did day-to-day, in case I needed to prove to someone why I put down the hours I did for a particular week. It had an unexpected side effect. It kept the days from blending together, and I was much more aware of what I was doing week-to-week and month-to-month.</p>
<p>If I had a few slow days, I could look back and ask myself, did I still do a good job? If not, why not? Could I improve anything under my control? On the good days, I could look back and ask myself, why was it good? What did I learn? Am I still learning anything, about coding or any other skill I want to work on?</p>
<p>Even though I&rsquo;m full time now, I still do it. It helps me constantly re-evaluate my &ldquo;contract&rdquo;. Look at each project you&rsquo;re on as a mini-contract. Is it still working for you? Are you learning what you want to learn? Contributing where you want to contribute? Improving personally and professionally?</p>

<h2 class="relative group">You aren&rsquo;t stuck
    <div id="you-arent-stuck" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#you-arent-stuck" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There are always other opportunities out there. I like Scott Hanselman&rsquo;s post on <a href="https://www.hanselman.com/blog/changing-perspectives-on-your-job-will-you-renew-your-boss-for-another-season"  target="_blank" rel="noreferrer">renewing your boss</a>. It&rsquo;s not a dig on anyone&rsquo;s particular boss, but a call to really ask yourself, is (my boss, my boss&rsquo;s boss, this company, the project) still doing it for me?</p>
<p>If the answer is no, don&rsquo;t settle for &ldquo;at least it&rsquo;s familiar&rdquo;. You can always change it, hopefully right where you&rsquo;re at, but <em>always</em> somewhere.</p>
]]></content:encoded><media:content url="https://grantwinney.com/were-all-contractors/feature.webp" medium="image" type="image/webp"/></item><item><title>How to log errors in WinForms using NLog</title><link>https://grantwinney.com/log-errors-in-winforms-with-nlog/</link><pubDate>Sat, 09 Oct 2021 15:53:15 +0000</pubDate><guid>https://grantwinney.com/log-errors-in-winforms-with-nlog/</guid><description>Logs are a great tool for squashing bugs and tracing errors. Let&amp;rsquo;s see how to add NLog to our project.</description><content:encoded><![CDATA[<p>What&rsquo;s more annoying than a bug in your code? Not knowing <em>why</em> there&rsquo;s a bug in your code! I&rsquo;ve worked in code bases before that have little to no logging, and it&rsquo;s awful. When an exception is thrown, .NET tells us what and where, including the long chain of method calls (stack trace) all the way back to the origin. To not make a note of that somewhere is a shame.. and a waste of everyone&rsquo;s time! Some people love debugging. I&rsquo;m not one of them.</p>
<p>Even when your code is running perfectly, sometimes it&rsquo;s handy to be able to log informational messages, especially during testing. Or maybe there&rsquo;s no exception but something still seems &ldquo;off&rdquo;. How convenient it is to write logs, and see what unexpected paths the system is going down!</p>
<p>Even in a monolithic WinForms app that has no logging, it&rsquo;s possible to add it - a little at a time. Now if you&rsquo;re on a team, don&rsquo;t try to fix the whole app and issue the Guinness book of world records sized PR. It&rsquo;s not going to garner the praise and admiration of your peers.. or management&hellip;. or customers. Just keep telling yourself things like &ldquo;Rome wasn&rsquo;t built in a day&rdquo; and &ldquo;the road to hell is paved with good intentions&rdquo;. 😂</p>
<p>Configure a tool like <a href="https://nlog-project.org/"  target="_blank" rel="noreferrer">NLog</a>, use it in whatever code you&rsquo;re touching at the moment, and go from there!</p>

<h2 class="relative group">Install NLog
    <div id="install-nlog" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#install-nlog" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There&rsquo;s different kinds of tools to do this, and you could even roll your own if you&rsquo;re a glutton for punishment, but <a href="https://nlog-project.org/"  target="_blank" rel="noreferrer">NLog</a> is a tried and true library, so we&rsquo;ll just use that.</p>
<p>Open your solution, go to &ldquo;Manage NuGet Packages&rdquo;, and search for NLog. You should see a few items. You <em>could</em> just install the first one, but I&rsquo;d recommend the fourth instead (NLog.Config), which installs NLog, and a sample <code>NLog.config</code> file to start with, <em>and</em> some helpful intellisense for the config file (that&rsquo;s the NLog.Schema one).</p>
<p>What&rsquo;s the NLog.Extensions.Logging one that I glossed over? Something to do with new features in .NET Core and .NET Standard, and probably not something you&rsquo;re worried about in a WinForms app.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/log-errors-in-winforms-with-nlog/vs-nlog-pkg.png"
    width="1410"
      height="620"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/log-errors-in-winforms-with-nlog/vs-nlog-config.png"
    width="1373"
      height="676"></figure>

<h2 class="relative group">Configure NLog
    <div id="configure-nlog" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#configure-nlog" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Everything&rsquo;s driven off the NLog.config file, so open that up and check it out. You should see some boilerplate stuff, a few links, and intellisense if you hover over the different elements. Pretty exciting. <em>(But then, I spent Friday evening writing this, so my judgement&rsquo;s probably off.)</em></p>
<p>There are <em>so many</em> configuration options you can choose from, and you can see them all by visiting those links! And get overwhelmed to your heart&rsquo;s content. For now though, let&rsquo;s just focus on writing to a file, which is <a href="https://github.com/NLog/NLog/wiki/File-target"  target="_blank" rel="noreferrer">documented here</a>.</p>
<p>Skip down to the section on &ldquo;<a href="https://github.com/NLog/NLog/wiki/File-target#simple-logging"  target="_blank" rel="noreferrer">simple logging</a>&rdquo; and replace everything in the NLog.config file with the contents of that section. That&rsquo;ll get you up and running quickly, and it&rsquo;ll write log files to wherever your app is running. Maybe not ideal in production, but you can adjust that later.</p>
<ul>
<li>Remove &ldquo;fileName&rdquo; and &ldquo;keepFileOpen&rdquo;.</li>
<li>Replace &ldquo;Debug&rdquo; with &ldquo;Trace&rdquo; in &ldquo;minLevel&rdquo;.</li>
<li>Skip further down in the docs to &ldquo;<a href="https://github.com/NLog/NLog/wiki/File-target#archive-old-log-files"  target="_blank" rel="noreferrer">archive old log files</a>&rdquo; and copy &ldquo;fileName&rdquo; and the two lines below it. Paste them into the &ldquo;target&rdquo; section.</li>
</ul>
<p>You should end up with something like this:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="cp">&lt;?xml version=&#34;1.0&#34; ?&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;nlog</span> <span class="na">xmlns=</span><span class="s">&#34;http://www.nlog-project.org/schemas/NLog.xsd&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">xmlns:xsi=</span><span class="s">&#34;http://www.w3.org/2001/XMLSchema-instance&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;targets&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;target</span> <span class="na">name=</span><span class="s">&#34;file&#34;</span> <span class="na">xsi:type=</span><span class="s">&#34;File&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="na">layout=</span><span class="s">&#34;${longdate} ${logger} ${message}${exception:format=ToString}&#34;</span> 
</span></span><span class="line"><span class="cl">            <span class="na">fileName=</span><span class="s">&#34;${basedir}/logs/AppLog.${shortdate}.txt&#34;</span> 
</span></span><span class="line"><span class="cl">            <span class="na">maxArchiveFiles=</span><span class="s">&#34;4&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="na">archiveAboveSize=</span><span class="s">&#34;10240&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="na">encoding=</span><span class="s">&#34;utf-8&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/targets&gt;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;rules&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;logger</span> <span class="na">name=</span><span class="s">&#34;*&#34;</span> <span class="na">minlevel=</span><span class="s">&#34;Trace&#34;</span> <span class="na">writeTo=</span><span class="s">&#34;file&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/rules&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/nlog&gt;</span></span></span></code></pre></div></div>
<p>Those lines about archiving aren&rsquo;t strictly needed for this basic demo, but I think they&rsquo;re important to call out. You don&rsquo;t want a log file that grows too large, so this will archive old files. It also hangs on to 4, although maybe you want to hang on to a month, or even several months. But log files from several years ago just aren&rsquo;t necessary.</p>

<h2 class="relative group">Use NLog
    <div id="use-nlog" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#use-nlog" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>To use it, just create a new instance of the logger. Since we didn&rsquo;t give it a name in the config file, it doesn&rsquo;t matter what name you specify here - even an empty string works. Then write whatever you want, and check for the file in the &ldquo;bin&rdquo; folder where <code>${basedir}</code> points to.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/log-errors-in-winforms-with-nlog/vs-use-nlog.png"
    width="1634"
      height="505"></figure>
<p>That&rsquo;s it!</p>

<h2 class="relative group">A more interesting example
    <div id="a-more-interesting-example" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#a-more-interesting-example" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>NLog can do a <em>lot</em> more, way more than I could cover here. Before wrapping things up though, let&rsquo;s try a slightly more interesting example. It&rsquo;ll probably be easier if you just <a href="https://github.com/grantwinney/SurvivingWinForms/tree/master/Debugging/Logging/NLogUtility"  target="_blank" rel="noreferrer">grab the code</a>, but I made a couple changes in the config file - it logs everything (including trace messages), and the message will include the &ldquo;level&rdquo; (info, warning, etc).</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;targets&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;target</span> <span class="na">name=</span><span class="s">&#34;file&#34;</span> <span class="na">xsi:type=</span><span class="s">&#34;File&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">layout=</span><span class="s">&#34;${longdate}|${level:uppercase=true}|${message} ${exception:format=ToString}${newline}&#34;</span> 
</span></span><span class="line"><span class="cl">        <span class="na">fileName=</span><span class="s">&#34;${basedir}/logs/AppLog.txt&#34;</span> 
</span></span><span class="line"><span class="cl">        <span class="na">maxArchiveFiles=</span><span class="s">&#34;10&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">archiveAboveSize=</span><span class="s">&#34;10240&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">archiveFileName=</span><span class="s">&#34;${basedir}/logs/archive/AppLog.{####}.txt&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">archiveNumbering=</span><span class="s">&#34;Sequence&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">encoding=</span><span class="s">&#34;utf-8&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/targets&gt;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="nt">&lt;rules&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;logger</span> <span class="na">name=</span><span class="s">&#34;app_logger&#34;</span> <span class="na">minlevel=</span><span class="s">&#34;Trace&#34;</span> <span class="na">writeTo=</span><span class="s">&#34;file&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/rules&gt;</span></span></span></code></pre></div></div>
<p>I wrote a little UI that logs a few different types of messages, and you can see the result here. If this is new to you, <a href="https://github.com/grantwinney/SurvivingWinForms/tree/master/Debugging/Logging/NLogUtility"  target="_blank" rel="noreferrer">get the code</a>, play around with it, change it, break it, revert the code.. lol.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/log-errors-in-winforms-with-nlog/winforms-nlog-example.gif"
    width="912"
      height="987"></figure>
<p>The <a href="https://github.com/NLog/NLog/wiki"  target="_blank" rel="noreferrer">NLog wiki on GitHub</a> is really comprehensive too - hundreds of pages on there covering everything imaginable. And if you&rsquo;re interested in logging to several targets at once, <a href="https://grantwinney.com/how-to-log-messages-to-multiple-targets-with-nlog/"  target="_blank" rel="noreferrer">check this out</a>.</p>
<p>Good luck! Feel free to reach out and let me know how it goes&hellip;</p>
]]></content:encoded><media:content url="https://grantwinney.com/log-errors-in-winforms-with-nlog/feature.webp" medium="image" type="image/webp"/></item><item><title>Move a subdirectory into its own Git repo, with history</title><link>https://grantwinney.com/how-to-move-a-subdirectory-of-one-repo-into-its-own-repository/</link><pubDate>Fri, 30 Jul 2021 03:38:23 +0000</pubDate><guid>https://grantwinney.com/how-to-move-a-subdirectory-of-one-repo-into-its-own-repository/</guid><description>Need to pull a subdirectory out of repo A and create a new repo B out with it? Including full history and branches? Okay, here&amp;rsquo;s how.</description><content:encoded><![CDATA[<p>I moved a project that was in a subdirectory of a larger, pretty much unrelated, repository. I don&rsquo;t know who thought it belonged there to begin with, but it was my first go at pulling something like that out into its own repo.</p>
<p>Everything below is for Windows, but the instructions shouldn&rsquo;t vary too much for other OS&rsquo;s. YMMV and all that.</p>
<ol>
<li>Follow steps 1-4 in <a href="https://docs.github.com/en/get-started/using-git/splitting-a-subfolder-out-into-a-new-repository"  target="_blank" rel="noreferrer">GitHub&rsquo;s tutorial</a>, but pause there. The <code>filter-branch</code> command in step 5 must be pretty awful, because even the docs <a href="https://git-scm.com/docs/git-filter-branch#_warning"  target="_blank" rel="noreferrer">advise you to use something else</a>.. so we will.</li>
<li>Install <a href="https://www.python.org/downloads/"  target="_blank" rel="noreferrer">Python3</a> if you don&rsquo;t already have it.</li>
<li>Copy the git-filter-repo file from <a href="https://github.com/newren/git-filter-repo"  target="_blank" rel="noreferrer">this repo</a>, and save it <a href="https://helpdeskgeek.com/windows-10/add-windows-path-environment-variable/"  target="_blank" rel="noreferrer">somewhere in your path</a>, so you can access it easily from the command line in like&hellip; 30 seconds.</li>
<li>According to the <a href="https://github.com/newren/git-filter-repo/blob/main/INSTALL.md#notes-for-windows-users"  target="_blank" rel="noreferrer">notes for using it on Windows</a>, you may have to change the first line of the file from <code>python3</code> to <code>python</code>. I did.</li>
<li>Run <code>git filter-repo --subdirectory-filter location/of/subfolder</code>. I had to use the <code>--force</code> command due to some error it was throwing about my repo not being a new clone, even though it was. Didn&rsquo;t harm anything though.</li>
<li>Pick back up with steps 6-10 in <a href="https://docs.github.com/en/get-started/using-git/splitting-a-subfolder-out-into-a-new-repository"  target="_blank" rel="noreferrer">GitHub&rsquo;s tutorial</a>. Step 8 returned nothing for me, and step 9 failed, so I ran <code>git remote add origin https://location_of_your_new_repo</code> instead, and then verified it with the command in step 10.</li>
<li>Instead of step 11, I ran <code>git push -u origin --all</code>.</li>
</ol>
<p>The end result was a new repo, with the previous subdirectory as the root of the new repo, complete with all branches and history. Success! 🎉</p>
]]></content:encoded><media:content url="https://grantwinney.com/how-to-move-a-subdirectory-of-one-repo-into-its-own-repository/feature.webp" medium="image" type="image/webp"/></item><item><title>Can I write my own HTML tags?</title><link>https://grantwinney.com/can-i-write-my-own-html-tags/</link><pubDate>Thu, 15 Jul 2021 21:46:32 +0000</pubDate><guid>https://grantwinney.com/can-i-write-my-own-html-tags/</guid><description>Can you create your own HTML tags? The answer is&amp;hellip;.. sorta. Yes and no. Not completely, but a little. Typical, I know.</description><content:encoded><![CDATA[<p>As usual, the answer is&hellip; yes and no.</p>
<p>There&rsquo;s a standard set of HTML tags, css elements, and JavaScript code that you can use in any browser, like div and font-weight and alert, but the reason any of it works is a two-step process.</p>
<ol>
<li>Someone writes up what all those tags and styles should do, and documents them in the <a href="https://html.spec.whatwg.org/"  target="_blank" rel="noreferrer">HTML Standard</a>, <a href="https://www.w3.org/Style/CSS/specs.en.html"  target="_blank" rel="noreferrer">CSS specs</a>, <a href="https://262.ecma-international.org/12.0/"  target="_blank" rel="noreferrer">ECMAScript language spec</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/API"  target="_blank" rel="noreferrer">Web API specs</a>, etc.</li>
<li>Someone else creates a browser that implements all those standards, so that font-weight makes your text appear bold, not colored red.</li>
</ol>
<p>This leads to a nice, predictable experience, unlike ye olden days when <a href="https://docstore.mik.ua/orelly/web2/wdesign/appd_01.htm"  target="_blank" rel="noreferrer">IE</a> and <a href="https://docstore.mik.ua/orelly/web2/wdesign/appd_02.htm"  target="_blank" rel="noreferrer">Netscape</a> used to create their own proprietary tags that only worked in their own browsers. If you&rsquo;ve been around long enough, you&rsquo;ll remember seeing these littered throughout the net, usually when a site only worked well in a single browser.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/can-i-write-my-own-html-tags/bvw-ie-4x-1.gif"
    width="352"
      height="124"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/can-i-write-my-own-html-tags/image.png"
    width="338"
      height="196"></figure>
<p>But now, standards and standards-compliance is the name of the game, and the differences between the major browsers are in the features they provide (sync&rsquo;ing bookmarks, ad blocking, etc), not in how they display web pages. It&rsquo;s a win-win for everyone.</p>
<p>We can use the standard HTML elements, and apply the standard CSS styles to those elements&hellip;</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/css&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">div</span> <span class="p">{</span> <span class="k">background</span><span class="p">:</span> <span class="mh">#ff6</span><span class="p">;</span> <span class="k">color</span><span class="p">:</span> <span class="kc">red</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>Sample!<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p>&hellip; and we&rsquo;ll always get a box with a yellow background and red text.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/can-i-write-my-own-html-tags/notification1.png"
    width="494"
      height="34"></figure>
<p><em>But&hellip;</em> what if we don&rsquo;t want all our div boxes to have a yellow background? What if you want some of them to have a <em>blue</em> background? Just assign a class named &ldquo;blue&rdquo; to the div, and layer on the css&hellip;</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/css&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">div</span> <span class="p">{</span> <span class="k">background</span><span class="p">:</span> <span class="mh">#ff6</span><span class="p">;</span> <span class="k">color</span><span class="p">:</span> <span class="kc">red</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nt">div</span><span class="p">.</span><span class="nc">blue</span> <span class="p">{</span> <span class="k">background</span><span class="p">:</span> <span class="mh">#6cf</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>Sample!<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;blue&#34;</span><span class="p">&gt;</span>Sample 2!<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p>&hellip; and the new styles are applied to the second div.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/can-i-write-my-own-html-tags/notification2.png"
    width="585"
      height="67"></figure>
<p>But <em>but&hellip;</em> what if you didn&rsquo;t want to keep applying that class to divs that should be blue? What if you just wanted an &ldquo;angrydiv&rdquo; element that displays a bright red box?</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/css&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">div</span> <span class="p">{</span> <span class="k">background</span><span class="p">:</span> <span class="mh">#ff6</span><span class="p">;</span> <span class="k">color</span><span class="p">:</span> <span class="kc">red</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nt">angrydiv</span> <span class="p">{</span> <span class="k">background</span><span class="p">:</span> <span class="kc">red</span><span class="p">;</span> <span class="k">color</span><span class="p">:</span> <span class="kc">white</span><span class="p">;</span> <span class="k">display</span><span class="p">:</span> <span class="kc">block</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>Sample!<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">angrydiv</span><span class="p">&gt;</span>Arrrg!<span class="p">&lt;/</span><span class="nt">angrydiv</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p>That&rsquo;s completely doable. Just create a new element, then use the standard css styles to give it the look you want.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/can-i-write-my-own-html-tags/notification3.png"
    width="521"
      height="61"></figure>
<p>It&rsquo;s interesting that this works, but it&rsquo;s completely predicable and works in every browser I&rsquo;ve tried, so I imagine there&rsquo;s something in the specs that allows for it. If you know where, feel free to leave a comment. I&rsquo;d be interested in checking it out.</p>
<p>The limitation here is that you can&rsquo;t extend existing elements, or just invent new elements that do something outside of how css can change their appearance. Like, you can&rsquo;t just create an &ldquo;errortable&rdquo; element and use that with tr and td elements inside it, because the browser uses all of those in combination, and has no idea what to do with a td if it&rsquo;s not nested in a standard table. Most likely, it&rsquo;ll just display all your text on one line instead of in a tabular format.</p>
<p>But css can do a <em>lot.</em> You can have div&rsquo;s that behave much like tables, then create an errortable, errorrow, etc. I&rsquo;m not saying that&rsquo;s a great idea. In fact, it doesn&rsquo;t offer much of anything over just assigning a class to the table, especially if you&rsquo;re not writing the site by hand using HTML. It&rsquo;s just an interesting oddity.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/can-i-write-my-own-html-tags/image-1.png"
    width="940"
      height="450"></figure>
<p>Because? Why not? Actually, the idea for writing about this came from <a href="https://leonarnott.neocities.org/"  target="_blank" rel="noreferrer">Leon Arnott&rsquo;s neocities site</a>, which I stumbled on at random while hopping around the geocities replacement the other day. The sites on there are <em>seriously</em> bad.. in a wow-websites-were-ugly-so-why-do-i-still-feel-nostalgic sorta way.</p>
<p>Here&rsquo;s a few elements based on those CSS elements (I think I got these from <a href="https://bradgessler.com/"  target="_blank" rel="noreferrer">Brad Gessler</a> originally, but I&rsquo;ve lost the direct link):</p>
<p><a href="https://codepen.io/astrangegame/pen/bNVjxmN"  target="_blank" rel="noreferrer">Goofy CSS effects</a></p>
<p>Still here? Something else random to reward you with then&hellip;</p>
<p>When I was looking for those browser button images at the top of this post, I stumbled across something from 20 years ago that&rsquo;s somehow still up. I love finding this stuff. <a href="https://www.thirteen.org/edonline/primer/"  target="_blank" rel="noreferrer">What is the Internet</a>? How do you <a href="https://www.thirteen.org/edonline/primer/b_how.html"  target="_blank" rel="noreferrer">get the most out of your browser</a>? All your questions will be answered. You&rsquo;re welcome!</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/can-i-write-my-own-html-tags/b-ns.gif"
    width="393"
      height="180"></figure>
]]></content:encoded><media:content url="https://grantwinney.com/can-i-write-my-own-html-tags/feature.webp" medium="image" type="image/webp"/></item><item><title>Avoiding tribal knowledge in the programming world</title><link>https://grantwinney.com/avoiding-tribal-knowledge-in-programming/</link><pubDate>Mon, 05 Jul 2021 19:20:06 +0000</pubDate><guid>https://grantwinney.com/avoiding-tribal-knowledge-in-programming/</guid><description>When I was less skilled as a developer, it was enough to just stay afloat, learning what I needed for the current day or project. As my skill and confidence grows, I&amp;rsquo;ve come to appreciate the extra things in life - like a decent set of docs.</description><content:encoded><![CDATA[<p>Whenever I&rsquo;ve started a new job, the most overwhelming thing is getting up to speed with everything. How do I run this project; has anyone run into that problem before; how do I change the configuration? Do we have a license for such-and-such; what server hosts this website; am I running into a new issue or something we&rsquo;ve already solved before?</p>
<p>No one person has all the answers, who <em>does</em> have the answers can be unclear, and sometimes (shudder) <em>no one</em> currently has the answers! Most knowledge is tribal, living in the heads of people who take bits and pieces of the puzzle with them when they leave&hellip; which can really suck for those of us left behind, cursed to sift through the ashes of whatever cobbled-together corner of the system they&rsquo;ve left behind.</p>
<p>Eventually though, sometimes slowly and painfully, the answers come, the gaps are filled in, and the pieces fall into place. And then the next new hire comes aboard and it happens again.</p>
<hr>
<p>But is it really even our job to write documentation? Isn&rsquo;t a company just hiring us to write code? The electrician I hired to wire up our basement and garage didn&rsquo;t request a full electrical blueprint, nor did he leave me with a wiring diagram afterward. He investigated what was there, built on top of it, and handed me a bill.</p>
<p>Maybe if you&rsquo;re just doing contract work, you can get away with that, but if you&rsquo;re working longterm somewhere, the docs are for <em>you</em> as much as for everyone else. Even shortterm, it seems like the responsible thing to do is leave behind a short user manual and troubleshooting guide, for everyone currently using it and for anyone who has to build on it later. After all, I know what change the electrician made, but the person I sell my house to won&rsquo;t.</p>
<p>When someone asks you about the work you did 6 months ago, how quickly can you answer them? Wouldn&rsquo;t it be nice to just send them a link to a document, instead of having to rack your brain and sift through code and notes? And hopefully, when they&rsquo;re done, they add their own notes in a version of the &ldquo;leave the place cleaner than you found it&rdquo; motto.</p>
<hr>
<p>If you&rsquo;re thinking, &ldquo;who has the time for that?!&rdquo;, then you&rsquo;re thinking about it wrong. Documentation, like writing tests, reviewing requirements, and everything else about coding that&rsquo;s not <em>strictly</em> coding, is best done as you go along. If you wait until the very end of the project, it&rsquo;ll be much more difficult. Eh, let&rsquo;s be honest - it won&rsquo;t happen at all.</p>
<p>When I started a new job last year, going through the same thing again, I took the opportunity to pick up a new habit. When a team member shows me how to access something, I document it. When we&rsquo;re setting up a new website or upgrading a third-party integration, I document it. When there&rsquo;s a meeting and I learn something new that&rsquo;ll come in useful in the future, I document it.</p>
<p>I&rsquo;m documenting pretty much everything I learn about our codebase and infrastructure, and as I learn more, I build the documentation out. It&rsquo;s not like you need some elaborate system either, although <a href="https://grantwinney.com/creating-your-own-secure-wiki-using-dokuwiki/"  target="_blank" rel="noreferrer">I do happen to like wiki&rsquo;s</a>. We have a sharepoint site that&rsquo;s basically a glorified google docs, but it&rsquo;s organized, searchable, and accessible to everyone on the team.</p>
<p><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/avoiding-tribal-knowledge-in-programming/questions-2245264_1280.jpg"
    width="1280"
      height="452"></figure>

<em>Image by <a href="https://pixabay.com/users/geralt-9301/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=2245264"  target="_blank" rel="noreferrer">Gerd Altmann</a> from <a href="https://pixabay.com/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=2245264"  target="_blank" rel="noreferrer">Pixabay</a></em></p>
<p>So what&rsquo;s in the documentation? I try to answer the basic who, when, where, etc questions.</p>
<ul>
<li>Where&rsquo;s the app or part of the system hosted?</li>
<li>How do we access it, change it, run it?</li>
<li>What other parts of the system does it rely on?</li>
<li>Who helped setup the infrastructure for it, developed it, or can help troubleshoot it?</li>
</ul>
<p>Most of the time, it&rsquo;s information that&rsquo;s fresh in my mind anyway and easy to write down, but experience has taught me that one thing pushes out another, and in no time at all it&rsquo;ll be tough to remember!</p>
<p>Do your team (and yourself) a favor&hellip; avoid tribal knowledge!</p>
]]></content:encoded><media:content url="https://grantwinney.com/avoiding-tribal-knowledge-in-programming/feature.webp" medium="image" type="image/webp"/></item><item><title>Using MVP to test a WinForms app</title><link>https://grantwinney.com/its-possible-to-test-a-winforms-app-using-mvp/</link><pubDate>Wed, 09 Jun 2021 02:12:00 +0000</pubDate><guid>https://grantwinney.com/its-possible-to-test-a-winforms-app-using-mvp/</guid><description>If you find yourself supporting a WinForms application, you&amp;rsquo;re likely to notice the tests&amp;hellip; or lack thereof. Just because we may not have been so focused on automated tests and continuous integration when WinForms was younger, that doesn&amp;rsquo;t mean we can&amp;rsquo;t introduce them now. Better late than never!</description><content:encoded><![CDATA[<p>If you find yourself in a position where you&rsquo;re supporting a WinForms application, you&rsquo;re likely to notice the tests&hellip; or lack thereof. Just because we may not have been so focused on automated tests and continuous integration when WinForms was younger, that doesn&rsquo;t mean we can&rsquo;t introduce them now. Better late than never!</p>
<p>Let&rsquo;s say you had a simple Form, like this one. It has 3 fields to enter numbers and an <code>ADD</code> button to, you know, <em>add</em> them in the bottom field. The &ldquo;Running Total&rdquo; field never resets, but just keeps adding each total as long as the app is running.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/its-possible-to-test-a-winforms-app-using-mvp/image-1.png"
    width="636"
      height="341"></figure>
<p>Assume the above is implemented like this.. a relatively short bit of code. <em>None</em> of these methods can take advantage of automated testing. You&rsquo;d need an instance of the Form itself, and every method is accessing or otherwise updating UI components.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">partial</span> <span class="k">class</span> <span class="nc">CalcForm</span> <span class="p">:</span> <span class="n">Form</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Form1</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">InitializeComponent</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">void</span> <span class="n">btnAdd_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">decimal</span> <span class="n">total</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="n">total</span> <span class="p">+=</span> <span class="n">SafeGetNumber</span><span class="p">(</span><span class="n">txtNumber1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">total</span> <span class="p">+=</span> <span class="n">SafeGetNumber</span><span class="p">(</span><span class="n">txtNumber2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">total</span> <span class="p">+=</span> <span class="n">SafeGetNumber</span><span class="p">(</span><span class="n">txtNumber3</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="n">txtTotal</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="n">total</span><span class="p">.</span><span class="n">ToString</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">txtRunningTotal</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="n">SafeGetNumber</span><span class="p">(</span><span class="n">txtTotal</span><span class="p">)</span> <span class="p">+</span> <span class="n">total</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">void</span> <span class="n">btnReset_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">txtNumber1</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="n">txtNumber2</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="n">txtNumber3</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="n">txtTotal</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="s">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">txtNumber1</span><span class="p">.</span><span class="n">Focus</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kt">decimal</span> <span class="n">SafeGetNumber</span><span class="p">(</span><span class="n">TextBox</span> <span class="n">tb</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">    	<span class="k">return</span> <span class="kt">decimal</span><span class="p">.</span><span class="n">TryParse</span><span class="p">(</span><span class="n">tb</span><span class="p">.</span><span class="n">Text</span><span class="p">,</span> <span class="k">out</span> <span class="kt">decimal</span> <span class="n">res</span><span class="p">)</span> <span class="p">?</span> <span class="n">res</span> <span class="p">:</span> <span class="m">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h2 class="relative group">What is MVP?
    <div id="what-is-mvp" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-is-mvp" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>In a nutshell, it&rsquo;s one of many frameworks (MVP, MVC, MVVM, etc) that all try to do the same thing - separate the UI and storage mechanisms from the business logic. There&rsquo;s multiple reasons for this, but right now I&rsquo;m focusing on the fact that it makes it easier to test the business logic.</p>
<p>MVP achieves this in 3 parts - a View, a Presenter, and a Model&hellip; and some interfaces thrown in for good measure. A quick disclaimer first - no doubt there are more ways to implement MVP than what I&rsquo;m about to present, but keep in mind the end goal - to separate the UI from the code we want to test.</p>

<h3 class="relative group">The View
    <div id="the-view" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-view" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The &ldquo;View&rdquo; part of MVP is the Form itself, and it includes an interface that represents everything you might need to get from (or set to) the Form, which is then used by the &ldquo;Presenter&rdquo; (more on that later) to tell it what to display next. Whereas before the View (your Form) had all the code neatly tucked away inside it, it&rsquo;s now very bare.</p>
<p>Here&rsquo;s how I converted the Form. Note that it&rsquo;s actually doing nothing intelligent now, other than wiring up all the UI components to properties defined in the interface. I also took the button click event handlers out of the designer file (where they automatically get created), and made those part of the interface as well. When a button&rsquo;s clicked, the Presenter will know about it, can act on it, and will tell the View what to display next.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">interface</span> <span class="nc">ICalcView</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">event</span> <span class="n">EventHandler</span> <span class="n">Add</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">event</span> <span class="n">EventHandler</span> <span class="n">Reset</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span> <span class="n">Value1</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span> <span class="n">Value2</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span> <span class="n">Value3</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span> <span class="n">Total</span> <span class="p">{</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span> <span class="n">RunningTotal</span> <span class="p">{</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">void</span> <span class="n">SetFocusOnFirstTextBox</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="k">void</span> <span class="n">Show</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">partial</span> <span class="k">class</span> <span class="nc">CalcForm</span> <span class="p">:</span> <span class="n">Form</span><span class="p">,</span> <span class="n">ICalcView</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">event</span> <span class="n">EventHandler</span> <span class="n">Add</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">event</span> <span class="n">EventHandler</span> <span class="n">Reset</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">CalcForm</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">InitializeComponent</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">btnAdd</span><span class="p">.</span><span class="n">Click</span> <span class="p">+=</span> <span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">Add</span><span class="p">.</span><span class="n">Invoke</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">e</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">btnReset</span><span class="p">.</span><span class="n">Click</span> <span class="p">+=</span> <span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">Reset</span><span class="p">.</span><span class="n">Invoke</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">e</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">string</span> <span class="n">ICalcView</span><span class="p">.</span><span class="n">Value1</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">get</span> <span class="p">=&gt;</span> <span class="n">txtNumber1</span><span class="p">.</span><span class="n">Text</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">set</span> <span class="p">=&gt;</span> <span class="n">txtNumber1</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="k">value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span> <span class="n">ICalcView</span><span class="p">.</span><span class="n">Value2</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">get</span> <span class="p">=&gt;</span> <span class="n">txtNumber2</span><span class="p">.</span><span class="n">Text</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">set</span> <span class="p">=&gt;</span> <span class="n">txtNumber2</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="k">value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kt">string</span> <span class="n">ICalcView</span><span class="p">.</span><span class="n">Value3</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">get</span> <span class="p">=&gt;</span> <span class="n">txtNumber3</span><span class="p">.</span><span class="n">Text</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">set</span> <span class="p">=&gt;</span> <span class="n">txtNumber3</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="k">value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Total</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">set</span> <span class="p">=&gt;</span> <span class="n">txtTotal</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="k">value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">RunningTotal</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">set</span> <span class="p">=&gt;</span> <span class="n">txtRunningTotal</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="k">value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">SetFocusOnFirstTextBox</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">txtNumber1</span><span class="p">.</span><span class="n">Focus</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h3 class="relative group">The Model
    <div id="the-model" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-model" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The &ldquo;Model&rdquo; represents an object that you&rsquo;re operating on. In my case, I made the Model a sort of calculator object that stores the totals and has a couple of (very testable) methods for affecting that data.</p>
<p>What you put in here is up to you, but just keep the end goal in mind of wanting to be able to test the logic. The View is the UI, the Presenter is the business logic, and the Model is for data.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">interface</span> <span class="nc">ICalcModel</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">decimal</span> <span class="n">Total</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kt">decimal</span> <span class="n">RunningTotal</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">void</span> <span class="n">CalculateTotal</span><span class="p">(</span><span class="n">List</span><span class="p">&lt;</span><span class="kt">decimal</span><span class="p">&gt;</span> <span class="n">numbers</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">void</span> <span class="n">ResetTotal</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">CalcModel</span> <span class="p">:</span> <span class="n">ICalcModel</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">decimal</span> <span class="n">Total</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">decimal</span> <span class="n">RunningTotal</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">CalculateTotal</span><span class="p">(</span><span class="n">List</span><span class="p">&lt;</span><span class="kt">decimal</span><span class="p">&gt;</span> <span class="n">numbers</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Total</span> <span class="p">=</span> <span class="n">numbers</span><span class="p">.</span><span class="n">Sum</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">RunningTotal</span> <span class="p">+=</span> <span class="n">Total</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">ResetTotal</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Total</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">RunningTotal</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h3 class="relative group">The Presenter
    <div id="the-presenter" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-presenter" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>So far, we&rsquo;ve got a View that displays nothing, and a Model that stores numbers but can&rsquo;t do much else. What&rsquo;s the glue that ties them together? I present.. the Presenter!</p>
<p>The Presenter doesn&rsquo;t have an interface, at least not the way I designed it. But it does accept the interfaces that the View and Model implement, and it operates on those. It orchestrates everything, subscribing to events in the View, getting data from the View, passing that data to the Model, and moving things back and forth as needed.</p>
<p>Note that it doesn&rsquo;t actually <em>touch</em> the UI though. It just calls methods on and passes data back to the View, which in turn updates the UI. That&rsquo;s important for testing, because if our presenter touches the UI then we&rsquo;re right back where we started.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">CalcPresenter</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">readonly</span> <span class="n">ICalcView</span> <span class="n">calcView</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">readonly</span> <span class="n">ICalcModel</span> <span class="n">calcModel</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">CalcPresenter</span><span class="p">(</span><span class="n">ICalcView</span> <span class="n">view</span><span class="p">,</span> <span class="n">ICalcModel</span> <span class="n">model</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">calcView</span> <span class="p">=</span> <span class="n">view</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">calcModel</span> <span class="p">=</span> <span class="n">model</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">calcView</span><span class="p">.</span><span class="n">Add</span> <span class="p">+=</span> <span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">Add</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">calcView</span><span class="p">.</span><span class="n">Reset</span> <span class="p">+=</span> <span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">Reset</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">calcView</span><span class="p">.</span><span class="n">Show</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">Add</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">calcModel</span><span class="p">.</span><span class="n">CalculateTotal</span><span class="p">(</span><span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="p">{</span> <span class="n">calcView</span><span class="p">.</span><span class="n">Value1</span><span class="p">,</span> <span class="n">calcView</span><span class="p">.</span><span class="n">Value2</span><span class="p">,</span> <span class="n">calcView</span><span class="p">.</span><span class="n">Value3</span> <span class="p">}.</span><span class="n">ConvertAll</span><span class="p">(</span><span class="n">TryGetNumber</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">calcView</span><span class="p">.</span><span class="n">Total</span> <span class="p">=</span> <span class="n">Convert</span><span class="p">.</span><span class="n">ToString</span><span class="p">(</span><span class="n">calcModel</span><span class="p">.</span><span class="n">Total</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">calcView</span><span class="p">.</span><span class="n">RunningTotal</span> <span class="p">=</span> <span class="n">Convert</span><span class="p">.</span><span class="n">ToString</span><span class="p">(</span><span class="n">calcModel</span><span class="p">.</span><span class="n">RunningTotal</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">Reset</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">calcModel</span><span class="p">.</span><span class="n">ResetTotal</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">calcView</span><span class="p">.</span><span class="n">Value1</span> <span class="p">=</span> <span class="n">calcView</span><span class="p">.</span><span class="n">Value2</span> <span class="p">=</span> <span class="n">calcView</span><span class="p">.</span><span class="n">Value3</span> <span class="p">=</span> <span class="n">calcView</span><span class="p">.</span><span class="n">Total</span> <span class="p">=</span> <span class="n">calcView</span><span class="p">.</span><span class="n">RunningTotal</span> <span class="p">=</span> <span class="s">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">calcView</span><span class="p">.</span><span class="n">SetFocusOnFirstTextBox</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">decimal</span> <span class="n">TryGetNumber</span><span class="p">(</span><span class="kt">string</span> <span class="n">input</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kt">decimal</span><span class="p">.</span><span class="n">TryParse</span><span class="p">(</span><span class="n">input</span><span class="p">,</span> <span class="k">out</span> <span class="kt">decimal</span> <span class="n">res</span><span class="p">)</span> <span class="p">?</span> <span class="n">res</span> <span class="p">:</span> <span class="m">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h2 class="relative group">How do I tie these layers together?
    <div id="how-do-i-tie-these-layers-together" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#how-do-i-tie-these-layers-together" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The way I have it above, the constructor in the Presenter accepts the interface for the View and Model. How you pass concrete instances of each is up to you.</p>
<p>The easy way is to just create an instance of both when you need them, assuming there&rsquo;s nothing around that logic that you want to test. Personally, I don&rsquo;t care what the &ldquo;launch calculator&rdquo; button is doing.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnLaunchCalculator_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span> <span class="n">CalcPresenter</span><span class="p">(</span><span class="k">new</span> <span class="n">CalcForm</span><span class="p">(),</span> <span class="k">new</span> <span class="n">CalcModel</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The other way is to use dependency injection, where you configure a framework to resolve dependencies for you, instead of having to instantiate everything yourself. That&rsquo;s much more than I want to go into here, but there&rsquo;s plenty of resources out there if you want to learn more.</p>
<p>Here&rsquo;s a couple links regarding Unity:</p>
<ul>
<li><a href="https://www.c-sharpcorner.com/article/dependency-injection-using-unity-resolve-dependency-of-dependencies/"  target="_blank" rel="noreferrer">Dependency Injection Using Unity - Resolve Dependency Of Dependencies</a></li>
<li><a href="https://www.accusoft.com/resources/blog/dependency-injection-going-start-finish-unity-c/"  target="_blank" rel="noreferrer">Dependency Injection: Going Start to Finish With Unity in C#</a></li>
</ul>
<p>And some SO advice on using a standard Microsoft package:</p>
<ul>
<li><a href="https://stackoverflow.com/a/70476716"  target="_blank" rel="noreferrer">How to use Dependency Injection (DI) in Windows Forms (WinForms)</a></li>
</ul>

<h2 class="relative group">Now how does all this help with testing?
    <div id="now-how-does-all-this-help-with-testing" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#now-how-does-all-this-help-with-testing" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>&ldquo;Ugh, this is <em>soo</em> much longer than before&rdquo;, you might be thinking. Okay, it is&hellip;. but it&rsquo;s also more intentional, and concerns are more separated. It allows us to mock the interfaces and <em>thoroughly</em> test the logic in the Presenter and Model, like this.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="na">[TestFixture]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">CalcPresenterTests</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Mock</span><span class="p">&lt;</span><span class="n">ICalcView</span><span class="p">&gt;</span> <span class="n">mockView</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">Mock</span><span class="p">&lt;</span><span class="n">ICalcModel</span><span class="p">&gt;</span> <span class="n">mockModel</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">CalcPresenter</span> <span class="n">presenter</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [SetUp]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">Setup</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">mockModel</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Mock</span><span class="p">&lt;</span><span class="n">ICalcModel</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl">        <span class="n">mockView</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Mock</span><span class="p">&lt;</span><span class="n">ICalcView</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl">        <span class="n">presenter</span> <span class="p">=</span> <span class="k">new</span> <span class="n">CalcPresenter</span><span class="p">(</span><span class="n">mockView</span><span class="p">.</span><span class="n">Object</span><span class="p">,</span> <span class="n">mockModel</span><span class="p">.</span><span class="n">Object</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [Test]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">AddTest</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">mockView</span><span class="p">.</span><span class="n">SetupGet</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">Value1</span><span class="p">).</span><span class="n">Returns</span><span class="p">(</span><span class="s">&#34;10&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">mockView</span><span class="p">.</span><span class="n">SetupGet</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">Value2</span><span class="p">).</span><span class="n">Returns</span><span class="p">(</span><span class="s">&#34;20&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">mockView</span><span class="p">.</span><span class="n">SetupGet</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">Value3</span><span class="p">).</span><span class="n">Returns</span><span class="p">(</span><span class="s">&#34;30&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">mockModel</span><span class="p">.</span><span class="n">SetupGet</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">Total</span><span class="p">).</span><span class="n">Returns</span><span class="p">(</span><span class="m">60</span><span class="n">m</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">mockModel</span><span class="p">.</span><span class="n">SetupGet</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">RunningTotal</span><span class="p">).</span><span class="n">Returns</span><span class="p">(</span><span class="m">100</span><span class="n">m</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">presenter</span><span class="p">.</span><span class="n">Add</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">mockModel</span><span class="p">.</span><span class="n">Verify</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">CalculateTotal</span><span class="p">(</span><span class="n">It</span><span class="p">.</span><span class="n">IsAny</span><span class="p">&lt;</span><span class="n">List</span><span class="p">&lt;</span><span class="kt">decimal</span><span class="p">&gt;&gt;()),</span> <span class="n">Times</span><span class="p">.</span><span class="n">Once</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">mockView</span><span class="p">.</span><span class="n">VerifySet</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">Total</span> <span class="p">=</span> <span class="s">&#34;60&#34;</span><span class="p">,</span> <span class="n">Times</span><span class="p">.</span><span class="n">Once</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">mockView</span><span class="p">.</span><span class="n">VerifySet</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">RunningTotal</span> <span class="p">=</span> <span class="s">&#34;100&#34;</span><span class="p">,</span> <span class="n">Times</span><span class="p">.</span><span class="n">Once</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [Test]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">ResetTest</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">presenter</span><span class="p">.</span><span class="n">Reset</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">mockView</span><span class="p">.</span><span class="n">VerifySet</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">Value1</span> <span class="p">=</span> <span class="s">&#34;&#34;</span><span class="p">,</span> <span class="n">Times</span><span class="p">.</span><span class="n">Once</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">mockView</span><span class="p">.</span><span class="n">VerifySet</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">Value2</span> <span class="p">=</span> <span class="s">&#34;&#34;</span><span class="p">,</span> <span class="n">Times</span><span class="p">.</span><span class="n">Once</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">mockView</span><span class="p">.</span><span class="n">VerifySet</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">Value3</span> <span class="p">=</span> <span class="s">&#34;&#34;</span><span class="p">,</span> <span class="n">Times</span><span class="p">.</span><span class="n">Once</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">mockView</span><span class="p">.</span><span class="n">VerifySet</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">Total</span> <span class="p">=</span> <span class="s">&#34;&#34;</span><span class="p">,</span> <span class="n">Times</span><span class="p">.</span><span class="n">Once</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">mockView</span><span class="p">.</span><span class="n">VerifySet</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">RunningTotal</span> <span class="p">=</span> <span class="n">It</span><span class="p">.</span><span class="n">IsAny</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;(),</span> <span class="n">Times</span><span class="p">.</span><span class="n">Never</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [Test]</span>
</span></span><span class="line"><span class="cl"><span class="na">    [TestCase(&#34;3&#34;, 3)]</span>
</span></span><span class="line"><span class="cl"><span class="na">    [TestCase(&#34;-3.22&#34;, -3.22)]</span>
</span></span><span class="line"><span class="cl"><span class="na">    [TestCase(&#34;0&#34;, 0)]</span>
</span></span><span class="line"><span class="cl"><span class="na">    [TestCase(&#34;&#34;, 0)]</span>
</span></span><span class="line"><span class="cl"><span class="na">    [TestCase(&#34;bad input!!&#34;, 0)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">TryGetNumberReturnsExpectedValue</span><span class="p">(</span><span class="kt">string</span> <span class="n">input</span><span class="p">,</span> <span class="kt">decimal</span> <span class="n">output</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">AreEqual</span><span class="p">(</span><span class="n">output</span><span class="p">,</span> <span class="n">presenter</span><span class="p">.</span><span class="n">TryGetNumber</span><span class="p">(</span><span class="n">input</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">[TestFixture]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">CalcModelTests</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">CalcModel</span> <span class="n">model</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [SetUp]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">Setup</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">model</span> <span class="p">=</span> <span class="k">new</span> <span class="n">CalcModel</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [Test]</span>
</span></span><span class="line"><span class="cl"><span class="na">    [TestCase(-13, -1, -1, -1, -10)]</span>
</span></span><span class="line"><span class="cl"><span class="na">    [TestCase(0, 0, 0, 0)]</span>
</span></span><span class="line"><span class="cl"><span class="na">    [TestCase(15, 1, 2, 3, 4, 5)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">AddingNumbersGeneratesExpectedTotal</span><span class="p">(</span><span class="kt">decimal</span> <span class="n">expectedTotal</span><span class="p">,</span> <span class="k">params</span> <span class="kt">int</span><span class="p">[]</span> <span class="n">inputs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">model</span><span class="p">.</span><span class="n">CalculateTotal</span><span class="p">(</span><span class="n">inputs</span><span class="p">.</span><span class="n">Select</span><span class="p">(</span><span class="n">Convert</span><span class="p">.</span><span class="n">ToDecimal</span><span class="p">).</span><span class="n">ToList</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">AreEqual</span><span class="p">(</span><span class="n">expectedTotal</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">Total</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [Test]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">AddingNumbersTwiceRetainsLastOnly</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">model</span><span class="p">.</span><span class="n">CalculateTotal</span><span class="p">(</span><span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">decimal</span><span class="p">&gt;</span> <span class="p">{</span> <span class="m">1</span><span class="p">,</span> <span class="m">2</span><span class="p">,</span> <span class="m">3</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">        <span class="n">model</span><span class="p">.</span><span class="n">CalculateTotal</span><span class="p">(</span><span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">decimal</span><span class="p">&gt;</span> <span class="p">{</span> <span class="m">10</span><span class="p">,</span> <span class="m">20</span><span class="p">,</span> <span class="m">30</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">AreEqual</span><span class="p">(</span><span class="m">60</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">Total</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [Test]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">AddingNumbersTwiceIncreasesRunningTotal</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">model</span><span class="p">.</span><span class="n">CalculateTotal</span><span class="p">(</span><span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">decimal</span><span class="p">&gt;</span> <span class="p">{</span> <span class="m">1</span><span class="p">,</span> <span class="m">2</span><span class="p">,</span> <span class="m">3</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">AreEqual</span><span class="p">(</span><span class="m">6</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">RunningTotal</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">model</span><span class="p">.</span><span class="n">CalculateTotal</span><span class="p">(</span><span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">decimal</span><span class="p">&gt;</span> <span class="p">{</span> <span class="m">10</span><span class="p">,</span> <span class="m">20</span><span class="p">,</span> <span class="m">30</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">AreEqual</span><span class="p">(</span><span class="m">66</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">RunningTotal</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">    
</span></span></span><span class="line"><span class="cl"><span class="na">    [Test]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">ResetNumbersToZeroWorksAsExpected</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">model</span><span class="p">.</span><span class="n">CalculateTotal</span><span class="p">(</span><span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">decimal</span><span class="p">&gt;</span> <span class="p">{</span> <span class="m">1</span><span class="p">,</span> <span class="m">2</span><span class="p">,</span> <span class="m">3</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">AreEqual</span><span class="p">(</span><span class="m">6</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">Total</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">AreEqual</span><span class="p">(</span><span class="m">6</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">RunningTotal</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">model</span><span class="p">.</span><span class="n">ResetTotal</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">AreEqual</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">Total</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">AreEqual</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">RunningTotal</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The end result? The beginnings of an automated test suite! You can plug this into TeamCity, Jenkins, or another CI tool and begin to get automated test runs. Yes, this is a lot more difficult in a large app that&rsquo;s been around for years, but with effort it&rsquo;s absolutely doable, one step at a time.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/its-possible-to-test-a-winforms-app-using-mvp/mvp-test-run-success.png"
    width="1206"
      height="496"></figure>
]]></content:encoded><media:content url="https://grantwinney.com/its-possible-to-test-a-winforms-app-using-mvp/feature.webp" medium="image" type="image/webp"/></item><item><title>Using Async, Await, and Task to keep the WinForms UI responsive</title><link>https://grantwinney.com/using-async-await-and-task-to-keep-the-winforms-ui-more-responsive/</link><pubDate>Mon, 07 Jun 2021 12:56:43 +0000</pubDate><guid>https://grantwinney.com/using-async-await-and-task-to-keep-the-winforms-ui-more-responsive/</guid><description>Using the async/await pattern in WinForms is an easy win, helping prevent one of the most annoying user experiences - a frozen UI.</description><content:encoded><![CDATA[<p>For most of my dev career, I&rsquo;ve been in C# shops. That doesn&rsquo;t mean <em>every</em> project required C# exclusively, but most of them did. I&rsquo;ve also used React, Ruby, C++, Erlang.. whatever&rsquo;s called for. But large company or small, if you&rsquo;re a C# dev, sooner or later you&rsquo;ll likely find yourself supporting a WinForms app. And crystal reports, but we shan&rsquo;t speak of that here. 😑</p>
<p>WinForms is 20 years old, but doesn&rsquo;t show signs of disappearing anytime soon. It still exists in <a href="https://docs.microsoft.com/en-us/dotnet/desktop/winforms/?view=netdesktop-5.0"  target="_blank" rel="noreferrer">.NET 5.0</a> (the successor to .NET Core 3.1), and will exist in <a href="https://dotnet.microsoft.com/download/dotnet/6.0"  target="_blank" rel="noreferrer">.NET 6.0</a> later this year. Web design, SAAS, and the cloud are all the rage, but not everyone is looking to upgrade or trusts their data to someone else&rsquo;s server. For a lot of people in rural or developing areas, the Internet is spotty at best, so the move to all web-based apps may not be an option!</p>
<p>In light of all that, and because the flagship app where I&rsquo;m currently employed is written in WinForms, I&rsquo;m going to start a series of posts that explore how we can make it, well&hellip; suck less. The .NET Framework was very, <em>very</em> different 20 years ago, but old paradigms continue on. No one wants to change the original code, and it&rsquo;s easier to introduce new code using the same old design patterns.</p>
<blockquote><p>The code in this article is available on <a href="https://github.com/grantwinney/SurvivingWinForms/tree/master/Threading/AsyncAwait"  target="_blank" rel="noreferrer">GitHub</a>, for you to use or just follow along with.</p>
</blockquote><p>But even if we don&rsquo;t want to overhaul a huge app, can&rsquo;t we leave the place cleaner than we found it? So let&rsquo;s check out something that can improve nearly any area of code, if used carefully - threading using the async/await pattern.</p>

<h2 class="relative group">What is Async / Await?
    <div id="what-is-async--await" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-is-async--await" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>We don&rsquo;t do single-threaded in real life. Imagine just standing there while the washer finishes cleaning your clothes, or the coffee machine brews your favorite java. Worse yet, you <em>can&rsquo;t</em> move until the clothes are clean. Everything around you just freezes in place. It&rsquo;s ridiculous.</p>
<p>But it&rsquo;s a <em>lot</em> easier to think that way, isn&rsquo;t it? Things are so much more predictable when you know one thing will happen at a time, that task A will end before task B begins. But just like in real life everyone would be <em>seriously</em> annoyed with you, in app dev life the user is annoyed. It&rsquo;s easy, but not right.</p>
<p>A much better user experience is to run tasks separately from the UI thread, and even better is to run <em>multiple</em> things separately and safely. To let a job run in the background while the user moves on to something else&hellip; or at least sees progress instead of a frozen UI.</p>
<p>The .NET Framework has had different ways of doing this for a long time, but the async/await pattern in .NET 4.5 is easier than ever before. We can group chunks of code together, run them separately from one another and the main UI thread, and we can tell the app when it should wait for certain threads to complete before going any further. Let&rsquo;s take a look at an example.</p>

<h2 class="relative group">Example 1: Running everything from the UI
    <div id="example-1-running-everything-from-the-ui" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#example-1-running-everything-from-the-ui" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The most common thing to do in a WinForms app (in my experience) is to just keep adding more and more code, without thinking about where it&rsquo;s running&hellip; which usually means the UI thread by default. The problem is that a long-running job running on the UI thread freezes the app. Even short job will lock the UI for a half-second here, a full second there, even if we&rsquo;ve gotten used to it.</p>
<p>In the <a href="https://github.com/grantwinney/Surviving-WinForms/blob/master/Threading/AsyncAwait/AsyncAwait/Breakfast.cs#L175"  target="_blank" rel="noreferrer">BreakfastSingleThread class</a>, I&rsquo;ve written a couple dozen methods for making a full breakfast. They&rsquo;re not doing any real work.. just sleeping for a fraction of a second or so, then continuing. When you click the &ldquo;Run on Main Thread&rdquo; button, it kicks off the process with this: <em>(ignore the inline method passed to the ctor - I&rsquo;m passing messages to the TextBox control, but that&rsquo;s not important right now)</em></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">bmt</span> <span class="p">=</span> <span class="k">new</span> <span class="n">BreakfastSingleThread</span><span class="p">((</span><span class="n">text</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">txtMainThread</span><span class="p">.</span><span class="n">AppendText</span><span class="p">(</span><span class="n">text</span> <span class="p">+</span> <span class="n">Environment</span><span class="p">.</span><span class="n">NewLine</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">bmt</span><span class="p">.</span><span class="n">MakeBreakfast</span><span class="p">();</span></span></span></code></pre></div></div>
<p>Watch below how, while the job is running, the UI is completely unresponsive. I can&rsquo;t resize or move, buttons don&rsquo;t respond&hellip; but when it&rsquo;s done, the UI plays catchup and processes all the pending messages in the queue.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-async-await-and-task-to-keep-the-winforms-ui-more-responsive/1mainthreadani.gif"
    width="852"
      height="461"></figure>

<h2 class="relative group">Example 2: Moving logic to a separate thread
    <div id="example-2-moving-logic-to-a-separate-thread" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#example-2-moving-logic-to-a-separate-thread" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>So how easy is it to take advantage of async/await, in order to not block the UI? You can change the second line in the above example to this, which tells it to run the code in a separate thread but then wait for it.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">await</span> <span class="n">Task</span><span class="p">.</span><span class="n">Run</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="n">bmt</span><span class="p">.</span><span class="n">MakeBreakfast</span><span class="p">());</span></span></span></code></pre></div></div>
<p>Now the UI thread is left available to process other events, so the progress bar animates, buttons are responsive, and the window can be resized and moved. You can disable anything you don&rsquo;t want the user to do (like I did with the button), but the UI itself doesn&rsquo;t lock up. <em>With one line of code!</em></p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-async-await-and-task-to-keep-the-winforms-ui-more-responsive/2separatethreadani.gif"
    width="852"
      height="469"></figure>

<h2 class="relative group">Example 3: Moving logic to many separate threads
    <div id="example-3-moving-logic-to-many-separate-threads" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#example-3-moving-logic-to-many-separate-threads" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>That previous example was a quick win. The UI was responsive again, but it&rsquo;s still running everything concurrently, so it takes the same amount of time by the end.</p>
<p>Running things in <em>separate</em> threads would be a much larger win, but there&rsquo;s more changes to make in the code, and we have to be more careful about what we&rsquo;re calling, and when we have to wait (await) for certain parts to finish before moving on with others.</p>
<p>To support multiple threads, I rewrote the previous class and named it <a href="https://github.com/grantwinney/Surviving-WinForms/blob/master/Threading/AsyncAwait/AsyncAwait/Breakfast.cs#L204"  target="_blank" rel="noreferrer">BreakfastMultipleThreads.cs</a>. The changes are pretty significant. Many of the methods have been changed to support async operations.</p>
<ul>
<li>The method signatures have changed from <code>void</code> to <code>async Task</code>. If you try to use <code>void Task</code>, you&rsquo;ll get a syntax error and VS will offer to change it for you.</li>
<li>The main &ldquo;work&rdquo; in each method (a sleepy thread) is moved inside a separate task. When the task is complete (we <code>await</code> it), the message is printed. I could&rsquo;ve called <code>await Task.Delay(2000)</code> but felt the way I did it made it clearer that we could&rsquo;ve been running other (more realistic) code.</li>
</ul>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Before"
    src="/using-async-await-and-task-to-keep-the-winforms-ui-more-responsive/2021-06-06-21_53_00-SurvivingWinForms---Microsoft-Visual-Studio.png"
    width="417"
      height="289"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="After"
    src="/using-async-await-and-task-to-keep-the-winforms-ui-more-responsive/2021-06-06-21_53_22-SurvivingWinForms---Microsoft-Visual-Studio.png"
    width="419"
      height="419"></figure>
<ul>
<li>Methods that called several other methods one at a time in order, like the steps for brewing a cup of coffee, still call them in order, but now we <code>await</code> for each task to complete before proceeding.</li>
<li>Not all methods were converted to async though. The steps for cooking eggs were not, so they&rsquo;re all grouped inside a single task together.</li>
</ul>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Before"
    src="/using-async-await-and-task-to-keep-the-winforms-ui-more-responsive/2021-06-06-21_55_37-SurvivingWinForms---Microsoft-Visual-Studio.png"
    width="407"
      height="294"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="After"
    src="/using-async-await-and-task-to-keep-the-winforms-ui-more-responsive/2021-06-06-21_55_50-SurvivingWinForms---Microsoft-Visual-Studio.png"
    width="414"
      height="296"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Before"
    src="/using-async-await-and-task-to-keep-the-winforms-ui-more-responsive/2021-06-06-22_00_38-SurvivingWinForms---Microsoft-Visual-Studio.png"
    width="391"
      height="392"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="After"
    src="/using-async-await-and-task-to-keep-the-winforms-ui-more-responsive/2021-06-06-22_01_07-SurvivingWinForms---Microsoft-Visual-Studio.png"
    width="399"
      height="465"></figure>
<ul>
<li>Methods that can be executed at the same time, like cooking bacon and eggs, or pouring the orange juice while the coffee brews, are run in separate tasks at the same time. <em><strong>(big win!)</strong></em></li>
<li>Obviously certain things just can&rsquo;t be. You can&rsquo;t make the sandwich until everything&rsquo;s cooked. You can&rsquo;t pour the coffee while it&rsquo;s still brewing.</li>
</ul>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Before"
    src="/using-async-await-and-task-to-keep-the-winforms-ui-more-responsive/2021-06-06-22_06_03-SurvivingWinForms---Microsoft-Visual-Studio.png"
    width="525"
      height="381"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="After"
    src="/using-async-await-and-task-to-keep-the-winforms-ui-more-responsive/2021-06-06-22_06_13-SurvivingWinForms---Microsoft-Visual-Studio.png"
    width="564"
      height="505"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Before"
    src="/using-async-await-and-task-to-keep-the-winforms-ui-more-responsive/2021-06-06-22_06_39-SurvivingWinForms---Microsoft-Visual-Studio.png"
    width="392"
      height="288"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="After"
    src="/using-async-await-and-task-to-keep-the-winforms-ui-more-responsive/2021-06-06-22_06_54-SurvivingWinForms---Microsoft-Visual-Studio.png"
    width="574"
      height="504"></figure>
<p>The real time saver here is running multiple tasks. We&rsquo;re no longer staring at the coffee machine, or waiting on the bacon until the eggs are fried. We&rsquo;re doing everything pretty much how we&rsquo;d make an actual breakfast, getting one thing going and then starting another while that finishes.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-async-await-and-task-to-keep-the-winforms-ui-more-responsive/3multiplethreadsani.gif"
    width="852"
      height="602"></figure>
<p>The result is pretty drastic! Even though I picked random sleep values that don&rsquo;t mean much by themselves, it&rsquo;s easy to see that multithreading is faster. If you look at the order of things in the last pane though, you&rsquo;ll see that it never finishes steps out of order, because I was careful about what I told it to kick off in parallel, and when to <code>await</code> for steps to finish before proceeding.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-async-await-and-task-to-keep-the-winforms-ui-more-responsive/3multiplethreads.png"
    width="852"
      height="602"></figure>
<p>I made one more change too. In the second example, I sent messages back to the UI thread to write them out, but when that happens, it pauses my task for a moment while the UI handles it. In the third example, I used the <a href="https://docs.microsoft.com/en-us/dotnet/api/system.progress-1?view=net-5.0"  target="_blank" rel="noreferrer"><code>Progress&lt;T&gt;</code> class</a> that was introduced in .NET 4.5 instead.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// frmResponsiveModal.cs</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">progress</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Progress</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl"><span class="n">progress</span><span class="p">.</span><span class="n">ProgressChanged</span> <span class="p">+=</span> <span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(!</span><span class="n">txtMultipleThreads</span><span class="p">.</span><span class="n">IsDisposed</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">txtMultipleThreads</span><span class="p">.</span><span class="n">AppendText</span><span class="p">(</span><span class="n">message</span> <span class="p">+</span> <span class="n">Environment</span><span class="p">.</span><span class="n">NewLine</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">bmt</span> <span class="p">=</span> <span class="k">new</span> <span class="n">BreakfastMultipleThreads</span><span class="p">(</span><span class="n">progress</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">await</span> <span class="n">Task</span><span class="p">.</span><span class="n">Run</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="n">bmt</span><span class="p">.</span><span class="n">MakeBreakfastAsync</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// BreakfastMultipleThreads.cs</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">SendMessage</span><span class="p">(</span><span class="kt">string</span> <span class="n">text</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">progress</span><span class="p">.</span><span class="n">Report</span><span class="p">(</span><span class="s">$&#34;[{ stopwatch.ElapsedMilliseconds }] {text}&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>I don&rsquo;t know the ins and outs of how this differs yet, but the effects are pretty noticeable when you run all three examples at once. Check out how the three examples behave. When the one that blocks the UI thread runs, it does what it always does - freezes the whole UI. But when it completes, two things happen:</p>
<ol>
<li>The example using <code>Progress&lt;T&gt;</code> immediately prints out all the rest of its messages as if no time has elapsed, and completes. It&rsquo;s clear that it continued running and completed, but its updates for the UI were stored.</li>
<li>The example <em>not</em> using it was frozen in place, waiting on the main UI thread. When it continues, the stopwatch reports that 16.4 seconds has elapsed - the time it took the first example to complete.</li>
</ol>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-async-await-and-task-to-keep-the-winforms-ui-more-responsive/4allatonce.gif"
    width="852"
      height="652"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-async-await-and-task-to-keep-the-winforms-ui-more-responsive/4allatonce.png"
    width="852"
      height="652"></figure>

<h2 class="relative group">Learn more
    <div id="learn-more" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#learn-more" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you want to learn much more than I can include in a post, check out these great tutorials on YouTube by <a href="https://www.iamtimcorey.com/"  target="_blank" rel="noreferrer">Tim Corey</a>. The first one covers most of what I covered here, while the second one covers some advanced topics like the <code>Progress&lt;T&gt;</code> class.</p>
<p><a href="https://www.youtube.com/watch?v=2moh18sh5p4"  target="_blank" rel="noreferrer">C# Async / Await - Make your app more responsive and faster with asynchronous programming - YouTube</a><br>
<a href="https://www.youtube.com/watch?v=ZTKGRJy5P2M"  target="_blank" rel="noreferrer">C# Advanced Async - Getting progress reports, cancelling tasks, and more - YouTube</a></p>
<p>If you prefer reading, check out <a href="https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/async-scenarios"  target="_blank" rel="noreferrer">Bill Wagner&rsquo;s article on async programming</a> too.</p>
<p>And finally, if you&rsquo;ve got something that&rsquo;s <em>already</em> async, like a BackgroundWorker, and you want to convert it to a Task, I wrote about that here:</p>
<p><a href="https://grantwinney.com/convert-backgroundworker-to-task-with-taskcompletionsource/"  target="_blank" rel="noreferrer">Converting a BackgroundWorker to a Task with TaskCompletionSource</a></p>
]]></content:encoded><media:content url="https://grantwinney.com/using-async-await-and-task-to-keep-the-winforms-ui-more-responsive/feature.webp" medium="image" type="image/webp"/></item><item><title>What is DotNet Try?</title><link>https://grantwinney.com/what-is-dotnet-try/</link><pubDate>Sat, 29 May 2021 21:40:16 +0000</pubDate><guid>https://grantwinney.com/what-is-dotnet-try/</guid><description>Do you prefer reading or doing? How about both? DotNet Try pulls in C# code from your project and turns your docs into an interactive experience.</description><content:encoded><![CDATA[<p>Some of my favorite sources of documentation are the ones that include interactive code snippets you can run right on the site. For example, any of the fantastic MDN web docs, like this one for <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map"  target="_blank" rel="noreferrer">Array.prototype.map()</a>. They&rsquo;ve really figured out how to do documentation right. 👍</p>
<p>But this isn&rsquo;t about MDN, it&rsquo;s about another tool I stumbled across while I was looking for some resources on LINQ. Microsoft has a tool for creating interactive C# documentation using markdown files, called DotNet Try (or Try .NET, depending on where on their site you&rsquo;re reading about it.. naming things is hard).</p>

<h2 class="relative group">Installing DotNet Try
    <div id="installing-dotnet-try" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#installing-dotnet-try" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Before we delve into it, you&rsquo;ll have to install it. The details are <a href="https://github.com/dotnet/try/blob/main/DotNetTryLocal.md"  target="_blank" rel="noreferrer">here</a>.</p>
<p>Download the SDKs like they suggest. .NET Core 3.0 is outdated now, but you can find the latest versions <a href="https://dotnet.microsoft.com/download/dotnet"  target="_blank" rel="noreferrer">here</a>. Before you bother downloading anything though, double-check your apps&hellip; you may already have what you need.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-dotnet-try/image-12.png"
    width="1515"
      height="860"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="500"
    src="/what-is-dotnet-try/image-11.png"
    width="589"
      height="401"></figure>
<p>When that&rsquo;s done, you can use the <code>dotnet</code> command line tool to <a href="https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet#global-tool-path-and-local-tools-commands"  target="_blank" rel="noreferrer">install other tools</a>, like DotNet Try. Open the command line of your choice and run this command:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">dotnet tool update -g Microsoft.dotnet-try</code></pre></div>

<h2 class="relative group">Kicking the Tires
    <div id="kicking-the-tires" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#kicking-the-tires" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Now go back to their <a href="https://github.com/dotnet/try/blob/main/DotNetTryLocal.md#getting-started"  target="_blank" rel="noreferrer">help doc</a> and check out the whole &ldquo;Getting Started&rdquo; section if you want to get more familiar with it. I got a <em>&ldquo;your connection is not private message&rdquo;,</em> but clicking &ldquo;proceed to localhost&rdquo; got me to something much more interesting.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-dotnet-try/image-7.png"
    width="1142"
      height="862"></figure>
<p>Play around with it a bit, change the code, progress through their examples, try different things out.. maybe break it. lol️</p>

<h2 class="relative group">Behind the Curtain
    <div id="behind-the-curtain" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#behind-the-curtain" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>To understand what&rsquo;s going on, find the directory where you ran the <code>dotnet try demo</code> command and check out the markdown files. Let me just say that I really like they&rsquo;re using markdown, and not html or (gasp) some one-off syntax.</p>
<p><em>(If you&rsquo;re not familiar with markdown, find a good tutorial and get familiar with it. It&rsquo;s used on GitHub as well as a lot of forums, commenting systems, blogs, etc. But I digress&hellip;)</em></p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-dotnet-try/image-9.png"
    width="1049"
      height="326"></figure>
<p>Open the QuickStart.md file in VSCode or some other editor, change the markdown a bit, and refresh the page in your browser. You should see the changes. So they&rsquo;re parsing the markdown into HTML&hellip; neat but not earth shattering.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-dotnet-try/image-10.png"
    width="1855"
      height="812"></figure>
<p>The cool bit is what they call a &ldquo;code fence&rdquo;. You specify a source file to read from, and give it the name of region in the file, and it parses whatever&rsquo;s inside the region, displays it on the page, and makes it executable.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">```csharp --source-file ./Snippets/Program.cs --project ./Snippets/Snippets.csproj --region run1
```</code></pre></div>
<p>So how&rsquo;s that work? As far as I can tell, displaying and running your code snippet are two different steps.</p>
<p>When you refresh the page, it reads the source file, extracts whatever region you specify, and tosses that on the page, even if it&rsquo;s invalid. You do get some red squigglies if something&rsquo;s wrong though.</p>
<p>When you <em>run</em> the sample code, it compiles the project and actually runs it - hence the need for the switch statement in the Main method. If it fails to compile, you get syntax errors in the browser.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-dotnet-try/image-17.png"
    width="1702"
      height="962"></figure>
<p>Fix the code (in my case, an invalid method name and missing <code>using</code> directive), and press Run again. Good to go! Oddly, I realized you can&rsquo;t just insert <code>using</code> directives directly into the web page. I mean, sure that&rsquo;d be invalid in your c# app so it makes sense, but I guess that makes the point that showing just one part of your code like this does make it confusing as to what you can and cannot do in the browser.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-dotnet-try/image-18.png"
    width="1667"
      height="964"></figure>

<h2 class="relative group">Other Samples
    <div id="other-samples" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#other-samples" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>They&rsquo;ve got <a href="https://github.com/dotnet/try-samples"  target="_blank" rel="noreferrer">some other samples</a> available in a different repo, so be sure to check those out too if you&rsquo;re curious. I stumbled on this project looking for some good LINQ tutorials, so let&rsquo;s try that one out.</p>
<p>Clone the above repo somewhere on disk, change to the directory with 101 LINQ samples, and run the <code>dotnet try</code> command. That should open the samples, easy peasy.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-dotnet-try/image-19.png"
    width="907"
      height="363"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-dotnet-try/image-20.png"
    width="1072"
      height="872"></figure>
<p>There&rsquo;s not a ton in there, but there are some samples of new additions to the C# 7 and 8 specs, so might check those out later&hellip;</p>
<p>I think when I&rsquo;ve got time, I&rsquo;ll write separate posts trying to make this work with a WinForms project, as well as a .NET Core project, perhaps with an API. <a href="https://docs.microsoft.com/en-us/aspnet/core/tutorials/web-api-help-pages-using-swagger?view=aspnetcore-5.0"  target="_blank" rel="noreferrer">Swagger</a> is a nice tool for exposing the top layers of an API, but this might be a good way to document tricky or useful methods, or even expose them for use to a dev team without having to open the project separately. Hmm&hellip; the wheels are turning.</p>

<h2 class="relative group">Caveats
    <div id="caveats" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#caveats" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The only thing that bugged me was the very limited options for specifying which code you want to display. It&rsquo;d be nice to just specify a method name, instead of having to clutter up your source code with region names to support your documentation.</p>
]]></content:encoded><media:content url="https://grantwinney.com/what-is-dotnet-try/feature.webp" medium="image" type="image/webp"/></item><item><title>Be ready to explain your code</title><link>https://grantwinney.com/be-ready-to-explain-your-code/</link><pubDate>Wed, 03 Feb 2021 13:28:48 +0000</pubDate><guid>https://grantwinney.com/be-ready-to-explain-your-code/</guid><description>Does the mere thought of explaining your code cause anxiety? Be confident! We should all understand what we&amp;rsquo;re writing and why. It&amp;rsquo;s an opportunity (for everyone) to learn!</description><content:encoded><![CDATA[<p>When we&rsquo;re programming, there&rsquo;s all kinds of ways to code defensively. We surround blocks of code with &ldquo;try / catch&rdquo; structures that prevent our apps from crashing. We log errors, to help track down problems later on. We include tests to help make sure next week&rsquo;s changes don&rsquo;t break last weeks&rsquo; code. We document our work so that other devs, business, end-users etc will find it when they (or you and me in 6 months!) need to figure out why something works the way it does.</p>
<p>All of these things defend our code against being subpar, riddled with bugs.</p>
<p>But there&rsquo;s another kind of defensive programming that benefits everyone - making sure you understand what you&rsquo;re writing and why you&rsquo;re writing it that way. That doesn&rsquo;t mean you can&rsquo;t lift a snippet from someone else.. there&rsquo;s no need to reinvent the wheel. But whether you invent your own wheel or use someone else&rsquo;s, anyone driving your car down the highway afterwards reasonably assumes you knew how the wheel works.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/be-ready-to-explain-your-code/simply-explained.webp"
    width="368"
      height="456"></figure>
<p>Imagine someone on your team asking you, genuinely and in the spirit of learning, why&rsquo;d you write that piece of code that way.. can you explain it to me? What do you tell them?</p>
<p>I&rsquo;ve had the benefit several times recently (yes, it&rsquo;s a benefit for both of us) to explain my code to a developer with less experience. Nothing makes me realize gaps in my knowledge, or where things could be written better, than explaining my code to someone else. I&rsquo;m forced to double-check my assumptions, look up docs to verify things, maybe even realize there was a better way.</p>
<p>Doing regular <a href="https://grantwinney.com/what-is-a-code-review/"  target="_blank" rel="noreferrer">code reviews</a> is a good way to do this, but that&rsquo;s usually an asynchronous process.. sitting with someone and talking it out is even better. If you ever have the opportunity to explain your code to someone else, don&rsquo;t get defensive, but do defend it. You might both learn something!</p>
]]></content:encoded><media:content url="https://grantwinney.com/be-ready-to-explain-your-code/feature.webp" medium="image" type="image/webp"/></item><item><title>Scratch that itch for coding!</title><link>https://grantwinney.com/scratch-that-itch-for-coding/</link><pubDate>Tue, 19 Jan 2021 04:28:00 +0000</pubDate><guid>https://grantwinney.com/scratch-that-itch-for-coding/</guid><description>Learning a little about programming could benefit anyone. If you want a fun intro to coding and logical thinking, check out Scratch!</description><content:encoded><![CDATA[<p>In the early 90&rsquo;s, when I first got the coding itch, there weren&rsquo;t many options for a curious kid. We had a family computer running Win 3.1 with QBasic on it. It&rsquo;s easy to get started with (hence the name), but not very easy to get excited about. The web of the early 90&rsquo;s cost a premium and was mostly mailing lists, so not much collaboration to be had for a kid either. It didn&rsquo;t matter though, little of what I learned was personally appealing - I was more interested in video games and music than input prompts and.. more input prompts. 🥱 💤</p>
<p>In fact, coding sat on the backburner until about 5 years later, when I put together a Legend of Zelda fan site on geocities. As it turns out, having a reason to learn and something you feel worth sharing are huge motivators! I learned enough about HTML and JavaScript to get my ideas out there. Years later, as an all growned-up professional programmer who&rsquo;s done his fair share of a-little-bit-of-everything, I&rsquo;ve come to realize a few things:</p>
<ul>
<li><em>Knowing how to program is awesome.</em>
The computer is a tool, but it&rsquo;s also a medium for bringing ideas to life. If they&rsquo;re someone else&rsquo;s ideas, or your ideas for solving other people&rsquo;s problem, you can even get paid for it. Like a kid in a candy shop.</li>
<li><em>Learning how to program, even a little, is an advantage.</em>
Not everyone&rsquo;s an author, statistician, or bus driver&hellip; but writing, basic math, and driving are essentials. There&rsquo;s no need for everyone to program professionally either, but knowing a little makes things easier.</li>
<li><em>Solving for a specific problem is the best way to stay motivated.</em><br>
The times in life when we learn the most is when we have a clear goal, or idea, or problem that&rsquo;s personally relevant - and let nothing stand in our way of achieving, sharing, or solving it.</li>
</ul>

<h2 class="relative group">An Idea
    <div id="an-idea" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#an-idea" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Around the same time I was hacking out my little site on geocities, Mitchell Resnick and the <a href="https://www.media.mit.edu/groups/lifelong-kindergarten/overview/"  target="_blank" rel="noreferrer">Lifelong Kindergarten</a> group at MIT were dreaming up ways to make programming more accessible to the masses, eventually creating Lego <a href="https://www.lego.com/en-us/themes/mindstorms"  target="_blank" rel="noreferrer">MindStorms</a>. He realized the same thing most of us eventually do - having a &ldquo;why&rdquo; is just as (maybe more?) important than a &ldquo;what&rdquo;. If you&rsquo;re not trying to build or fix anything, a bag of tools is kinda meaningless.</p>
<p>In the early 2000&rsquo;s, Mitchell, John Maeda of MIT, and Yasmin Kafai of UCLA <a href="https://web.media.mit.edu/~mres/papers/scratch-proposal.pdf"  target="_blank" rel="noreferrer">had another idea</a> - something to integrate programming into art and media, the way MindStorms integrated it into legos. Backed by Intel and the NSF, they spent a few years creating Scratch, focusing on accessibility (available on every platform and device), collaboration (share creations and ideas easily), and a building block design reminscent of legos. The end goal was to introduce programming to the masses.</p>
<blockquote><p>Indeed, our primary goal is not to prepare people for careers as professional programmers but to nurture a new generation of creative, systematic thinkers comfortable using programming to express their ideas.</p>
</blockquote>
<h2 class="relative group">The Design
    <div id="the-design" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-design" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Despite that, the design and implementation in Scratch teaches some concepts most casual users wouldn&rsquo;t even be aware of, aside from the obvious stuff like loops and <code>if</code> statements.</p>
<p>You can create your own &ldquo;blocks&rdquo; that accept parameters and contain other blocks, just like methods and parameters. You can run multiple blocks concurrently, so it introduces users to threading. And like <a href="https://grantwinney.com/creating-music-with-sonic-pi-on-raspberry-pi/"  target="_blank" rel="noreferrer">Sonic Pi</a>, it supports &ldquo;live coding&rdquo;. Users can modify their code on the fly, without needing to wait for compilation.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/scratch-that-itch-for-coding/scratch-ui.gif"
    width="1303"
      height="800"></figure>
<p>As for the tech stack, it was originally written in Squeak (yeah, never heard of it either), probably due to it&rsquo;s cross-platform portability, then ported to Flash and AIR (<a href="https://www.adobe.com/products/flashplayer/end-of-life.html"  target="_blank" rel="noreferrer">dead</a> and <a href="https://blog.adobe.com/en/2019/05/30/the-future-of-adobe-air.html#gs.qyix8o"  target="_blank" rel="noreferrer">dying</a>), and finally to <a href="https://bocoup.com/blog/porting-scratch-from-flash-to-javascript-performance-interoperability-and-extensions"  target="_blank" rel="noreferrer">HTML 5 and JavaScript</a>&hellip; which means a much greater compatibility with various systems. If you want to check out the code, <a href="https://github.com/LLK"  target="_blank" rel="noreferrer">it&rsquo;s available on GitHub</a>.</p>
<p>Or if you just want to get started using it, read more <a href="https://scratch.mit.edu/about"  target="_blank" rel="noreferrer">here</a>, <a href="https://scratch.mit.edu/explore/projects/tutorials/"  target="_blank" rel="noreferrer">find a tutorial</a>, or <a href="https://scratch.mit.edu/projects/editor/?tutorial=getStarted"  target="_blank" rel="noreferrer">jump right in</a> (there&rsquo;s an <a href="https://scratch.mit.edu/download/"  target="_blank" rel="noreferrer">offline version</a> too). If you want to learn more, check out these articles and papers. They&rsquo;re all a little older, but they go indepth into what the goals were and how they were going to achieve them. I found them interesting.. but then that probably says something about me. 😏</p>
<ul>
<li><a href="https://web.archive.org/web/20090521031855/http://llk.media.mit.edu/projects/scratch/ScratchSneakPreview.pdf"  target="_blank" rel="noreferrer">Scratch: A Sneak Preview</a></li>
<li><a href="https://web.archive.org/web/20070307105748/http://weblogs.media.mit.edu/llk/scratch/archives/CreativeCoding-PepperKafai.pdf"  target="_blank" rel="noreferrer">Creative Coding: Programming for Personal Expression</a></li>
<li><a href="https://news.mit.edu/2007/resnick-scratch"  target="_blank" rel="noreferrer">Creating from Scratch | MIT News</a></li>
<li><a href="https://web.media.mit.edu/~mres/papers/Scratch-CACM-final.pdf"  target="_blank" rel="noreferrer">Scratch: Programming for All</a></li>
<li><a href="https://web.media.mit.edu/~jmaloney/papers/ScratchLangAndEnvironment.pdf"  target="_blank" rel="noreferrer">The Scratch Programming Language and Environment</a></li>
</ul>
<p>As far as I&rsquo;m concerned, the amazing thing is not that they created a platform for learning how to program. There are plenty of sites for that. What&rsquo;s impressive is that they made it so appealing, by showing people that it&rsquo;s not necessarily a thing in and of itself, but about bringing ideas to life, and sharing them, and collaborating and learning and thinking logically.</p>
<p>It can be a medium of expression, like writing or singing or painting or woodworking. The world doesn&rsquo;t need everyone to be a professional developer, but the computer&rsquo;s a tool worth knowing - not just how to consume it, but how to bend and shape it too.</p>
]]></content:encoded><media:content url="https://grantwinney.com/scratch-that-itch-for-coding/feature.webp" medium="image" type="image/webp"/></item><item><title>What is mocking a dependency?</title><link>https://grantwinney.com/what-is-mocking-a-dependency/</link><pubDate>Wed, 09 Dec 2020 13:19:00 +0000</pubDate><guid>https://grantwinney.com/what-is-mocking-a-dependency/</guid><description>When you&amp;rsquo;re writing tests, you generally don&amp;rsquo;t want to write to the database, email customers, and hit third-party API&amp;rsquo;s. That&amp;rsquo;s why we need to know how to mock dependencies!</description><content:encoded><![CDATA[<p>Ever taken your car for an e-check and had it placed on those rollers for testing? It&rsquo;s part of a gadget called a dynamometer, and it&rsquo;s used to test your car at high speeds without having to move it an inch.</p>
<p>Imagine.. you have a car with a problem. It makes a horrible noise and shakes when you hit 60 mph. Being an intrepid individual, you decide to diagnose it yourself. Being a <em>smart</em> individual, you decide not to do it while careening down the highway. You need a way to make the car seem to be going really fast without it actually moving. Hm, those rollers might help!</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-mocking-a-dependency/image.png"
    width="1221"
      height="619"></figure>
<p>That&rsquo;s not a perfect analogy, but in essence that&rsquo;s mocking a dependency.</p>
<ol>
<li>You identify a part of the system to test - something you <em>can</em> control.</li>
<li>You identify other parts of the system that it touches that you <em>can&rsquo;t</em> control.</li>
<li>You replace those parts, in such a way that the system you&rsquo;re testing never knows.</li>
</ol>
<p>You don&rsquo;t want your car moving while you diagnose it. And you don&rsquo;t want your app writing records to a database, kicking up prompts for input, or connecting to third-party APIs, while you&rsquo;re testing it. You don’t want your test failing because a network drive is unavailable, a location on disk can’t be written to, or an SMTP server is down.</p>

<h2 class="relative group">Let&rsquo;s get practical
    <div id="lets-get-practical" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#lets-get-practical" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I don&rsquo;t know about you, but examples always help me&hellip; so let&rsquo;s take a closer look at a couple.</p>

<h3 class="relative group">Don&rsquo;t write to disk
    <div id="dont-write-to-disk" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#dont-write-to-disk" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>When you&rsquo;re writing a large app, one of your best friends is the logger. He&rsquo;s not an exciting friend but he&rsquo;s a great listener. He&rsquo;s taking notes of everything you do and say&hellip; and can throw them back in your face at a moment&rsquo;s notice. &hellip;. &hellip;&hellip;. 🤨</p>
<p>Anyyyyway&hellip; in the .NET world there&rsquo;s a popular logging library called NLog. Let&rsquo;s check out a short example that validates a username and logs some debug info to a file. I removed the configuration part for the logger, but <a href="https://github.com/grantwinney/BlogCodeSamples/tree/master/Languages/CSharp/MockingDependencies"  target="_blank" rel="noreferrer">you can see it all here</a>.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">UsernameValidation</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">readonly</span> <span class="n">Logger</span> <span class="n">logger</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">UsernameValidation</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// ... configure nlog</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Create new instance of logger</span>
</span></span><span class="line"><span class="cl">        <span class="n">logger</span> <span class="p">=</span> <span class="n">LogManager</span><span class="p">.</span><span class="n">GetCurrentClassLogger</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">bool</span> <span class="n">IsUsernameAlphaOnly</span><span class="p">(</span><span class="kt">string</span> <span class="n">username</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">logger</span><span class="p">.</span><span class="n">Debug</span><span class="p">(</span><span class="s">$&#34;{nameof(IsUsernameAlphaOnly)}: Testing whether {username} is valid.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">isMatch</span> <span class="p">=</span> <span class="n">Regex</span><span class="p">.</span><span class="n">IsMatch</span><span class="p">(</span><span class="n">username</span><span class="p">,</span> <span class="s">&#34;^[A-Za-z]+$&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">logger</span><span class="p">.</span><span class="n">Debug</span><span class="p">(</span><span class="s">$&#34;{nameof(IsUsernameAlphaOnly)}: {username} is {(isMatch ? &#34;</span><span class="n">a</span> <span class="n">valid</span><span class="s">&#34; : &#34;</span><span class="n">an</span> <span class="n">invalid</span><span class="s">&#34;)} username.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">isMatch</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">logger</span><span class="p">.</span><span class="n">Error</span><span class="p">(</span><span class="n">ex</span><span class="p">,</span> <span class="s">$&#34;{nameof(IsUsernameAlphaOnly)}: Guess {username} wasn&#39;t valid. :/&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>If we run a test against that method, it&rsquo;ll run the logger code too, writing out a log file with debug statements in it. What if the logger can&rsquo;t write to the disk? We&rsquo;re inadvertently testing the NLog library too, and the test could randomly fail for a reason that&rsquo;s out of our control.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="na">[TestCase(&#34;Bob&#34;, Description = &#34;should be valid&#34;)]</span>
</span></span><span class="line"><span class="cl"><span class="na">[TestCase(&#34;JDoe1&#34;, Description = &#34;should be invalid&#34;)]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">Test1</span><span class="p">(</span><span class="kt">string</span> <span class="n">username</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">l</span> <span class="p">=</span> <span class="k">new</span> <span class="n">UsernameValidation</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">True</span><span class="p">(</span><span class="n">l</span><span class="p">.</span><span class="n">IsUsernameAlphaOnly</span><span class="p">(</span><span class="n">username</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p><em>Test the method with a couple different inputs&hellip;</em></p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">2020-12-09 12:31:27.2294|DEBUG|MockingDependencies.MockLogger.UsernameValidation|IsUsernameAlphaOnly: Testing whether Bob is valid.
2020-12-09 12:31:27.2583|DEBUG|MockingDependencies.MockLogger.UsernameValidation|IsUsernameAlphaOnly: Bob is a valid username.
2020-12-09 12:31:27.2767|DEBUG|MockingDependencies.MockLogger.UsernameValidation|IsUsernameAlphaOnly: Testing whether JDoe1 is valid.
2020-12-09 12:31:27.2767|DEBUG|MockingDependencies.MockLogger.UsernameValidation|IsUsernameAlphaOnly: JDoe1 is an invalid username.</code></pre></div>
<p><em>A log file is written to disk - probably not what we want!</em></p>
<p><strong>Mock out the dependency</strong></p>
<p>Different languages and frameworks have different ways of mocking out dependencies. In .NET, it usually means mocking out an interface. That&rsquo;s a whole separate topic, but the short version is that an interface is a contract, and you can change the terms of that contract depending on who&rsquo;s running the code, like your user in production or your test framework. The way your test <em>changes</em> those terms is via a mocking framework like <a href="https://github.com/Moq/moq4"  target="_blank" rel="noreferrer">moq</a>, <a href="https://www.telerik.com/products/mocking.aspx"  target="_blank" rel="noreferrer">JustMock</a>, <a href="http://www.typemock.com/"  target="_blank" rel="noreferrer">TypeMock</a>, <a href="https://hibernatingrhinos.com/oss/rhino-mocks"  target="_blank" rel="noreferrer">RhinoMocks</a>, etc&hellip; lots of options.</p>
<p>The NLog library happens to implement an interface called <code>ILogger</code>, and by adding a new constructor and changing a couple lines, we can pass that around our little method instead of the concrete Logger class.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">UsernameValidation_MockLogger</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">readonly</span> <span class="n">ILogger</span> <span class="n">logger</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">UsernameValidation_MockLogger</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// ... configure nlog</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Create new instance of logger</span>
</span></span><span class="line"><span class="cl">        <span class="n">logger</span> <span class="p">=</span> <span class="n">LogManager</span><span class="p">.</span><span class="n">GetCurrentClassLogger</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">UsernameValidation_MockLogger</span><span class="p">(</span><span class="n">ILogger</span> <span class="n">logger</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="n">logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">bool</span> <span class="n">IsUsernameAlphaOnly</span><span class="p">(</span><span class="kt">string</span> <span class="n">username</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">logger</span><span class="p">.</span><span class="n">Debug</span><span class="p">(</span><span class="s">$&#34;{nameof(IsUsernameAlphaOnly)}: Testing whether {username} is valid.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">isMatch</span> <span class="p">=</span> <span class="n">Regex</span><span class="p">.</span><span class="n">IsMatch</span><span class="p">(</span><span class="n">username</span><span class="p">,</span> <span class="s">&#34;^[A-Za-z]+$&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">logger</span><span class="p">.</span><span class="n">Debug</span><span class="p">(</span><span class="s">$&#34;{nameof(IsUsernameAlphaOnly)}: {username} is {(isMatch ? &#34;</span><span class="n">a</span> <span class="n">valid</span><span class="s">&#34; : &#34;</span><span class="n">an</span> <span class="n">invalid</span><span class="s">&#34;)} username.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">isMatch</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">logger</span><span class="p">.</span><span class="n">Error</span><span class="p">(</span><span class="n">ex</span><span class="p">,</span> <span class="s">$&#34;{nameof(IsUsernameAlphaOnly)}: Guess {username} wasn&#39;t valid. :/&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Then we modify the tests to create a &ldquo;fake&rdquo; (mock) logger, and use that in the method instead. Mocking frameworks are really powerful, but I&rsquo;m not touching on any of that here. This is enough to cause the log statements to &ldquo;succeed&rdquo; as far as the other class is concerned, even though it&rsquo;s actually doing nothing. No log file is written to disk!</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="na">[TestCase(&#34;Bob&#34;, Description = &#34;should be valid&#34;)]</span>
</span></span><span class="line"><span class="cl"><span class="na">[TestCase(&#34;JDoe1&#34;, Description = &#34;should be invalid&#34;)]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">Test1</span><span class="p">(</span><span class="kt">string</span> <span class="n">username</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">mock</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Mock</span><span class="p">&lt;</span><span class="n">ILogger</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">l</span> <span class="p">=</span> <span class="k">new</span> <span class="n">UsernameValidation_MockLogger</span><span class="p">(</span><span class="n">mock</span><span class="p">.</span><span class="n">Object</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">True</span><span class="p">(</span><span class="n">l</span><span class="p">.</span><span class="n">IsUsernameAlphaOnly</span><span class="p">(</span><span class="n">username</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h3 class="relative group">Don&rsquo;t read from disk
    <div id="dont-read-from-disk" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#dont-read-from-disk" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Something else that&rsquo;s pretty common in programming is reading from a file - maybe a configuration file or an ini (initialization) file. So here&rsquo;s another example, that reads an XML file from disk to find the price of a book <em>(</em><a href="https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ms762271%28v=vs.85%29"  target="_blank" rel="noreferrer"><em>thanks Microsoft</em></a><em>).</em></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Books</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">readonly</span> <span class="n">XDocument</span> <span class="n">xDoc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Books</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">xDoc</span> <span class="p">=</span> <span class="n">XDocument</span><span class="p">.</span><span class="n">Load</span><span class="p">(</span><span class="n">Path</span><span class="p">.</span><span class="n">Combine</span><span class="p">(</span><span class="n">Path</span><span class="p">.</span><span class="n">GetDirectoryName</span><span class="p">(</span><span class="n">Assembly</span><span class="p">.</span><span class="n">GetExecutingAssembly</span><span class="p">().</span><span class="n">Location</span><span class="p">),</span> <span class="s">&#34;MockXDocument&#34;</span><span class="p">,</span> <span class="s">&#34;books.xml&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">decimal?</span> <span class="n">GetPrice</span><span class="p">(</span><span class="kt">string</span> <span class="n">bookId</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">book</span> <span class="p">=</span> <span class="n">xDoc</span><span class="p">.</span><span class="n">Descendants</span><span class="p">(</span><span class="s">&#34;book&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                        <span class="p">.</span><span class="n">Where</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">Attribute</span><span class="p">(</span><span class="s">&#34;id&#34;</span><span class="p">).</span><span class="n">Value</span> <span class="p">==</span> <span class="n">bookId</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                        <span class="p">.</span><span class="n">SingleOrDefault</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kt">decimal</span><span class="p">.</span><span class="n">TryParse</span><span class="p">(</span><span class="n">book</span><span class="p">?.</span><span class="n">Element</span><span class="p">(</span><span class="s">&#34;price&#34;</span><span class="p">)?.</span><span class="n">Value</span><span class="p">,</span> <span class="k">out</span> <span class="kt">decimal</span> <span class="n">price</span><span class="p">)</span> <span class="p">?</span> <span class="n">price</span> <span class="p">:</span> <span class="p">(</span><span class="kt">decimal?</span><span class="p">)</span><span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>If we run it with the following test, it&rsquo;ll read the file from disk. That&rsquo;s risky when you&rsquo;re testing. What if the file isn&rsquo;t present? What if an antivirus scanner gobbles it up? What if it&rsquo;s on a network drive, and someone moves it? All that stuff is outside your control, and all you really wanted to do was make sure you can return the correct price.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Books_Tests</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Books</span> <span class="n">books</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [SetUp]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">Setup</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">books</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Books</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [TestCase(&#34;bk102&#34;, 5.95)]</span>
</span></span><span class="line"><span class="cl"><span class="na">    [TestCase(&#34;bk111&#34;, 36.95)]</span>
</span></span><span class="line"><span class="cl"><span class="na">    [TestCase(&#34;bk999&#34;, null, Description = &#34;not a book&#34;)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">Test1</span><span class="p">(</span><span class="kt">string</span> <span class="n">bookId</span><span class="p">,</span> <span class="kt">decimal?</span> <span class="n">bookPrice</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">AreEqual</span><span class="p">(</span><span class="n">bookPrice</span><span class="p">,</span> <span class="n">books</span><span class="p">.</span><span class="n">GetPrice</span><span class="p">(</span><span class="n">bookId</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p><strong>Mock out the dependency</strong></p>
<p>This time, the <code>XDocument</code> doesn&rsquo;t implement an interface that we can easily take advantage of, so we&rsquo;ll have to do something else instead. One option would be to wrap the class we want to mock in a new class, and create the interface for our new class to implement, like this:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">interface</span> <span class="nc">IXDocument</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">IXDocument</span> <span class="n">Load</span><span class="p">(</span><span class="kt">string</span> <span class="n">fileName</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">System</span><span class="p">.</span><span class="n">Xml</span><span class="p">.</span><span class="n">Linq</span><span class="p">.</span><span class="n">XElement</span><span class="p">&gt;</span> <span class="n">Descendants</span><span class="p">(</span><span class="n">System</span><span class="p">.</span><span class="n">Xml</span><span class="p">.</span><span class="n">Linq</span><span class="p">.</span><span class="n">XName</span> <span class="n">name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">XDocument</span> <span class="p">:</span> <span class="n">IXDocument</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="n">System</span><span class="p">.</span><span class="n">Xml</span><span class="p">.</span><span class="n">Linq</span><span class="p">.</span><span class="n">XDocument</span> <span class="n">XDoc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">IXDocument</span> <span class="n">Load</span><span class="p">(</span><span class="kt">string</span> <span class="n">uri</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">XDoc</span> <span class="p">=</span> <span class="n">System</span><span class="p">.</span><span class="n">Xml</span><span class="p">.</span><span class="n">Linq</span><span class="p">.</span><span class="n">XDocument</span><span class="p">.</span><span class="n">Load</span><span class="p">(</span><span class="n">uri</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">this</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="n">IXDocument</span> <span class="n">LoadEx</span><span class="p">(</span><span class="kt">string</span> <span class="n">fileName</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">new</span> <span class="n">XDocument</span><span class="p">().</span><span class="n">Load</span><span class="p">(</span><span class="n">fileName</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">System</span><span class="p">.</span><span class="n">Xml</span><span class="p">.</span><span class="n">Linq</span><span class="p">.</span><span class="n">XElement</span><span class="p">&gt;</span> <span class="n">Descendants</span><span class="p">(</span><span class="n">System</span><span class="p">.</span><span class="n">Xml</span><span class="p">.</span><span class="n">Linq</span><span class="p">.</span><span class="n">XName</span> <span class="n">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">XDoc</span><span class="p">.</span><span class="n">Descendants</span><span class="p">(</span><span class="n">name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>I won&rsquo;t go into too many details about the above, except to say that we create our own <code>XDocument</code> class that wraps the .NET class of the same name, and implements an interface that we can use with the moq mocking library. Just like the first example, all it takes is a new constructor that accepts the interface and a couple other extra lines.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">readonly</span> <span class="n">IXDocument</span> <span class="n">xDoc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="n">Books_MockXDocument</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">xDoc</span> <span class="p">=</span> <span class="n">XDocument</span><span class="p">.</span><span class="n">LoadEx</span><span class="p">(</span><span class="n">Path</span><span class="p">.</span><span class="n">Combine</span><span class="p">(</span><span class="n">Path</span><span class="p">.</span><span class="n">GetDirectoryName</span><span class="p">(</span><span class="n">Assembly</span><span class="p">.</span><span class="n">GetExecutingAssembly</span><span class="p">().</span><span class="n">Location</span><span class="p">),</span> <span class="s">&#34;MockXDocument&#34;</span><span class="p">,</span> <span class="s">&#34;books.xml&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="n">Books_MockXDocument</span><span class="p">(</span><span class="n">IXDocument</span> <span class="n">xDoc</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="n">xDoc</span> <span class="p">=</span> <span class="n">xDoc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kt">decimal?</span> <span class="n">GetPrice</span><span class="p">(</span><span class="kt">string</span> <span class="n">bookId</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">book</span> <span class="p">=</span> <span class="n">xDoc</span><span class="p">.</span><span class="n">Descendants</span><span class="p">(</span><span class="s">&#34;book&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                    <span class="p">.</span><span class="n">Where</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">Attribute</span><span class="p">(</span><span class="s">&#34;id&#34;</span><span class="p">).</span><span class="n">Value</span> <span class="p">==</span> <span class="n">bookId</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                    <span class="p">.</span><span class="n">SingleOrDefault</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kt">decimal</span><span class="p">.</span><span class="n">TryParse</span><span class="p">(</span><span class="n">book</span><span class="p">?.</span><span class="n">Element</span><span class="p">(</span><span class="s">&#34;price&#34;</span><span class="p">)?.</span><span class="n">Value</span><span class="p">,</span> <span class="k">out</span> <span class="kt">decimal</span> <span class="n">price</span><span class="p">)</span> <span class="p">?</span> <span class="n">price</span> <span class="p">:</span> <span class="p">(</span><span class="kt">decimal?</span><span class="p">)</span><span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Here&rsquo;s the test again, now feeding the exact XML to our app that we want. What could&rsquo;ve failed randomly before is now completely in our control!</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">Books_MockXDocument</span> <span class="n">books</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">Mock</span><span class="p">&lt;</span><span class="n">IXDocument</span><span class="p">&gt;</span> <span class="n">mockDoc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">[SetUp]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">Setup</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">mockDoc</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Mock</span><span class="p">&lt;</span><span class="n">IXDocument</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl">    <span class="n">books</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Books_MockXDocument</span><span class="p">(</span><span class="n">mockDoc</span><span class="p">.</span><span class="n">Object</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">[TestCase(&#34;bk102&#34;, 5.95)]</span>
</span></span><span class="line"><span class="cl"><span class="na">[TestCase(&#34;bk111&#34;, 36.95)]</span>
</span></span><span class="line"><span class="cl"><span class="na">[TestCase(&#34;bk999&#34;, null)]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">Test1</span><span class="p">(</span><span class="kt">string</span> <span class="n">bookId</span><span class="p">,</span> <span class="kt">decimal?</span> <span class="n">bookPrice</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">bookPrice</span><span class="p">.</span><span class="n">HasValue</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">testBook</span> <span class="p">=</span> <span class="s">$@&#34;&lt;book id=&#34;&#34;{bookId}&#34;&#34;&gt;&lt;price&gt;{bookPrice}&lt;/price&gt;&lt;/book&gt;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">mockDoc</span><span class="p">.</span><span class="n">Setup</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">Descendants</span><span class="p">(</span><span class="s">&#34;book&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">            <span class="p">.</span><span class="n">Returns</span><span class="p">(</span><span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">XElement</span><span class="p">&gt;</span> <span class="p">{</span> <span class="n">System</span><span class="p">.</span><span class="n">Xml</span><span class="p">.</span><span class="n">Linq</span><span class="p">.</span><span class="n">XDocument</span><span class="p">.</span><span class="n">Parse</span><span class="p">(</span><span class="n">testBook</span><span class="p">).</span><span class="n">Root</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="p">.</span><span class="n">AreEqual</span><span class="p">(</span><span class="n">bookPrice</span><span class="p">,</span> <span class="n">books</span><span class="p">.</span><span class="n">GetPrice</span><span class="p">(</span><span class="n">bookId</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h2 class="relative group">Be mindful of what you control.. and what you don&rsquo;t!
    <div id="be-mindful-of-what-you-control-and-what-you-dont" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#be-mindful-of-what-you-control-and-what-you-dont" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I could come up with more examples - there&rsquo;s lots out there to consider - but hopefully these ones drive the point home well enough. When it comes to testing:</p>
<ul>
<li>Don&rsquo;t rely on hardware, like physical drives, emails servers, etc.</li>
<li>Don&rsquo;t rely on software outside your control, like third-party APIs.</li>
<li>Tests should be repeatable, reliable, <em>and completely in your control</em>. When they fail, you should know exactly why, and it should happen consistently.</li>
<li>When you&rsquo;re dependant on something that&rsquo;s outside your control, look for a way to mock it out. You may have to get creative, but it&rsquo;ll almost certainly make your tests more reliable, which is a huge peace of mind!</li>
</ul>
]]></content:encoded><media:content url="https://grantwinney.com/what-is-mocking-a-dependency/feature.webp" medium="image" type="image/webp"/></item><item><title>What is a code review / pull request?</title><link>https://grantwinney.com/what-is-a-code-review/</link><pubDate>Sat, 28 Nov 2020 04:15:10 +0000</pubDate><guid>https://grantwinney.com/what-is-a-code-review/</guid><description>Does the idea of submitting to a code review make you sweat bullets? Or do you brush it off as a necessary evil? It should be a (hopefully positive) conversation, wherein the team agrees to the code they&amp;rsquo;re all going to have to help maintain, and maybe learns something new too.</description><content:encoded><![CDATA[<p>Has someone told you to submit your code for a review? Or to review someone else&rsquo;s code? You might be worrying about the criticism you&rsquo;ll receive, or that you won&rsquo;t have anything constructive to share. You might be concerned they&rsquo;ll poke holes in your code, or fear you&rsquo;re not good enough. Or maybe you feel it&rsquo;s all ridiculous - why should it matter anyway? If the code compiles, ship it! 🚢</p>
<p>The truth is that no one codes on an island <em>(thanks</em> <a href="https://allpoetry.com/No-man-is-an-island"  target="_blank" rel="noreferrer"><em>John Donne</em></a><em>),</em> and eventually you&rsquo;ll be asked to participate in a code review - for school, for work, for an open source project. And that&rsquo;s a good thing, even if it doesn&rsquo;t feel like it yet. Most of the code you&rsquo;ll ever write will be meant for more than just one person, and it&rsquo;s likely that hundreds or even <em>thousands</em> of people will eventually use it, support it, or (in the case of your fellow devs) help maintain and extend it.</p>
<p>It may not be an easy thing at first, opening yourself up to feedback on a regular basis, but it&rsquo;s a good way to catch bugs and prevent more problems down the road. It&rsquo;s also a good way to learn, if it&rsquo;s done well.</p>

<h2 class="relative group">What are the benefits?
    <div id="what-are-the-benefits" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-are-the-benefits" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>A code review is, at its best, a chance to get constructive feedback about the code you just wrote, an opportunity to learn something new, and to gain a better picture about the system you&rsquo;re working on. Or if you&rsquo;re giving one, it&rsquo;s an opportunity to share what you know, and to teach someone else the same.</p>
<p>At the very least, it puts more sets of eyes (and a fresh perspective) on the thing you&rsquo;ve been heads-down on for days or even weeks. That kind of focus is often required, but it&rsquo;s easy to lose sight of the forest for the trees too. That is, it&rsquo;s easy to miss how your piece fits into the larger system, when you&rsquo;re focused on the immediate requirements you&rsquo;re being asked to code.</p>
<p>Imagine, though, someone on an assembly line putting together the car you&rsquo;re going to drive everyday. How about an engineer adding 10 new floors to the skyscraper where you&rsquo;ll be working&hellip; or a mechanic repairing the engine in the jet you&rsquo;ll take on vacation. No matter how certified those people are, how well vetted, would you ever turn down an extra set of (experienced) eyes double-checking their work? No way!</p>
<p>Sure, most of us won&rsquo;t be working on mission critical systems that mean life and death - but that doesn&rsquo;t mean we couldn&rsquo;t benefit from a second opinion.</p>

<h2 class="relative group">What should you expect?
    <div id="what-should-you-expect" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-should-you-expect" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Every place is different, so your experience will be too. If you&rsquo;re in school, it might just mean zipping up your code and uploading it so a couple other students can provide some feedback. But most code reviews take place as the final step before merging new code (which usually lives on its own git branch) back into master (where it needs to play nicely with everyone else&rsquo;s code, the build environment, etc) - a process known as a <a href="https://dzone.com/articles/learning-git-what-is-a-pull-request"  target="_blank" rel="noreferrer">pull request</a>.</p>
<p>Some teams may have a loose requirement on their code review / pull request process, simply giving other developers a chance to approve it if they&rsquo;d like. Other teams have a stricter requirement, which actually prevents merging (completing the pull request) before at least x number of devs review and approve it. Some are okay leaving code reviews open for days, while others prefer they be addressed within a few hours.</p>
<p>Whatever the differences, a code review should be a conversation. The kind of conversation it is, though, will depend on your experience as a developer - and in the codebase you&rsquo;re working in. If you&rsquo;re new to programming or the language being used, expect suggestions on how to improve your syntax. If you&rsquo;re new to the codebase, expect suggestions about how your code can &ldquo;fit in&rdquo; better, and warnings about pitfalls to avoid. You should also expect to explain your choices, because a code review is also an opportunity for the rest of your team to get familiar with the new code - and you may just teach them something too!</p>
<p>It&rsquo;ll also depend on <em>who&rsquo;s</em> doing the review. If the person reviewing your code suggests a change to fit some set of team standards, and that person is a team lead or manager who helped <em>set</em> those standards, well&hellip; you should still feel comfortable asking for clarification, but don&rsquo;t expect challenging the standards at that moment to bear much fruit!</p>
<p>Ideally, there should be a back and forth, while you explain why you made a particular decision, and the reviewer does too, until a consensus is made. The point is to make sure the best code possible is being merged back to master, and that once it is, everyone&rsquo;s comfortable maintaining it.</p>

<h2 class="relative group">How do you create one?
    <div id="how-do-you-create-one" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#how-do-you-create-one" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Like everything, it depends. The most popular platform is currently GitHub, but there are others too, like Azure DevOps and Atlassian Bitbucket (pictured below). No matter the system your team uses, you should always try to answer the various what&rsquo;s, why&rsquo;s, and how&rsquo;s. When someone steps in to review your code, they shouldn&rsquo;t have to guess why it exists or what it&rsquo;s purpose is.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-a-code-review/github-pr.png"
    width="1009"
      height="393"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-a-code-review/github-new-pr.png"
    width="780"
      height="608"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-a-code-review/bitbucket-pr.png"
    width="521"
      height="379"></figure>
<p>GitHub, Azure DevOps, and Bitbucket</p>
<p><strong>What</strong> requirements does this code fulfill? What bug does it fix? Most teams try to break down the work into small units - aka stories, cards, or issues. If the work you did completes one of those units, link to it, so reviewers have more context.</p>
<p><strong>How</strong> does your code fulfill the requirements? If you fixed a bug, the &ldquo;card&rdquo; you were working from probably only stated the problem, so you could explain what you discovered, and how your code fixes it. Include screen captures of the code running, if it seems that&rsquo;d help&hellip; a picture is worth a thousand words after all. Do before and after shots, using all the shapes and arrows in MS Paint if that&rsquo;s your thing. 😉</p>
<p><strong>Why</strong> did you do things a certain way? If your code avoids some pitfall no one was aware of, it might help to spell it out. If a piece of code nags at you, but it works and you couldn&rsquo;t find a &ldquo;better&rdquo; way while you were writing it, ask for suggestions. If you refactored some method or removed some dead code, mention it. Get eyes on it; spur the conversations you want to have.</p>

<h2 class="relative group">How should you respond?
    <div id="how-should-you-respond" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#how-should-you-respond" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The very first thing you should do, especially if the feedback seems overly critical or negative, is take a step back and a deep breath. When too many people weigh in on anything, even constructive feedback can seem overwhelming - it feels like you&rsquo;re being piled on. You <em>are</em> the focus, in as much as you wrote the code being reviewed, but a code review shouldn&rsquo;t be a negative thing.</p>
<p>Once feedback is given, address it. Don&rsquo;t feel pressured to blindly implement every suggestion made, nor to argue every point and defend every line of code you wrote. Think about what&rsquo;s being suggested, and the why behind it. Ask for clarification. Think intentionally about why you made the choices you did, and why the reviewer&rsquo;s suggestion might or might not work. Were your choices deliberate, or just the first thing that happened to work?</p>

<h2 class="relative group">What if you&rsquo;re the reviewer?
    <div id="what-if-youre-the-reviewer" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-if-youre-the-reviewer" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Reviewing someone&rsquo;s code is a different ballgame. Think about who you&rsquo;re reviewing - what&rsquo;s their experience and their level of knowledge about the codebase?</p>
<p>The easiest temptation is just to skim the code changes and, deciding it looks like there&rsquo;s probably not any bugs, hit the &ldquo;approve&rdquo; button. After all, if there&rsquo;s <em>really</em> a problem, someone else will catch it right? Except someone else may be thinking the same thing about you!</p>
<p>Here&rsquo;s some things to consider:</p>
<ul>
<li>Why was the code written, and does it fulfill the original requirements? Check the original story / card / issue.</li>
<li>Do you notice outdated or missing comments or documentation, especially if that documentation is public facing like with an API? It should be clear, useful, and up-to-date.</li>
<li>Does the code compile and do the tests run? Is there an opportunity for more or better tests?</li>
<li>Does the code run like it&rsquo;s supposed to? Does it break anything else, especially something in the vicinity of the code being changed (like the same screen if it&rsquo;s a UI change)?</li>
<li>Do you see any potential bugs? Better to call them out and be wrong, than the customer finds it, goes through support, etc, etc, and it drops right back in your lap later on.</li>
<li>Do you recognize any code smells? Did someone copy/paste a block of code instead of keeping things <a href="https://code.tutsplus.com/tutorials/3-key-software-principles-you-must-understand--net-25161"  target="_blank" rel="noreferrer">DRY</a>? Did they write something in 10 lines, that you realize could&rsquo;ve been a single line and still just as readable?</li>
<li>Does it follow the team standards? Tabs vs spaces, 2 vs 4 spaces, pascal vs camel case, etc - ultimately they just don&rsquo;t matter. What <em>does</em> matter is consistency! If the team generally goes in one direction, or formally agrees on something, and there&rsquo;s a difference, call it out.</li>
<li>Be explicit about what needs addressing, why, and don&rsquo;t hesitate to include a suggested fix too. It makes your intention clearer, and the person you&rsquo;re reviewing is free to use it as-is or expand on it. Avoid things like, &ldquo;This could be written better&rdquo; or &ldquo;That&rsquo;s not how we do it&rdquo; without further explanation. If you can recognize a problem, you might as well help improve it too.</li>
<li>Ask questions about things you suspect might be a problem, or just things you don&rsquo;t fully understand. If you&rsquo;re reviewing the code of someone with more experience, you might even learn something from them during the code review!</li>
</ul>
<p>No matter which side of the fence you&rsquo;re on, don&rsquo;t hesitate to call out something good too - a useful refactoring or a sleek piece of code, some good tests or just a really helpful suggestion. Code reviews don&rsquo;t have to be negative - they should be an opportunity for a team to learn, teach, and grow, a little bit at a time!</p>
]]></content:encoded><media:content url="https://grantwinney.com/what-is-a-code-review/feature.webp" medium="image" type="image/webp"/></item><item><title>The transient nature of code</title><link>https://grantwinney.com/the-transient-nature-of-code/</link><pubDate>Sun, 11 Oct 2020 03:00:46 +0000</pubDate><guid>https://grantwinney.com/the-transient-nature-of-code/</guid><description>I just deleted my coworkers code. 😱 It was good code that wasn&amp;rsquo;t needed anymore, and he understood why. The nature of coding is that it&amp;rsquo;s a progression, and any individual code is transient by nature. Today&amp;rsquo;s code is subject to tomorrow&amp;rsquo;s refactoring.</description><content:encoded><![CDATA[<p>I just did one of the most painful things a dev can do to another dev. I deleted his code! I mean, it wasn&rsquo;t out of spite or anything, lol. It was good, solid code, covered by dozens of tests, which made it <em>that</em> much harder to pull the trigger. But it wasn&rsquo;t needed anymore.</p>
<p>Our team was sent new marching orders, and although at first I just commented it all out (in case our orders changed again), it was ultimately relegated to the annals of git log. And he totally got why and supported it.. even if he was relieved I was pulling the trigger instead of him. 😏</p>
<p>Programming is a funny thing - any individual piece of code is quite ephemeral. Sometimes, someone comes up with a better way of doing a thing and you have to swallow your pride. Other times, priorities change and you find yourself pivoting (as in this case). But most of the time&hellip; it&rsquo;s just the passage of time.</p>
<p>The code you write today, like a brush stroke on a large canvas, changes the overall picture slightly (probably not as much as most of us would like to believe), but other brush strokes will eventually cover it up. The tough thing to come to terms with as part of a team, is that any individual contribution is only part of a larger goal. With a certain maturity comes a (somewhat painful) acceptance that nothing is safe from being moved around, not even the code you wrote last week.</p>
<p>On the flip side, we should all fight the urge to delete code for the sake of &ldquo;doing it better&rdquo;. Wiping out someone&rsquo;s code shouldn&rsquo;t be the first thing you do, even though we&rsquo;ve all been there. It takes time to understand why someone did what they did, and the next dev is just as likely to look at <em>your</em> rewrite and think the same exact thing.</p>
<p>You, me, that next dev - we&rsquo;re all the little guy in the blue hat&hellip; <em>and</em> we wrote the code he&rsquo;s whining about! It&rsquo;s missing a final pane where he realizes the project, mystifying as it seemed at first, actually has a goal - catch a mouse - and there&rsquo;s no need to rewrite or delete it.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/the-transient-nature-of-code/abstrusegoose-432.png"
    width="744"
      height="612"></figure>
]]></content:encoded><media:content url="https://grantwinney.com/the-transient-nature-of-code/feature.webp" medium="image" type="image/webp"/></item><item><title>The right way to rethrow an exception in C#</title><link>https://grantwinney.com/rethrowing-an-exception-in-csharp/</link><pubDate>Thu, 17 Sep 2020 04:08:32 +0000</pubDate><guid>https://grantwinney.com/rethrowing-an-exception-in-csharp/</guid><description>All programming languages have gotchas to trip you up, and C# is no exception. Today, let&amp;rsquo;s check out the subtle (but significant) difference between &amp;ldquo;throw&amp;rdquo; and &amp;ldquo;throw ex&amp;rdquo;.</description><content:encoded><![CDATA[<p>All languages have gotchas, and C# is no different. A subtle one is the difference between <code>catch (Exception) throw;</code> and <code>catch (Exception ex) throw ex;</code>. On the surface, it seems like they&rsquo;ll do the same thing; in reality, the difference is really important if you care to know why your app is <em>really</em> crashing.</p>
<p>Check out the following code. Can you tell what the difference will be, if any, between the two <code>Console</code> statements?</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                    
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Program</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="k">void</span> <span class="n">Main</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;If we do a simple throw, the stacktrace should show the nitty-gritty details:\n&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">try</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">new</span> <span class="n">PreserveStack</span><span class="p">().</span><span class="n">Do</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">ex</span><span class="p">.</span><span class="n">StackTrace</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;\nIf we do an also-simple throw ex, the stacktrace is reset:\n&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">try</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">new</span> <span class="n">ResetStack</span><span class="p">().</span><span class="n">Do</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">ex</span><span class="p">.</span><span class="n">StackTrace</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">PreserveStack</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Do</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="k">new</span> <span class="n">ImportantClass</span><span class="p">().</span><span class="n">DoSomething</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">throw</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">ResetStack</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Do</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="k">new</span> <span class="n">ImportantClass</span><span class="p">().</span><span class="n">DoSomething</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">throw</span> <span class="n">ex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">ImportantClass</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">DoSomething</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">new</span> <span class="n">LessImportantButJustAsSpecial</span><span class="p">().</span><span class="n">DoSomething</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">LessImportantButJustAsSpecial</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">DoSomething</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">new</span> <span class="n">NowWereInTheWeeds</span><span class="p">().</span><span class="n">DoSomething</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">NowWereInTheWeeds</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">DoSomething</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">throw</span> <span class="k">new</span> <span class="n">ArgumentException</span><span class="p">(</span><span class="s">&#34;What does I did?? There are so many weeds down here!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The <code>PreserveStack.Do</code> function does a <code>throw</code>, which includes a stack trace all the way down to the <code>NowWereInTheWeeds</code> class that threw the exception, allowing a developer to quickly get to the root of the problem.</p>
<p>The <code>ResetStack.Do</code> function does a <code>throw ex</code>. The difference? The stack trace is reset, dropping everything further down the line, hiding the true origin of the exception. Minor difference in code, major difference in the end result!</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">If we do a simple throw, the stacktrace should show the nitty-gritty details:

   at NowWereInTheWeeds.DoSomething() in d:\Windows\Temp\5cydernb.0.cs:line 81
   at LessImportantButJustAsSpecial.DoSomething() in d:\Windows\Temp\5cydernb.0.cs:line 73
   at ImportantClass.DoSomething() in d:\Windows\Temp\5cydernb.0.cs:line 65
   at PreserveStack.Do() in d:\Windows\Temp\5cydernb.0.cs:line 41
   at Program.Main() in d:\Windows\Temp\5cydernb.0.cs:line 11

If we do an also-simple throw ex, the stacktrace is reset:

   at ResetStack.Do() in d:\Windows\Temp\5cydernb.0.cs:line 56
   at Program.Main() in d:\Windows\Temp\5cydernb.0.cs:line 22</code></pre></div>
<p>I ran this against .NET Core 3.1 and .NET 4.7.2 to see if it&rsquo;s still an issue after all these years, and it is&hellip; but that&rsquo;s no surprise. Microsoft takes great pains to maintain backwards compatibility, whether in the .NET Framework or <a href="https://www.youtube.com/watch?v=vPnehDhGa14"  target="_blank" rel="noreferrer">in Windows</a>. Since any number of devs might have intentionally used a thing, even if it appears to be subpar, <a href="https://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/"  target="_blank" rel="noreferrer">only very seldom do they make a breaking change</a>.</p>
<p>If you want to try it out yourself, <a href="https://dotnetfiddle.net/BNYEy2"  target="_blank" rel="noreferrer">fiddle with it here</a>.</p>

<h2 class="relative group">But.. why??
    <div id="but-why" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#but-why" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>So, this begs the question.. since the <code>throw ex</code> resets the stacktrace, when would that behavior be desirable? The only thing I can come up with is obfuscation of some sort. If you had a library of code, and someone else wrote an app that consumed it, maybe you&rsquo;d want to bury the true source of the exception (after logging it, hopefully?) from the calling app.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">ResetStack</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Do</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="k">new</span> <span class="n">ImportantClass</span><span class="p">().</span><span class="n">DoSomething</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// log the original exception and stack trace</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">throw</span> <span class="n">ex</span><span class="p">;</span> <span class="c1">// reset the stack trace</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>But that seems useless anyway. If they had the compiled code, they could decompile it to debug it. If they&rsquo;re hitting an API, you probably want to return a useful error instead of a raw exception in any case.</p>
<p>The official docs failed me here. The <a href="https://learn.microsoft.com/en-us/previous-versions/dotnet/netframework-4.0/ms229005%28v=vs.100%29"  target="_blank" rel="noreferrer">legacy docs</a> simply warn against it:</p>
<blockquote><p>[P]refer using an empty throw when catching and re-throwing an exception. This is the best way to preserve the exception call stack. The following code example demonstrates catching an exception and incorrectly specifying it when re-throwing the exception. This causes the stack trace to point to the re-throw as the error location&hellip;.</p>
</blockquote><p>While <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/throw#re-throwing-an-exception"  target="_blank" rel="noreferrer">newer docs</a> include it as an option, with the resulting behavior called out, but no opinion on whether or not it might be a bad thing. (I think it generally is.)</p>
<blockquote><p>You can also use the <code>throw e</code> syntax in a <code>catch</code> block to instantiate a new exception that you pass on to the caller. In this case, the stack trace of the original exception, which is available from the <a href="https://docs.microsoft.com/en-us/dotnet/api/system.exception.stacktrace#System_Exception_StackTrace"  target="_blank" rel="noreferrer">StackTrace</a> property, is not preserved.</p>
</blockquote>]]></content:encoded><media:content url="https://grantwinney.com/rethrowing-an-exception-in-csharp/feature.webp" medium="image" type="image/webp"/></item><item><title>How will I know when I'm a programmer?</title><link>https://grantwinney.com/how-do-you-know-when-youre-a-programmer/</link><pubDate>Tue, 15 Sep 2020 03:52:08 +0000</pubDate><guid>https://grantwinney.com/how-do-you-know-when-youre-a-programmer/</guid><description>How do you know when you&amp;rsquo;ve finally arrived, and are officially a programmer? Is it a set of skills, a certain amount of time? Can you ever really arrive, when it&amp;rsquo;s a race of one with no finish line?</description><content:encoded><![CDATA[<p>In the early 2000s, I worked for a small business where part of my job involved writing some software our clients used for uploading data to us. Still in college, I bought the academic version of .NET 2003 to get familiar with programming at home. I remember the excitement I felt opening that large honking package, with a thick book and 6 CDs, when it finally arrived in the mail. I was on my way to becoming a programmer!</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-do-you-know-when-youre-a-programmer/vs-2003-box.png"
    width="1024"
      height="768"></figure>
<p>A few years later, on a help desk at a much larger company, I&rsquo;d occasionally wander into other areas of the campus, as we called it, to check out where the developers worked. There were whiteboards all over, with drawings and flowcharts. I didn&rsquo;t know what Agile and kanban were at the time, but I figured if I could decipher what it all meant, well&hellip; <em>then</em> I&rsquo;d be able to become a programmer.</p>
<p>I spent a year applying internally to beginner level programming positions, but it wasn&rsquo;t panning out. The lack of progress was disheartening and I began looking elsewhere. It felt as if &ldquo;programming&rdquo; were an X on a map that I couldn&rsquo;t find.</p>
<p>Eventually I found a position to break into the field, and after 6 months it hit me - I&rsquo;d arrived! It said &ldquo;developer&rdquo; on my nameplate, and I was writing code and compiling things. I was <em>finally</em> a programmer.</p>
<p>In reality, I was on a team of 3 devs, maintaining a legacy app, with little direction and little clue how to do my job well. The team grew, I met people who knew far more than I did, and suddenly I didn&rsquo;t feel so impressive. Several years went by, and I found myself wondering if I were really a decent programmer at all. Notice a pattern?</p>
<p>I&rsquo;ve gone back and forth over the years, following the fine tradition of alternating between self-doubt and self-congratulation, every few days sometimes. If you&rsquo;re in the same boat, asking yourself how you know when you&rsquo;re a programmer, then read on. This is as much for me as you, lol.</p>

<h2 class="relative group">You&rsquo;ll improve
    <div id="youll-improve" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#youll-improve" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>You&rsquo;ll learn new things, you&rsquo;ll learn from your mistakes, you&rsquo;ll learn from others. You&rsquo;ll get better, and a year from now you&rsquo;ll be a better programmer than you were a year ago. Maybe you&rsquo;ll get lucky and save the day! Maybe you&rsquo;ll get a PR in right before the weekend that gets approved with no corrections, and it&rsquo;ll feel <em>good.</em></p>
<p>To be a programmer is to improve, better today than you were yesterday.</p>
<p><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-do-you-know-when-youre-a-programmer/perl-to-the-rescue.png"
    width="600"
      height="607"></figure>

<a href="https://xkcd.com/208/"  target="_blank" rel="noreferrer">xkcd: Regular Expressions</a></p>

<h2 class="relative group">You&rsquo;ll grow
    <div id="youll-grow" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#youll-grow" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>In reality, telling a computer what to do (aka programming) is too general to perfect in a lifetime. Technologies evolve, techniques evolve, and the codebase you&rsquo;re working in evolves (moreso on a large team).</p>
<p>Devs will come and go, each with their own best practices and favorite tools, more than you could learn in 10 lifetimes. Some will be awesome and blow you away with their level of knowledge and willingness to share it. Some will be less-than-awesome but may still blow you away with their level of knowledge and tireless preaching on how &ldquo;real programmers&rdquo; do it. 🙄 Still others will&hellip; just make you feel good about where you&rsquo;re at. 😏</p>
<p>To be a programmer is to continually grow, better tomorrow than you are today.</p>
<p><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-do-you-know-when-youre-a-programmer/real-programmers.png"
    width="740"
      height="406"></figure>

<a href="https://xkcd.com/378/"  target="_blank" rel="noreferrer">xkcd: Real Programmers</a></p>

<h2 class="relative group">You&rsquo;re more
    <div id="youre-more" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#youre-more" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Whether you&rsquo;re banging out open source tools for free, or you find a company willing to pay you for what you know, you&rsquo;re a programmer. If you stick with it, you&rsquo;ve already arrived&hellip; and yet, you&rsquo;ll never arrive at any final destination. It&rsquo;s a marathon, not a sprint. And it&rsquo;s a marathon without any definitive finish line until you decide to draw one by retiring or switching career paths. 😀</p>
<p>13 years (and some minor crises of identity) later, I&rsquo;m learning self-control over the urge to redo things that don&rsquo;t need redoing, and the benefits of many things (tools and soft skills) that I used to think were a waste of time.</p>
<p>I&rsquo;m a good programmer. Better than I was a decade ago.</p>
<p>I&rsquo;m not as good as I could be. I&rsquo;ll learn more in the next decade.</p>
<p>No matter what direction you go with it, remember that you&rsquo;re far more than someone churning out some code. There are people (not too many, I hope) who seem to have their identity and self-worth wrapped up in their ability to hack on software and hardware. Life is too short and programming, though pretty cool, is not all there is.</p>
]]></content:encoded><media:content url="https://grantwinney.com/how-do-you-know-when-youre-a-programmer/feature.webp" medium="image" type="image/webp"/></item><item><title>A 30th anniversary Zelda tribute, in Node.js</title><link>https://grantwinney.com/30th-anniversary-zelda-tribute/</link><pubDate>Wed, 09 Sep 2020 16:28:52 +0000</pubDate><guid>https://grantwinney.com/30th-anniversary-zelda-tribute/</guid><description>A few years ago, on the 30th anniversary of the Legend of Zelda, Scott Lininger and Mike Magee open sourced a 3D version of the original LoZ. The site was taken down, but the code&amp;rsquo;s still available to run!</description><content:encoded><![CDATA[<p>Nearly 30 years ago, I remember being at one family thing or another, when my cousin brought out his new SNES game console and hooked it up to a little color TV. He popped in a game that I immediately fell in love with (hey I was like 12), although I had no idea who or what Zelda was.</p>
<p>Some little green-clad forest dude gets a message in a dream, wakes up to find his uncle slipping off in the middle of a stormy night (note to self, grab a lantern before climbing down a well), and within 30 seconds the player is off exploring. Right from the start, it had me, and I&rsquo;ll bet I&rsquo;ve played it through 20 times over the years.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/Dream.jpg"
    width="1251"
      height="1017"></figure>
<p>Before Link to the Past though, there was the original Legend of Zelda for the NES. I never got into that one quite as much, but I definitely played it through a few times. I haven&rsquo;t played either of them in a long time though, which is why I geeked out on a project I stumbled onto the other day - <a href="https://github.com/scottlininger/zelda30tribute"  target="_blank" rel="noreferrer">an opensource 3D version of the original Legend of Zelda</a> that runs right in your browser!</p>
<p>A few years ago, on the 30th anniversary of the LoZ, Scott Lininger and Mike Magee released it on a website that, well.. got <a href="https://www.facebook.com/zelda30tribute/posts/485743838275370"  target="_blank" rel="noreferrer">smacked down by Nintendo</a> pretty quickly. The author said he had no hard feelings - clearly he expected it, since Nintendo is known for <a href="https://www.polygon.com/2016/9/2/12770344/nintendo-slaps-metroid-2-remake-and-500-plus-fangames-with-takedown-orders/"  target="_blank" rel="noreferrer">aggressively protecting its IP</a>.</p>
<p>But, although they issue DMCA requests to take sites down all the time, they don&rsquo;t seem to pursue the hosted source code. Or didn&rsquo;t in this case&hellip; I don&rsquo;t know how all that legal nonsense works. It&rsquo;s good for you and me though, because we can still try out the fan-made 30th anniversary homage to Zelda. It&rsquo;s not even close to being finished, and hasn&rsquo;t been touched in years, but the potential was pretty epic.</p>

<h2 class="relative group">Play it local
    <div id="play-it-local" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#play-it-local" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>To run it on your own PC, just <a href="https://nodejs.org/en/download/"  target="_blank" rel="noreferrer">install NodeJS</a> (I got a warning from Windows Defender to allow it, the first time I fired up the game) and execute the following script from wherever you want to download and play the game. It clones the repo, removes one line that causes an error, and starts it up.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1">#/bin/bash</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">git clone <span class="s2">&#34;https://github.com/scottlininger/zelda30tribute&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> <span class="s2">&#34;zelda30tribute&#34;</span>
</span></span><span class="line"><span class="cl">sed -i <span class="s2">&#34;s/sys\.puts//&#34;</span> <span class="s2">&#34;./nodeserver.js&#34;</span>
</span></span><span class="line"><span class="cl">start <span class="s2">&#34;http://localhost:9378/www/index.html&#34;</span>
</span></span><span class="line"><span class="cl">node nodeserver.js</span></span></code></pre></div></div>
<p>You should get some output like this:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">$ ./runzeldarun.sh
Cloning into &#39;zelda30tribute&#39;...
remote: Enumerating objects: 846, done.
remote: Total 846 (delta 0), reused 0 (delta 0), pack-reused 846
Receiving objects: 100% (846/846), 73.36 MiB | 13.05 MiB/s, done.
Resolving deltas: 100% (259/259), done.
Updating files: 100% (802/802), done.
(node:2268) [DEP0025] DeprecationWarning: sys is deprecated. Use util instead.</code></pre></div>
<p>And then&hellip; &lt;insert the sounds of angelic choirs here&gt;</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/image.png"
    width="956"
      height="555"></figure>

<h2 class="relative group">Play it remote
    <div id="play-it-remote" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#play-it-remote" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you don&rsquo;t want to install NodeJS locally, you can host it remotely on DigitalOcean. Word of warning though - don&rsquo;t post the IP address somewhere public or assign it a domain name. Then people will find it, or you&rsquo;ll be tempted to share it, and Nintendo will send Ganon after you.</p>
<p>If you don&rsquo;t already have an account, <a href="https://m.do.co/c/448f25462030"  target="_blank" rel="noreferrer">sign up</a> for one. They provide one-click servers (droplets), and there&rsquo;s a <a href="https://do.co/2PQEqgd"  target="_blank" rel="noreferrer">NodeJS droplet</a> that&rsquo;ll work perfectly. Just click the blue &ldquo;Create NodeJS Droplet&rdquo; button and choose the most minimal server config available. After a few minutes, you should be able to open the IP address in your browser and see the sample NodeJS app running.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/image-1.png"
    width="1463"
      height="725"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/2020-09-05-23_05_43-Create-Droplets---DigitalOcean---Brave.png"
    width="1212"
      height="832"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/2020-09-05-23_19_39-Your-NodeJS-Droplet---Brave.png"
    width="851"
      height="842"></figure>
<p>Once it&rsquo;s up and running, use your favorite terminal tool like <a href="https://git-scm.com/downloads"  target="_blank" rel="noreferrer">Git Bash</a> to connect to your new VM. Type in <code>ssh root@your-ip</code> to connect, and then type in the &ldquo;root&rdquo; password you set during the setup process. If you get a prompt asking if you&rsquo;re sure you want to continue, I&rsquo;d suggest typing yes or the rest of this is going to be really boring.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/image-2.png"
    width="979"
      height="228"></figure>
<p>You can run the same file as for the local version, with two changes - opening the port in UFW and <em>not</em> opening the site. And don&rsquo;t forget to <code>chmod +x your-file.sh</code> so you can execute the script.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1">#/bin/bash</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">git clone <span class="s2">&#34;https://github.com/scottlininger/zelda30tribute&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> <span class="s2">&#34;zelda30tribute&#34;</span>
</span></span><span class="line"><span class="cl">sed -i <span class="s2">&#34;s/sys\.puts//&#34;</span> <span class="s2">&#34;./nodeserver.js&#34;</span>
</span></span><span class="line"><span class="cl">ufw allow <span class="m">9378</span>
</span></span><span class="line"><span class="cl">node nodeserver.js</span></span></code></pre></div></div>
<p>Go to http://161.35.118.133:9378/www/index.html (with <em>your</em> IP address obviously) and try it out!</p>

<h2 class="relative group">Ah, ah, ah, ah, stayin&rsquo; alive
    <div id="ah-ah-ah-ah-stayin-alive" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#ah-ah-ah-ah-stayin-alive" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>For all the game&rsquo;s awesomitude (real word), it&rsquo;s got a few bugs and glitches. Hit boxes on enemies are a little off, and it seems to be far easier for them to hit you. I suggest going into <code>www/js/game.js</code> and making a little tweak around line 73 to increase your hit points to 5000. That&rsquo;s a few more hearts than you could find on the NES, but seems like a fair handicap.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">  /*
</span></span><span class="line"><span class="cl">   * The avatar.
</span></span><span class="line"><span class="cl">   */
</span></span><span class="line"><span class="cl">  this.avatar <span class="o">=</span> new ace.Avatar<span class="o">(</span>this<span class="o">)</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  this.avatar.hitPoints <span class="o">=</span> 5000<span class="p">;</span></span></span></code></pre></div></div>
<p>If you want to have a little more fun, modify the state a little further down. You can add most of the original items to your inventory, but unfortunately nothing really works besides the sword, bombs and raft. The whistle works, if all you&rsquo;re looking to do is make a whistle noise.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// The current &#34;save game&#34; state, with everything important we&#39;ve done.
</span></span></span><span class="line"><span class="cl"><span class="k">this</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="p">{</span><span class="nx">inventory</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                 <span class="nx">bow</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                 <span class="nx">itemwoodensword</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                 <span class="nx">raft</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                 <span class="nx">whistle</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                 <span class="nx">bombs</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                 <span class="nx">candle</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                 <span class="nx">boomerang</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                 <span class="nx">potion</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                 <span class="nx">ladder</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                 <span class="nx">ring</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="p">},</span>
</span></span><span class="line"><span class="cl">               <span class="nx">coins</span><span class="o">:</span> <span class="mi">500</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="nx">bombs</span><span class="o">:</span> <span class="mi">5000</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="nx">keys</span><span class="o">:</span> <span class="mi">500</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="nx">canvasScaleX</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="nx">canvasScaleY</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="nx">hasCompassByDungeon</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">               <span class="nx">hasMapByDungeon</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">               <span class="nx">hasVisitedRoomByDungeon</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">               <span class="nx">maxHitPoints</span><span class="o">:</span> <span class="mi">3</span>
</span></span><span class="line"><span class="cl">             <span class="p">};</span></span></span></code></pre></div></div>
<p>Armed with invincibility, have a look around. They put in a ton of work into getting the basic layout of overworld in place and navigable. Most of the overworld enemies are present, though they only drop rupees.. ironic, since all items in the few working caves are free.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/2020-09-06-01_59_42-Zelda-30-Year-Tribute---Brave.png"
    width="1588"
      height="923"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/2020-09-07-12_19_11-Zelda-30-Year-Tribute---Brave.png"
    width="1420"
      height="741"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/2020-09-07-12_19_35-Zelda-30-Year-Tribute---Brave.png"
    width="1328"
      height="737"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/2020-09-07-12_25_31-Zelda-30-Year-Tribute---Brave.png"
    width="1525"
      height="763"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/2020-09-07-12_25_59-Zelda-30-Year-Tribute---Brave.png"
    width="1483"
      height="729"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/2020-09-07-12_44_59-Zelda-30-Year-Tribute---Brave.jpg"
    width="1558"
      height="745"></figure>
<p>You can even go through the first two dungeons and beat Aquamentus and Dodongo, although certain enemies and areas aren&rsquo;t really complete.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/2020-09-07-12_36_45-Zelda-30-Year-Tribute---Brave.jpg"
    width="1728"
      height="877"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/2020-09-07-12_45_30-Zelda-30-Year-Tribute---Brave.jpg"
    width="1748"
      height="889"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/2020-09-07-12_46_33--1.jpg"
    width="1658"
      height="781"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/2020-09-09-08_57_59-Zelda-30-Year-Tribute---Brave.jpg"
    width="1604"
      height="868"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/2020-09-08-00_57_09--1.jpg"
    width="1820"
      height="893"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/2020-09-08-00_59_23-Zelda-30-Year-Tribute---Brave.jpg"
    width="1206"
      height="810"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/2020-09-09-01_03_35-Zelda-30-Year-Tribute---Brave.jpg"
    width="1454"
      height="566"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/2020-09-09-09_01_23-Zelda-30-Year-Tribute---Brave.jpg"
    width="1440"
      height="877"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/2020-09-08-00_58_33-Zelda-30-Year-Tribute---Brave.jpg"
    width="1472"
      height="706"></figure>
<p>Things fall apart a little in the second dungeon, with &ldquo;flat&rdquo; enemies painted onto the floor in place, rooms that are pitch black, dead ends with &ldquo;demo&rdquo; messages, etc.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/2020-09-07-12_26_39-Zelda-30-Year-Tribute---Brave.jpg"
    width="1556"
      height="661"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/2020-09-07-12_33_53-.jpg"
    width="1671"
      height="780"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/2020-09-07-12_33_07-Zelda-30-Year-Tribute---Brave.png"
    width="1211"
      height="579"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/2020-09-07-12_26_21-Zelda-30-Year-Tribute---Brave.jpg"
    width="1477"
      height="711"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/2020-09-06-02_33_15-Zelda-30-Year-Tribute.jpg"
    width="1594"
      height="925"></figure>
<p>All in all, I <em>so</em> wish this had been worked on and was more complete. The concept is one that any classic Legend of Zelda fan could appreciate, and they obviously put a ton of work into getting it as far as they did. I guess once Nintendo came knocking, most of the fun was gone, it&rsquo;s open sourced so maybe someone (you?) will pick it up and finish it!</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/30th-anniversary-zelda-tribute/2020-09-09-09_02_34-.jpg"
    width="1911"
      height="779"></figure>
]]></content:encoded><media:content url="https://grantwinney.com/30th-anniversary-zelda-tribute/feature.webp" medium="image" type="image/webp"/></item><item><title>How do I learn to program?</title><link>https://grantwinney.com/dev101/</link><pubDate>Sat, 04 Jul 2020 17:43:45 +0000</pubDate><guid>https://grantwinney.com/dev101/</guid><description>Saying somone can&amp;rsquo;t learn to tell a computer what to do and how to do it, or learn any other skill for that matter, is selling them (or yourself) short. It&amp;rsquo;s about the right tools, a desire to learn, and setting aside the time to make it happen.</description><content:encoded><![CDATA[<p>A short question, with a long answer. A decade in, I&rsquo;m still figuring out the answer.</p>
<p>Are you just curious and want to dabble? Do you want to switch careers? Is it about something (or someone) else? Maybe you&rsquo;ve been telling yourself for years that you&rsquo;re not a computer person, but saying you can&rsquo;t learn a skill is selling yourself short. Natural abilities affect the starting line and the pace, but marathons are won with fortitude, time, and patience&hellip;&hellip; and learning to program well is a marathon.</p>

<h2 class="relative group">What is programming?
    <div id="what-is-programming" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-is-programming" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>It&rsquo;s tough to define programming, maybe because it&rsquo;s so vague to begin with.</p>
<ul>
<li>For some, it&rsquo;s a means to an end, like automating a tedious task.</li>
<li>For others, it&rsquo;s a creative pursuit - a game, or expression of art.</li>
<li>For still others, it&rsquo;s a paycheck. It might be all three, or something else.</li>
</ul>
<p>If I had to summarize it in one sentence, I think I&rsquo;d say that:</p>
<p>Programming is writing instructions for a computer, to achieve some end goal beyond what the computer is already capable of.</p>
<p>It&rsquo;s a means to an end, a solution to a problem, an answer to a question. Not every problem or question, but concrete ones. You might find yourself writing a script in Excel to validate data, or an app to manage inventory or track finances. You could help train pilots or work on cybersecurity for the government.</p>

<h2 class="relative group">Where do I begin?
    <div id="where-do-i-begin" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#where-do-i-begin" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>That&rsquo;s the million dollar question isn&rsquo;t it? When I first wanted to be a programmer, I started looking through some random c++ source code I found, but that was completely unmotivating. In hindsight, I think my problem was that I had no reason to learn it, beyond wanting to learn it.</p>
<p>If you set out for a nice afternoon stroll, you&rsquo;ll get somewhere, but who knows exactly where.. or when. Set your sites on a specific destination, and you&rsquo;ll find a way to get there, even if it means clawing and scrabbling over rocky terrain.</p>
<p>What&rsquo;s the best way to learn? Sorry to disappoint, but there isn&rsquo;t one! There are plenty of sites and books and programs with their own magic formulas - take it all with a grain of salt. Take what you read here with a grain of salt. No two people have the exact same talents and skills, background and experience, drive and patience, so everyone&rsquo;s bridge looks a little different.</p>

<h2 class="relative group">Start with &ldquo;why&rdquo;
    <div id="start-with-why" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#start-with-why" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Come up with a decent why, and the what and how will follow naturally. If you just want to take up writing code as a career, the why might be obvious - to financially support yourself. In that case, you might need a why for the why, like financing a deck or something.</p>
<p>Otherwise, the &ldquo;why&rdquo; is the problem you&rsquo;re trying to solve, and the person or people you&rsquo;re solving it before. You don&rsquo;t need a reason to program, but I do my best work when I know exactly what I&rsquo;m trying to achieve and why I&rsquo;m doing it.</p>

<h2 class="relative group">Learn basic syntax
    <div id="learn-basic-syntax" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#learn-basic-syntax" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>When my kids were little, they picked up English by being completely immersed in it. It&rsquo;s amazing. It&rsquo;s also pretty inefficient. If every new skill we learned meant being dumped into it without a guide, roadmap, or clue, it&rsquo;d take forever. After kids pick up a few basics on their own, they need a more formal teaching.</p>
<p>Learn the basic syntax, in any language of your choice. If it&rsquo;s something you&rsquo;re interested in using for a project or pursuing for a career, it&rsquo;d make sense to use that. Classes and modules, functions and methods, loops and exception handling, primitive types, etc, etc - these are the building blocks, and they&rsquo;re basically the same everywhere.</p>
<p>There are just too many programming languages to learn the detailed syntax about everything, but they all have similarities. One language might call it select, another map, and yet another list comprehension, but all you really need to know is that most languages give you a way to transform the items in one list to create a second list. When you need it, you&rsquo;ll know to look for it.</p>
<p>I joined a C# shop earlier this year, but almost immediately found myself doing React development. A Pluralsight course and a few tutorials over the course of a week, and I was off. There&rsquo;s been constant learning since then, but the basics are the basics.</p>

<h2 class="relative group">Learn basic tools
    <div id="learn-basic-tools" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#learn-basic-tools" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There&rsquo;s a basic set of tools every developer needs, and then some more specialized stuff depending on what kind of work you&rsquo;re doing.</p>

<h3 class="relative group">Source code editors
    <div id="source-code-editors" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#source-code-editors" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Find an editor that supports a wide array of programming languages, ideally with support for extensions that extend its functionality. VS Code is my favorite right now, but other popular ones include Sublime, Atom, and Notepad++.</p>
<p>Pick one, try it on, feel it out. Some are more powerful than others, but the added complexity can make them more difficult to learn.</p>

<h3 class="relative group">IDEs
    <div id="ides" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#ides" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>An integrated development environment, or IDE, is usually more powerful than a code editor, but only for a specific language or framework. If you&rsquo;re interested in working in a single language, taking the time to find and learn to use an IDE will pay off huge dividends.</p>
<ul>
<li>C# has Visual Studio</li>
<li>Ruby has RubyMine</li>
<li>Python has PyCharm</li>
<li>Java has Eclipse and NetBeans</li>
</ul>

<h3 class="relative group">Git / GitHub
    <div id="git--github" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#git--github" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Once you start programming and get something working, you&rsquo;ll want to save your progress before making changes. If you mess something up, you&rsquo;ll want a way to go back. The best way to do that used to be copying the source code to another folder. If you do that now, the code gods will curse you and your children so.. don&rsquo;t do that.</p>
<p>Most projects you work on - your own, open source, with teams of people - will end up in source control. The defacto version control standard is git, and the defacto product for using git is GitHub. Get familiar with it.</p>

<h3 class="relative group">Testing
    <div id="testing" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#testing" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>If you write a piece of code and you know it works, prove it! Writing tests is a skill that, for some reason, quite a few developers still don&rsquo;t use. Sometimes it&rsquo;s a limitation of the system - old codebases are difficult to wield in a way that supports testing. Sometimes it&rsquo;s a limitation of time - when I&rsquo;m under the gun on a tight deadline, testing might take a backseat to shippable code.</p>
<p>Familiarize yourself with different types of testing - unit testing, acceptance testing, integration testing, etc. The more types of automated tests you write for a given piece of code, the more sure you can be that it won&rsquo;t unknowingly break on you.</p>

<h2 class="relative group">Write some code
    <div id="write-some-code" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#write-some-code" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>You can&rsquo;t learn about syntax and how to use tools without writing a little code, but eventually you&rsquo;re gonna be ready for something.. bigger. But what? Well, what drove you to learn programming in the first place?</p>
<p>If you don&rsquo;t have a project in mind, you could look for an online challenge or jump into an open source project. Writing a browser plugin might be more your style.</p>

<h2 class="relative group">Be patient
    <div id="be-patient" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#be-patient" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Writing code is easy. Learning to write code is not. Understanding the simple example above means learning about markup language, event handlers, and some basic JavaScript. There may be times you&rsquo;ll wonder whether you really know anything, or at least whether you know the right stuff.</p>
<p>It&rsquo;ll take much longer than 21 days to become an expert.</p>
<p>We&rsquo;re always in between what we do know and what we&rsquo;d like to know, and both sides are growing all the time. There&rsquo;s always going to be a new language, framework, or methodology to learn (especially with JavaScript!), but the fundamentals don&rsquo;t change much.</p>
<p>Take a step back once in awhile to think about what you&rsquo;ve accomplished, so you don&rsquo;t get overwhelmed with what you want to learn. Maybe you want to write about your accomplishments in a blog, a private journal, or just find someone to talk to.</p>

<h2 class="relative group">Break down the problem
    <div id="break-down-the-problem" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#break-down-the-problem" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I&rsquo;ve been writing code for awhile now, and I still get overwhelmed with projects that are out of my depth. I&rsquo;ve gotten better at catching myself staring at the screen with a vague sense of &ldquo;where the @#$% do I begin??&rdquo;, and realizing when I need to break down the problem.</p>
<p>It doesn&rsquo;t matter if you&rsquo;re working on a huge app with a team at a large company, or your own project in your spare time. Break down what you&rsquo;re trying to do into smaller steps, then rinse and repeat until you feel like you&rsquo;ve got something you can actually dig into and start attacking.</p>
<p>If you can&rsquo;t get there, well.. that tells you something too. Maybe you&rsquo;re not exactly sure what the problem is, or what the required solution should look like.</p>
<ul>
<li>Can you describe the problem in a single sentence, or a few?</li>
<li>Can you describe what a solution might look like in a few sentences? How would it fix the problem? How do you envision someone using it?</li>
</ul>
<p>Don&rsquo;t worry about the details at first - just think about where you&rsquo;d like to end up, then break the 50 mile journey into short jogs, then individual steps.</p>

<h2 class="relative group">Pick a direction and run
    <div id="pick-a-direction-and-run" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#pick-a-direction-and-run" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Doing isn&rsquo;t frustrating - sitting on the sideline, thinking about all the possibilities, and trying to figure out the place to start is. Sometimes it&rsquo;s better to do anything that achieves your goal, then keep finding better ways to do it.</p>
<p>You&rsquo;ll get better and better over time, but you&rsquo;re never really done!</p>
]]></content:encoded></item><item><title>Creating a basic Word Cloud in Python</title><link>https://grantwinney.com/how-to-make-a-word-cloud-in-python/</link><pubDate>Thu, 04 Jun 2020 21:43:00 +0000</pubDate><guid>https://grantwinney.com/how-to-make-a-word-cloud-in-python/</guid><description>We&amp;rsquo;ve all seen word clouds, like in the sidebars of blogs, but let&amp;rsquo;s see how we might create our own with a little bit of code!</description><content:encoded><![CDATA[<p>You&rsquo;ve most likely seen word clouds before, like in the sidebars of blogs. It&rsquo;s a fun, easy way to visualize which words in a group are more significant in some way. While you can <a href="https://worditout.com/"  target="_blank" rel="noreferrer">create your own</a> online, there&rsquo;s no reason you can&rsquo;t write your own in the language of your choice too.</p>
<p>Here&rsquo;s a quick example I threw together using Python and the built-in <a href="https://likegeeks.com/python-gui-examples-tkinter-tutorial/"  target="_blank" rel="noreferrer">Tkinter module</a> for drawing to the screen. In a very rough way, the smaller the percentage gets, the more faded I make the color, and the farther I move it from the center.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">random</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">tkinter</span> <span class="kn">import</span> <span class="o">*</span>     <span class="c1"># use Tkinter for Python2</span>
</span></span><span class="line"><span class="cl"><span class="n">window</span> <span class="o">=</span> <span class="n">Tk</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">window</span><span class="o">.</span><span class="n">title</span><span class="p">(</span><span class="s2">&#34;Welcome to LikeGeeks app&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">window</span><span class="o">.</span><span class="n">geometry</span><span class="p">(</span><span class="s1">&#39;800x480&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">mid_h</span> <span class="o">=</span> <span class="mi">320</span>
</span></span><span class="line"><span class="cl"><span class="n">mid_v</span> <span class="o">=</span> <span class="mi">240</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">results</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;Back-end Dev&#39;</span><span class="p">:</span> <span class="mf">55.2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;Full-stack Dev&#39;</span><span class="p">:</span> <span class="mf">54.9</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;Front-end Dev&#39;</span><span class="p">:</span> <span class="mf">37.1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;Desktop / Enterprise&#39;</span><span class="p">:</span> <span class="mf">23.9</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;Mobile Dev&#39;</span><span class="p">:</span> <span class="mf">19.2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;DevOps&#39;</span><span class="p">:</span> <span class="mf">12.1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;DB Admin&#39;</span><span class="p">:</span> <span class="mf">11.6</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;Designer&#39;</span><span class="p">:</span> <span class="mf">10.8</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;System Admin&#39;</span><span class="p">:</span> <span class="mf">10.6</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;Embedded Apps&#39;</span><span class="p">:</span> <span class="mf">9.6</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;Data / Business Analyst&#39;</span><span class="p">:</span> <span class="mf">8.2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;Data Scientist / Machine Learning&#39;</span><span class="p">:</span> <span class="mf">8.1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;QA / Tester&#39;</span><span class="p">:</span> <span class="mi">8</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;Data Engineer&#39;</span><span class="p">:</span> <span class="mf">7.6</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;Academic Researcher&#39;</span><span class="p">:</span> <span class="mf">7.2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;Educator&#39;</span><span class="p">:</span> <span class="mf">5.9</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;Gaming / Graphics&#39;</span><span class="p">:</span> <span class="mf">5.6</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;Engineering Manager&#39;</span><span class="p">:</span> <span class="mf">5.5</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;Product Manager&#39;</span><span class="p">:</span> <span class="mf">5.1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;Scientist&#39;</span><span class="p">:</span> <span class="mf">4.2</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">rnd_pos</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">1</span> <span class="k">if</span> <span class="n">random</span><span class="o">.</span><span class="n">random</span><span class="p">()</span> <span class="o">&lt;</span> <span class="mf">0.5</span> <span class="k">else</span> <span class="o">-</span><span class="mi">1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">dev_type</span><span class="p">,</span> <span class="n">percentage</span> <span class="ow">in</span> <span class="n">results</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">start_pos</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="mi">165</span><span class="o">-</span><span class="p">(</span><span class="n">percentage</span><span class="o">*</span><span class="mi">3</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="n">color</span> <span class="o">=</span> <span class="s1">&#39;#</span><span class="si">%02x%02x%02x</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="mi">255</span><span class="p">,</span> <span class="mi">50</span><span class="o">+</span><span class="n">start_pos</span><span class="p">,</span> <span class="mi">50</span><span class="o">+</span><span class="n">start_pos</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">lbl</span> <span class="o">=</span> <span class="n">Label</span><span class="p">(</span><span class="n">window</span><span class="p">,</span> <span class="n">text</span><span class="o">=</span><span class="n">dev_type</span><span class="p">,</span> <span class="n">font</span><span class="o">=</span><span class="p">(</span><span class="s2">&#34;Arial&#34;</span><span class="p">,</span> <span class="nb">int</span><span class="p">(</span><span class="n">percentage</span><span class="p">)),</span> <span class="n">fg</span><span class="o">=</span><span class="n">color</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">lbl</span><span class="o">.</span><span class="n">place</span><span class="p">(</span><span class="n">x</span><span class="o">=</span><span class="n">mid_h</span><span class="o">-</span><span class="p">(</span><span class="n">start_pos</span><span class="o">*</span><span class="n">rnd_pos</span><span class="p">()),</span> <span class="n">y</span><span class="o">=</span><span class="n">mid_v</span><span class="o">-</span><span class="p">(</span><span class="n">start_pos</span><span class="o">*</span><span class="n">rnd_pos</span><span class="p">()))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">window</span><span class="o">.</span><span class="n">mainloop</span><span class="p">()</span></span></span></code></pre></div></div>
<p>It ain&rsquo;t the prettiest, but I think it&rsquo;s passable for a non-pythonista in under an hour. In the right setting, it&rsquo;s a good way to steer someone&rsquo;s focus on whatever&rsquo;s most important first&hellip; however you happen to define that!</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="2020-06-04-13_20_37-Welcome-to-LikeGeeks-app.webp"
    ></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-make-a-word-cloud-in-python/2020-06-04-13_54_50-Welcome-to-LikeGeeks-app.png"
    width="1002"
      height="640"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-make-a-word-cloud-in-python/2020-06-04-13_29_16-Welcome-to-LikeGeeks-app.png"
    width="1002"
      height="640"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-make-a-word-cloud-in-python/2020-06-04-13_56_51-Welcome-to-LikeGeeks-app.png"
    width="1002"
      height="640"></figure>
]]></content:encoded><media:content url="https://grantwinney.com/how-to-make-a-word-cloud-in-python/feature.webp" medium="image" type="image/webp"/></item><item><title>Where should I store application data in Windows?</title><link>https://grantwinney.com/where-should-i-store-app-data-in-windows/</link><pubDate>Fri, 22 May 2020 03:59:32 +0000</pubDate><guid>https://grantwinney.com/where-should-i-store-app-data-in-windows/</guid><description>Windows sets certain locations aside for apps, and makes them easily discoverable for devs to use. Let&amp;rsquo;s see how.</description><content:encoded><![CDATA[<p>When it comes to writing software and deciding where to store the files an application needs to run, Windows makes it easy by setting certain areas of the system aside, and grouping them into two general categories.</p>
<ul>
<li>Files an app requires to run, or creates while running (i.e. logs), should go in program files, application data, etc. Keep them away from the user - they generally shouldn&rsquo;t know they&rsquo;re there.</li>
<li>Files a user creates with the app (i.e. a spreadsheet in Excel) should go in my documents, music, desktop, etc. Keep them near and dear to the user, so they can find them again, back them up, sync them using Dropbox, share them, whatever. They&rsquo;re interested in those for sure.</li>
</ul>
<p>These special folders aren&rsquo;t necessarily a specific location. They vary by version of Windows, and users can even change their location manually. But that doesn&rsquo;t matter, because Windows abstracts away the exact location to make life easier for developers, and you should take advantage of that.</p>

<h2 class="relative group">Why&rsquo;s it matter?
    <div id="whys-it-matter" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whys-it-matter" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Here&rsquo;s what my <em>My Documents</em> currently looks like - loads of stuff I shouldn&rsquo;t be aware most of the time. Amazon is storing encrypted ebook files that are useless outside their app, ShareX is storing log files and plugins, Microsoft some default templates, and Zoom.. an empty directory.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/where-should-i-store-app-data-in-windows/windows-explorer-1.png"
    width="1031"
      height="676"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/where-should-i-store-app-data-in-windows/windows-explorer-2.png"
    width="1271"
      height="395"></figure>
<p>I&rsquo;ve got this nice little area that&rsquo;s supposed to be just mine, but everyone decided to run everything through it. If I want to sync <em>My Documents</em> between several machines, I don&rsquo;t need ShareX&rsquo;s logs on both. I can install the Kindle app on multiple machines and it&rsquo;ll download books, so those encrypted files are unnecessary.</p>

<h2 class="relative group">What can we do?
    <div id="what-can-we-do" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-can-we-do" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Windows has had the concept of special folders for decades, going all the way back to Windows 95. The folders are visible in the registry editor, but don&rsquo;t rely on them there.. it&rsquo;s a <a href="https://devblogs.microsoft.com/oldnewthing/20031103-00/?p=41973"  target="_blank" rel="noreferrer">long and sad story</a> apparently. On a side note, let no one say Microsoft takes backwards compatibility lightly.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/where-should-i-store-app-data-in-windows/regedit.png"
    width="954"
      height="407"></figure>
<p>Special folders in the registry editor</p>
<p>Raymond Chen&rsquo;s article references the outdated Windows API calls for getting special folders, but the new hotness is <a href="https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath"  target="_blank" rel="noreferrer">SHGetKnownFolderPath</a> (although <a href="https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetfolderpatha"  target="_blank" rel="noreferrer">SHGetFolderPath</a> works as well, and simply calls the former).</p>

<h3 class="relative group">Example: C++
    <div id="example-c" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#example-c" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>If you&rsquo;re using c++, here&rsquo;s something I cobbled together that uses the <code>SHGetKnownFolderPath</code> function directly.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;windows.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;shlobj.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#pragma comment(lib, &#34;shell32.lib&#34;)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">PWSTR</span> <span class="n">path</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">HRESULT</span> <span class="n">result</span> <span class="o">=</span> <span class="n">SHGetKnownFolderPath</span><span class="p">(</span><span class="n">FOLDERID_Documents</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">path</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">result</span> <span class="o">!=</span> <span class="n">S_OK</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;Error: &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">result</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">        <span class="n">wprintf</span><span class="p">(</span><span class="sa">L</span><span class="s">&#34;%ls</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">path</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">cin</span><span class="p">.</span><span class="n">get</span><span class="p">();</span>  <span class="c1">// pause to view result
</span></span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// output: C:\Users\Grant\Documents
</span></span></span></code></pre></div></div>

<h3 class="relative group">Example: C#
    <div id="example-c-1" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#example-c-1" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The .NET Framework makes life easier, by <a href="https://referencesource.microsoft.com/#mscorlib/system/environment.cs,1445"  target="_blank" rel="noreferrer">wrapping the API call</a> for us.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">System</span>
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">static</span> <span class="n">System</span><span class="p">.</span><span class="n">Environment</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">namespace</span> <span class="nn">GetKnownFolder</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">class</span> <span class="nc">Program</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">static</span> <span class="k">void</span> <span class="n">Main</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">GetFolderPath</span><span class="p">(</span><span class="n">SpecialFolder</span><span class="p">.</span><span class="n">MyDocuments</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">            <span class="n">Console</span><span class="p">.</span><span class="n">ReadLine</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// output: C:\Users\Grant\Documents</span></span></span></code></pre></div></div>
<p>.NET Framework</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">hresult</span> <span class="p">=</span>
</span></span><span class="line"><span class="cl">  <span class="n">Win32Native</span><span class="p">.</span><span class="n">SHGetFolderPath</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">IntPtr</span><span class="p">.</span><span class="n">Zero</span><span class="p">,</span>                    <span class="cm">/* hwndOwner: [in] Reserved */</span>
</span></span><span class="line"><span class="cl">    <span class="p">((</span><span class="kt">int</span><span class="p">)</span><span class="n">folder</span> <span class="p">|</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">option</span><span class="p">),</span>    <span class="cm">/* nFolder:   [in] CSIDL    */</span>
</span></span><span class="line"><span class="cl">    <span class="n">IntPtr</span><span class="p">.</span><span class="n">Zero</span><span class="p">,</span>                    <span class="cm">/* hToken:    [in] access token */</span>
</span></span><span class="line"><span class="cl">    <span class="n">Win32Native</span><span class="p">.</span><span class="n">SHGFP_TYPE_CURRENT</span><span class="p">,</span> <span class="cm">/* dwFlags:   [in] retrieve current path */</span>
</span></span><span class="line"><span class="cl">    <span class="n">sb</span><span class="p">);</span>                            <span class="cm">/* pszPath:   [out]resultant path */</span></span></span></code></pre></div></div>
<p>Behind the scenes</p>

<h3 class="relative group">Example: Python
    <div id="example-python" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#example-python" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>In Python, you can <a href="https://pypi.org/project/pywin32/227/"  target="_blank" rel="noreferrer">install pywin32</a> (a wrapper for the Win32 API calls) and specify a <a href="https://docs.microsoft.com/en-us/windows/win32/shell/csidl"  target="_blank" rel="noreferrer">constant special item ID list</a> (CSIDL) value. <em>(I don&rsquo;t know Python that well, so this might only work for Python 2, and there might be newer constructs in Python 2 and 3.)</em></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">win32com.shell</span> <span class="kn">import</span> <span class="n">shell</span><span class="p">,</span> <span class="n">shellcon</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span> <span class="n">shell</span><span class="o">.</span><span class="n">SHGetFolderPath</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">shellcon</span><span class="o">.</span><span class="n">CSIDL_MYPICTURES</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span> <span class="n">shell</span><span class="o">.</span><span class="n">SHGetFolderPath</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">shellcon</span><span class="o">.</span><span class="n">CSIDL_PERSONAL</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Output:</span>
</span></span><span class="line"><span class="cl"><span class="c1"># C:\Users\Grant\Pictures</span>
</span></span><span class="line"><span class="cl"><span class="c1"># C:\Users\Grant\Documents</span></span></span></code></pre></div></div>
<p>The point is, when we&rsquo;re writing something for Windows that needs access to the file system, in <em>any</em> language, it&rsquo;s highly likely there&rsquo;s a way to access the special system folders.</p>
<p>We should strive to store anything our apps need in application data, or local data, or local application data, or really anything with &ldquo;application&rdquo; or &ldquo;program&rdquo; in the name. Because <em>My Documents</em> is mine all mine. 🙂</p>
]]></content:encoded><media:content url="https://grantwinney.com/where-should-i-store-app-data-in-windows/feature.webp" medium="image" type="image/webp"/></item><item><title>What is the Law of Demeter?</title><link>https://grantwinney.com/the-law-of-demeter-a-practical-example/</link><pubDate>Sat, 02 May 2020 03:59:22 +0000</pubDate><guid>https://grantwinney.com/the-law-of-demeter-a-practical-example/</guid><description/><content:encoded><![CDATA[<p>At work, we&rsquo;re running through <a href="https://amzn.to/2KNdr4i"  target="_blank" rel="noreferrer">The Pragmatic Programmer</a> - the original, not the 2nd edition published last year. If anyone is reading that, I&rsquo;d love to know if it really updates things for modern programming and whether it seems necessary. The original seems pretty timeless.</p>
<p>Yesterday was my turn to present, listing out highlights from chapter 5, sharing some thoughts, and hopefully spurring some conversation. The authors start by talking about the Law of Demeter, but they don&rsquo;t explain it very well, nor do they call it by its much more self-explanatory name, the Principle of Least Knowledge.</p>

<h2 class="relative group">The Principle of Least Knowledge
    <div id="the-principle-of-least-knowledge" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-principle-of-least-knowledge" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I like this name a lot more. I&rsquo;ve no idea who Demeter was, or why he was making laws. And while there seems to be a set of rules to follow, I think the spirit of the thing leaves it open to interpretation based on individual circumstances.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/the-law-of-demeter-a-practical-example/pirates.jpg"
    width="1200"
      height="628"></figure>
<p>Basically, it&rsquo;s about classes (modules, libraries, whatever) not exposing more about themselves than needed for other classes to use them. The term the authors use is writing &ldquo;shy&rdquo; code, in that a piece of code shouldn&rsquo;t interact more than it has to, and shouldn&rsquo;t let other bits of code see more than they have to.</p>
<p>I found a great example in an article written by <a href="https://www.linkedin.com/in/davidbock/"  target="_blank" rel="noreferrer">David Bock</a>, called <a href="https://www2.ccs.neu.edu/research/demeter/demeter-method/LawOfDemeter/paper-boy/demeter.pdf"  target="_blank" rel="noreferrer">The Paperboy, the Wallet, and the Law of Demeter</a>. He presents a fictional story, where a paperboy needs to collect money from one of his customers, and he represents the process in code with something kinda like this, which I think is something most of us have seen.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">paid</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Customer</span><span class="p">().</span><span class="n">GetWallet</span><span class="p">().</span><span class="n">GetPayment</span><span class="p">();</span></span></span></code></pre></div></div>
<p>The problem is that, in real life, would the paperboy really grab the customer&rsquo;s wallet and get money from it? Why doesn&rsquo;t the customer just hand the money over, which in code might mean the <code>Customer</code> class has a <code>GetPayment</code> method that hides the fact that internally there&rsquo;s a wallet at all. Later on, if the wallet is replaced with piggy bank, the paperboy doesn&rsquo;t know or care&hellip; he still gets paid!</p>

<h2 class="relative group">Celebrate good times, come on!
    <div id="celebrate-good-times-come-on" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#celebrate-good-times-come-on" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>All that stuff I said above was a generalization of David&rsquo;s, so I highly recommend reading his article if you want to learn more. Since you&rsquo;re here anyway, I&rsquo;ll throw my own example into the ring. Sometimes we need to see something from several slightly different angles before it clicks.</p>
<p>Imagine you&rsquo;re at a company, working on an application that all the employees use. It&rsquo;s got financial tools built in, and sales tools too; you can administer users and permissions, and run all manner of reports. This is far more common than you might realize, at least for companies with a few hundred employees.</p>
<p>A new request comes in - employees feel underappreciated, so management wants a report of anniversaries for the upcoming week, and new features that let them order a cake. And send an email. Maybe at the same time. Hey, they care but they&rsquo;ve got other stuff to do too.</p>
<p>The first thing you do is dig up the <code>Employee</code> class, because it&rsquo;s got to exist <em>somewhere</em> in the codebase&hellip; ah, there it is, with the usual fields attributed to an employee&hellip;</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Employee</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">FirstName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">LastName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">SSN</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">decimal</span> <span class="n">Salary</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">DateTime</span> <span class="n">HireDate</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">DateTime</span><span class="p">?</span> <span class="n">TerminationDate</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">bool</span> <span class="n">IsActive</span> <span class="p">{</span> <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="p">!</span><span class="n">TerminationDate</span><span class="p">.</span><span class="n">HasValue</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Now we need a <code>Celebration</code> class, to store logic for properly celeberating them employees&rsquo; anniversaries and whatnot.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Celebration</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Employee</span> <span class="n">Employee</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="kd">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">	
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Celebration</span><span class="p">(</span><span class="kt">int</span> <span class="n">empId</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// somehow we load the employee&#39;s data</span>
</span></span><span class="line"><span class="cl">        <span class="n">Employee</span> <span class="p">=</span> <span class="n">dbContext</span><span class="p">.</span><span class="n">Students</span><span class="p">.</span><span class="n">Get</span><span class="p">(</span><span class="n">empId</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">PurchaseCake</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// order a costco cake</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">PurchaseDeluxeCake</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// order a dairyqueen icecream cake</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">SendCard</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// fire off a hallmark card</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Since we&rsquo;ve made the instance of <code>Employee</code> public, it&rsquo;ll make things really easy for us to test certain conditions when it comes time to purchase those treats and send all that paper.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">celebrate</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Celebration</span><span class="p">(</span><span class="m">1234</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">celebrate</span><span class="p">.</span><span class="n">Employee</span><span class="p">.</span><span class="n">IsActive</span>
</span></span><span class="line"><span class="cl">	<span class="p">&amp;&amp;</span> <span class="n">celebrate</span><span class="p">.</span><span class="n">Employee</span><span class="p">.</span><span class="n">HireDate</span><span class="p">.</span><span class="n">Date</span> <span class="p">==</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span><span class="p">.</span><span class="n">Date</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="p">(</span><span class="n">celebrate</span><span class="p">.</span><span class="n">Employee</span><span class="p">.</span><span class="n">HireDate</span><span class="p">.</span><span class="n">Year</span> <span class="p">+</span> <span class="m">9</span> <span class="p">&lt;</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span><span class="p">.</span><span class="n">Year</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="n">celebrate</span><span class="p">.</span><span class="n">PurchaseDeluxeCake</span><span class="p">();</span>  <span class="c1">// 10 years gets you the big cake</span>
</span></span><span class="line"><span class="cl">	<span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">celebrate</span><span class="p">.</span><span class="n">Employee</span><span class="p">.</span><span class="n">HireDate</span><span class="p">.</span><span class="n">Year</span> <span class="p">&lt;</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span><span class="p">.</span><span class="n">Year</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="n">celebrate</span><span class="p">.</span><span class="n">PurchaseCake</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">	<span class="k">else</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="n">celebrate</span><span class="p">.</span><span class="n">SendCard</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>But wait a sec. Why does whatever piece of code that&rsquo;s checking for celebrations need to have access to the <code>Employee</code> class? Say this weren&rsquo;t code, and a manager needed to check an employee&rsquo;s record manually to view their hire date. Say someday all this gets offloaded to someone <em>other</em> than a manager, like the newly-formed party-planning committee. Is it reasonable that, just to send you a card and order your cake, a person would need access to your entire employee record including salary and social security number? Noooo. No it is not.</p>
<p>The Principle of Least Knowledge challenges us to rethink how much access Class A has, through Class B, to Classes C, D, and E. In other words, we should hide any details about the <code>Employee</code> class that don&rsquo;t need to be exposed - even hide the fact that there&rsquo;s an instance of <code>Employee</code> in there at all.</p>
<p>If we make the <code>Employee</code> class private inside <code>Celebration</code>, it forces us to refactor the rest of the class so that it never makes the rest of the employee&rsquo;s data available. The class instantiating <code>Celebration</code> could presumably access employee data anyway, but the programmer behind it would have to deliberately instantiate it.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Celebration</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="n">Employee</span> <span class="n">employee</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Celebration</span><span class="p">(</span><span class="kt">int</span> <span class="n">empId</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// somehow we load the employee&#39;s data</span>
</span></span><span class="line"><span class="cl">        <span class="n">employee</span> <span class="p">=</span> <span class="n">dbContext</span><span class="p">.</span><span class="n">Students</span><span class="p">.</span><span class="n">Get</span><span class="p">(</span><span class="n">empId</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">	
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">bool</span> <span class="n">IsEmployeeAnniversary</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">employee</span><span class="p">.</span><span class="n">IsActive</span>
</span></span><span class="line"><span class="cl">            <span class="p">&amp;&amp;</span> <span class="n">employee</span><span class="p">.</span><span class="n">HireDate</span><span class="p">.</span><span class="n">Date</span> <span class="p">&lt;</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span><span class="p">.</span><span class="n">Date</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">PurchaseCake</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">employee</span><span class="p">.</span><span class="n">HireDate</span><span class="p">.</span><span class="n">Year</span> <span class="p">+</span> <span class="m">9</span> <span class="p">&lt;</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span><span class="p">.</span><span class="n">Year</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// send for a dairyqueen cake</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// send for a costco cake</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">SendCard</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// fire off a hallmark card</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>This has loads of positive effects on the codebase, including:</p>
<ul>
<li>Simplifying the logic in the caller, which no longer has to figure out how to decide if it&rsquo;s the employee&rsquo;s anniversary, just call methods and let another class do the work.</li>
<li>If the same code as below is called anywhere else, then the above changes <a href="https://dzone.com/articles/is-your-code-dry-or-wet"  target="_blank" rel="noreferrer">DRY</a> up the code base too, keeping the logic in one place.</li>
<li>And if the logic inside the <code>Celebration</code> code changes - maybe some other criteria goes into determining an anniversary, or being at the company <em>20</em> years gets you a Cheesecake Factory cake - then anything else in the codebase that happens to run code like the code below won&rsquo;t need to be touched. Nice!</li>
</ul>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">celebrate</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Celebration</span><span class="p">(</span><span class="m">1234</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">celebrate</span><span class="p">.</span><span class="n">IsEmployeeAnniversary</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">celebrate</span><span class="p">.</span><span class="n">PurchaseCake</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">celebrate</span><span class="p">.</span><span class="n">SendCard</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Thanks Demeter, for your useful laws. I owe you a cake and a card.</p>
]]></content:encoded><media:content url="https://grantwinney.com/the-law-of-demeter-a-practical-example/feature.webp" medium="image" type="image/webp"/></item><item><title>13 addons to power up your GitHub game</title><link>https://grantwinney.com/13-addons-that-power-up-your-github-game/</link><pubDate>Sat, 04 Apr 2020 19:01:02 +0000</pubDate><guid>https://grantwinney.com/13-addons-that-power-up-your-github-game/</guid><description>GitHub is an amazing set of tools around Git, but it&amp;rsquo;s lacking in certain areas. Where it fails to impress, browser addons often pick up the slack. Here&amp;rsquo;s 13 addons (plus a few honorable mentions) that will take your GitHub experience to the next level!</description><content:encoded><![CDATA[<p>Git is the defacto VCS for most developers today. It happens to lend itself well to all kinds of not-strictly-code-related things too, like <a href="https://grantwinney.com/github-a-tool-for-collaborative-list-making/"  target="_blank" rel="noreferrer">collaborative list making</a> and <a href="https://reclaimthenet.org/china-github-coronavirus-censorship/"  target="_blank" rel="noreferrer">other surprising purposes</a>.</p>
<p>GitHub, in turn, is the defacto Git <em>platform,</em> tacking on a bunch of fancy tooling around Git. So your issues and PR&rsquo;s are right there, you get a wiki for documentation, a &ldquo;project&rdquo; board, a UI that lets people manage their repos without resorting to the command line, etc, etc.</p>
<p>The experience isn&rsquo;t always the best it could be, though. The UI tends to make poor use of available screen real estate, the <a href="https://grantwinney.com/5-things-you-can-do-with-a-locally-cloned-github-wiki/"  target="_blank" rel="noreferrer">wiki experience is subpar</a>, and the project area is (afaik) tied to a single repo which is pretty unrealistic in an enterprise setting.</p>
<p>Where GitHub fails to impress, browser addons (and the services they tie into) can pick up the slack, so here&rsquo;s 13 addons (plus a few honorable mentions) that will take your GitHub experience to the next level!</p>
<ul>
<li>If an addon hasn&rsquo;t been touched in years and doesn&rsquo;t seem to be actively maintained, I might exclude it.</li>
<li>I only included addons that solve one (or a few) problems, not general addons that address every issue under the sun.</li>
<li>These are listed in the Chrome store, but easily work for Brave, Opera and Edge too. If there&rsquo;s a Firefox version as well, I listed it.</li>
</ul>
<p>One more thing.. shockingly, after installing all of these addons to test them out, none of them seemed to get in each others way. Let&rsquo;s just take a moment to appreciate that fact, shall we? 😮</p>
<hr>

<h2 class="relative group">What is an Access Token? (READ FIRST)
    <div id="what-is-an-access-token-read-first" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-is-an-access-token-read-first" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Most of these addons use the <a href="https://developer.github.com/v3"  target="_blank" rel="noreferrer">GitHub API</a>, so what you experience when using them (and what you read in their docs) starts to look pretty similar. GitHub allows you to use their API without telling them who you are, but <a href="https://developer.github.com/v3/#rate-limiting"  target="_blank" rel="noreferrer">at a <em>severely</em> limited rate</a> and only for public resources. Like, on the order of 60 requests/hour vs <em>5000 requests/hour</em> for authenticated requests!</p>
<p>Needless to say, unauthenticated requests can run out pretty quickly if you&rsquo;re using GitHub a lot, and most of us are, so you want to create access tokens when possible. You could create a <em>single</em> access token with every permission possible, and plug it into each of the following addons when they ask for it, but then you may as well just give each of the addons your username and password. 🙄</p>
<p>What I&rsquo;d highly suggest is to <a href="https://github.com/settings/tokens/new"  target="_blank" rel="noreferrer">create a new, separate token</a> for each addon, with a note that indicates which addon it&rsquo;s for, and give it only the permissions the addon needs. Most of the addons include a link in their respective Options pages that includes just the permissions it needs. If you decide to remove an addon, <a href="https://github.com/settings/tokens"  target="_blank" rel="noreferrer">delete the access token</a> associated with it too!</p>
<p>Alright, enough of that! On to the main attraction&hellip;</p>
<hr>

<h2 class="relative group">Sourcegraph (web-based IDE)
    <div id="sourcegraph-web-based-ide" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#sourcegraph-web-based-ide" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://about.sourcegraph.com/"  target="_blank" rel="noreferrer">About</a> | <a href="https://sourcegraph.com/docs/integration/browser_extension"  target="_blank" rel="noreferrer">Docs</a> | <a href="https://github.com/sourcegraph/sourcegraph-public-snapshot"  target="_blank" rel="noreferrer">Source Code</a> | <a href="https://sourcegraph.com/docs/integration/browser_extension/references/privacy"  target="_blank" rel="noreferrer">Privacy</a><br>
<a href="https://chrome.google.com/webstore/detail/sourcegraph/dgjhfomjieaadpoljlnidmbgkdffpack"  target="_blank" rel="noreferrer">Chrome</a></p>
<p>If you need to do a little debugging on a repo, you have to clone it locally and open it in your favorite IDE or code editor, something that can be expensive (time-wise) if you just need to take a quick peek. Well you <em>had</em> to clone it, anyway.</p>
<p><a href="https://sourcegraph.com/docs/integration/browser_extension"  target="_blank" rel="noreferrer">Sourcegraph</a> is an IDE for the browser that works with multiple languages. Just hover over a keyword or identifier in your codebase, and the addon pops up a link that takes you to sourcegraph&rsquo;s site, which in turn provides syntax highlighting and click-through navigation for your application. I tried it with a C# application I wrote for a recent post on <a href="https://grantwinney.com/its-possible-to-test-a-winforms-app-using-mvp/"  target="_blank" rel="noreferrer">MVP</a>, and it had no problem navigating around.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/sourcegraph-in-action-1.png"
    width="992"
      height="554"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/sourcegraph-in-action-2.png"
    width="838"
      height="810"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/sourcegraph-in-action-3.png"
    width="858"
      height="906"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/sourcegraph-in-action-4.png"
    width="1329"
      height="792"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/sourcegraph-in-action-5.png"
    width="742"
      height="689"></figure>
<p>Sourcegraph brings the IDE experience to your browser</p>
<p>It&rsquo;s <a href="https://about.sourcegraph.com/pricing/"  target="_blank" rel="noreferrer">free to use</a> for small teams, and you can <a href="https://sourcegraph.com/docs#quickstart"  target="_blank" rel="noreferrer">install it on premise</a> for free too. Their <a href="https://about.sourcegraph.com/plan"  target="_blank" rel="noreferrer">future goals</a> are lofty, to say the least. It seems they&rsquo;d like to replace the need for separate, local IDEs using a protocol called <a href="https://microsoft.github.io/language-server-protocol/"  target="_blank" rel="noreferrer">LSP</a>, and to eventually have a global graph of <em>all</em> OSS to make it easier to find and share code. 🤯</p>
<p><strong>Alternative:</strong> <a href="https://chrome.google.com/webstore/detail/octohint/hbkpjkfdheainjkkebeoofkpgddnnbpk"  target="_blank" rel="noreferrer">Octohint</a> <em>(</em><a href="https://github.com/pd4d10/octohint"  target="_blank" rel="noreferrer"><em>source code</em></a><em>)</em> appears to do something similar, although I didn&rsquo;t try it out and I have no idea how it&rsquo;s implemented.</p>
<hr>

<h2 class="relative group">Octotree (easy-to-navigate code tree)
    <div id="octotree-easy-to-navigate-code-tree" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#octotree-easy-to-navigate-code-tree" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://www.octotree.io/"  target="_blank" rel="noreferrer">About</a> | <a href="https://github.com/ovity/octotree/blob/master/README.md"  target="_blank" rel="noreferrer">Docs</a> | <a href="https://github.com/ovity/octotree"  target="_blank" rel="noreferrer">Source Code</a> | <a href="https://www.octotree.io/privacy"  target="_blank" rel="noreferrer">Privacy</a><br>
<a href="https://chrome.google.com/webstore/detail/octotree/bkhaagjahfmjljalopjnoealnfndnagc"  target="_blank" rel="noreferrer">Chrome</a> | <a href="https://addons.mozilla.org/en-US/firefox/addon/octotree/"  target="_blank" rel="noreferrer">Firefox</a> | <a href="https://addons.opera.com/en/extensions/details/octotree/"  target="_blank" rel="noreferrer">Opera</a> | <a href="https://itunes.apple.com/us/app/octotree-pro/id1457450145?mt=12"  target="_blank" rel="noreferrer">Safari</a> | <a href="https://github.com/ovity/octotree#access-token"  target="_blank" rel="noreferrer">Access Tokens</a></p>
<p>Once you&rsquo;ve installed sourcegraph, you can navigate your codebase from your browser like a pro, but what about when you need to find a certain file in the first place? You have to either click through multiple levels of directories, or use GitHub&rsquo;s built-in <a href="https://help.github.com/en/github/searching-for-information-on-github/finding-files-on-github"  target="_blank" rel="noreferrer">file search function</a>, which is powerful but a little cumbersome.</p>
<p>Octotree produces a file explorer style &ldquo;directory structure&rdquo; view of your entire repo. Just expand to the file you need, and click the name to go to that file or click the arrow to the left of it to see the &ldquo;raw&rdquo; version of the file. I think this complements sourcegraph really well, and makes the IDE experience in the browser that much more awesome.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/octotree1.png"
    width="1920"
      height="908"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/octotree2.png"
    width="1920"
      height="908"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/octotree3.png"
    width="1920"
      height="908"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/octotree4.png"
    width="1920"
      height="908"></figure>
<hr>

<h2 class="relative group">ZenHub (kanban-style project management)
    <div id="zenhub-kanban-style-project-management" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#zenhub-kanban-style-project-management" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://www.zenhub.com/product"  target="_blank" rel="noreferrer">About</a> | <a href="https://help.zenhub.com/support/solutions/43000361405"  target="_blank" rel="noreferrer">Docs</a> | <a href="https://www.zenhub.com/privacy-policy"  target="_blank" rel="noreferrer">Privacy</a><br>
<a href="https://chrome.google.com/webstore/detail/zenhub-for-github/ogcgkffhplmphkaahpmffcafajaocjbd"  target="_blank" rel="noreferrer">Chrome</a></p>
<p>One of the toughest things to grok when you join a new team, is to figure out where everything <em>is.</em> Where the source code is, where the issues and PR&rsquo;s related to that source code are, where the project management that organizes and prioritizes the stories related to those issues and PR&rsquo;s are&hellip; where the internal documentation lives, where the <em>external</em> documentation lives, and on and on <em>and on&hellip;</em></p>
<p>If you can keep your tools in a single area, it makes life that much easier. I haven&rsquo;t played with GitHub&rsquo;s projects much, but they seem tied to a single repo. In my experience, a single team working on a single project might actually be committing changes to several (or dozens) of related repos.</p>
<p><a href="https://www.zenhub.com/product"  target="_blank" rel="noreferrer">ZenHub</a> lets you group a bunch of projects into a single &ldquo;workspace&rdquo;, and then integrates itself smoothly into the GitHub UI, so you can manage the workspace from any repo. I was able to easily group two related repos, pull in their issues, move the cards around, and even mark an issue in one repo as &ldquo;blocked&rdquo; by an issue in the other repo.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/brave_XTqYuR969v.png"
    width="533"
      height="1050"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/brave_TkHhqi14Ul.png"
    width="773"
      height="884"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/brave_mYgeA1xxHW.png"
    width="1241"
      height="872"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/brave_HLB3SFKXXg.png"
    width="1366"
      height="701"></figure>
<p>ZenHub tracks progress across multiple repos, right from the GitHub UI</p>
<p>The ZenHub board is a kanban style, where you can easily drag issues (and PR&rsquo;s, etc) around the board to indicate what their current status is. It&rsquo;s free for personal, public repos too, so check it out.</p>
<hr>

<h2 class="relative group">GitHub File Icons (file icons from Atom editor)
    <div id="github-file-icons-file-icons-from-atom-editor" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#github-file-icons-file-icons-from-atom-editor" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://github.com/lvarayut/github-file-icons"  target="_blank" rel="noreferrer">Source Code</a><br>
<a href="https://chrome.google.com/webstore/detail/github-file-icons/kkokonbjllgdmblmbichgkkikhlcnekp"  target="_blank" rel="noreferrer">Chrome</a></p>
<p>GitHub shows a generic text file icon for every file, regardless of its type. Considering <a href="https://www.scholastic.com/parents/family-life/creativity-and-critical-thinking/learning-skills-for-kids/why-colors-and-shapes-matter.html"  target="_blank" rel="noreferrer">colors and shapes were the first things most of us learned</a>, I think GitHub could do a little better. <a href="https://chrome.google.com/webstore/detail/github-file-icons/kkokonbjllgdmblmbichgkkikhlcnekp"  target="_blank" rel="noreferrer">GitHub File Icons</a> brings the same as Octotree (which come from the <a href="https://ide.atom.io/"  target="_blank" rel="noreferrer">Atom</a> editor) into the main GitHub UI. So if you use Octotree, this fits in really nicely.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/fileicons1-1.png"
    width="1911"
      height="899"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/fileicons3-1.png"
    width="1911"
      height="905"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/fileicons4-1.png"
    width="1911"
      height="907"></figure>
<p>GitHub File Icons add file type specific icons (optionally with color)</p>
<p><strong>Alternative:</strong> The <a href="https://chrome.google.com/webstore/detail/github-vscode-icons/hoccpcefjcgnabbmojbfoflggkecmpgd"  target="_blank" rel="noreferrer">github-vscode-icons</a> addon <em>(</em><a href="https://github.com/dderevjanik/github-vscode-icons"  target="_blank" rel="noreferrer"><em>source code</em></a><em>)</em> uses icons from <a href="https://code.visualstudio.com/"  target="_blank" rel="noreferrer">VS Code</a>, which IMO stand out <em>much</em> better. I find the other icon colors a bit washed out. Presently though, this addon seems to have an issue checking for updated icons.. they appear for a moment, then all change to a small &ldquo;loading&rdquo; icon until you refresh the page. So &hellip; kinda broken unfortunately. :(</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/vscodeicons1.png"
    width="1418"
      height="800"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/vscodeicons2.png"
    width="1418"
      height="800"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/vscodeicons3.png"
    width="1418"
      height="800"></figure>
<p>Vanilla GitHub vs GitHub File Icons vs github-vscode-icons</p>
<hr>

<h2 class="relative group">Enhanced GitHub (view repository size)
    <div id="enhanced-github-view-repository-size" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#enhanced-github-view-repository-size" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://github.com/softvar/enhanced-github/blob/master/README.md"  target="_blank" rel="noreferrer">Docs</a> | <a href="https://github.com/softvar/enhanced-github"  target="_blank" rel="noreferrer">Source Code</a><br>
<a href="https://chrome.google.com/webstore/detail/enhanced-github/anlikcnbgdeidpacdbdljnabclhahhmd"  target="_blank" rel="noreferrer">Chrome</a> | <a href="https://github.com/softvar/enhanced-github#github-api-rate-limiting"  target="_blank" rel="noreferrer">Access Tokens</a></p>
<p>Did you know you can store any type of file on GitHub, with <a href="https://help.github.com/en/github/managing-large-files/what-is-my-disk-quota#file-and-repository-size-limitations"  target="_blank" rel="noreferrer">files up to 100 MB and repos up to 100 GB</a> (although they recommend 50 MB and 1 GB, respectively, for top performance)? That being said, you need to have a legitimate reason for files and repos that large, but assuming there <em>is</em> one, wouldn&rsquo;t you like to know it before cloning some 5 GB repo to your local disk?!</p>
<p><a href="https://chrome.google.com/webstore/detail/enhanced-github/anlikcnbgdeidpacdbdljnabclhahhmd"  target="_blank" rel="noreferrer">Enhanced GitHub</a> displays the size of the repo right above the&quot;Clone&quot; button. It also displays sizes of individual files, which is cool although I don&rsquo;t find that as useful. Another feature that I do like though is &ldquo;Copy File&rdquo;, which.. um&hellip; copies the current file to your clipboard. Kinda self-explanatory, now that I think about it.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/image-9.png"
    width="999"
      height="381"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/image-10.png"
    width="996"
      height="465"></figure>
<p>View a repository&rsquo;s size before deciding to clone</p>
<p>Regarding privacy, similar to the other addons, <a href="https://github.com/softvar/enhanced-github#features"  target="_blank" rel="noreferrer">it can&rsquo;t access private repos</a> unless you create an access token, but again you might as well so you don&rsquo;t hit the unauthenticated API rate limit.</p>
<hr>

<h2 class="relative group">GitZip (download partial repos)
    <div id="gitzip-download-partial-repos" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#gitzip-download-partial-repos" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://gitzip.org/"  target="_blank" rel="noreferrer">About</a> | <a href="https://github.com/GitZip/chrome-extension"  target="_blank" rel="noreferrer">Source Code</a> | <a href="https://gitzip.org/privacy_policy.html"  target="_blank" rel="noreferrer">Privacy</a><br>
<a href="https://chrome.google.com/webstore/detail/gitzip-for-github/ffabmkklhbepgcgfonabamgnfafbdlkn"  target="_blank" rel="noreferrer">Chrome</a> | <a href="https://addons.mozilla.org/en-US/firefox/addon/gitzip/"  target="_blank" rel="noreferrer">Firefox</a></p>
<p><em><strong>(possibly dead?)</strong></em></p>
<p>One of the ways I use GitHub is to upload code samples from various posts I&rsquo;ve written, going back several years. I create a new directory for the blog post in that repo, then upload the code to that directory, and link to it from the post. It&rsquo;s sorta like Finder or Windows Explorer, except you can&rsquo;t download just the directory.. you have to clone the entire repo and dig around in it! Until now&hellip;</p>
<p><a href="https://gitzip.org/"  target="_blank" rel="noreferrer">GitZip</a> lets you click on one or more files or directories (actually, you have to click the white space <em>next to</em> the name), and then you get a handy &ldquo;download&rdquo; button for your selection, which gets (gits?) you a zip file. Who says naming things is hard?</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/gitzip2.png"
    width="1494"
      height="946"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/gitzip3.png"
    width="1494"
      height="946"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/gitzip4.png"
    width="961"
      height="557"></figure>
<p>This complements the Enhanced GitHub addon pretty well - if you find out a repo is too large and you don&rsquo;t want to bother cloning it, use GitZip to download just the parts you <em>are</em> interested in! I can imagine other sites where this would be useful, like programming books that upload the related code to a repo, one lesson or exercise per directory.</p>
<p>After a couple tries, I hit my unauthenticated limits. Just click the GitZip icon to create an access token, and you&rsquo;ll be back in business.</p>
<hr>

<h2 class="relative group">Wide GitHub (same ol&rsquo; GitHub.. but wider)
    <div id="wide-github-same-ol-github-but-wider" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#wide-github-same-ol-github-but-wider" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://github.com/xthexder/wide-github"  target="_blank" rel="noreferrer">Source Code</a><br>
<a href="https://chrome.google.com/webstore/detail/wide-github/kaalofacklcidaampbokdplbklpeldpj/related"  target="_blank" rel="noreferrer">Chrome</a></p>
<p>There are times when you fully expect an app won&rsquo;t use all your screen real estate.. playing a retro PC game, or <a href="https://grantwinney.com/installing-windows-3-1-in-vmware-player/"  target="_blank" rel="noreferrer">installing Windows 3.1</a> for example. But in a modern web app, there&rsquo;s no reason <em>not</em> to take advantage of a wider screen. For some reason though, GitHub doesn&rsquo;t and (AFAIK) never has.</p>
<p><a href="https://github.com/xthexder/wide-github"  target="_blank" rel="noreferrer">Wide GitHub</a> applies some CSS that takes full advantage of your 32:9 super-duper ultra-mega-wide curvy monitor. Now your GitHub code can wrap all around you, like a giant omnimax theater.. for code. But if you hit a page that seems less readable in wide format, just click the icon to disable it.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/wide1.png"
    width="1911"
      height="1030"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/wide4.png"
    width="1911"
      height="1030"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/wide2.png"
    width="1911"
      height="1030"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/wide5.png"
    width="1911"
      height="1030"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/wide3.png"
    width="1911"
      height="1030"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/wide6.png"
    width="1911"
      height="1030"></figure>
<p>If you already use <a href="https://add0n.com/stylus.html"  target="_blank" rel="noreferrer">Stylus</a> (or Tampermonkey, etc), there are instructions on <a href="https://github.com/xthexder/wide-github/blob/master/README.md#installing"  target="_blank" rel="noreferrer">how to use them</a> instead. But the addon is easier and you&rsquo;ll get automatic updates, so there&rsquo;s that&hellip; decisions, decisions.</p>
<hr>

<h2 class="relative group">GitHub 404 Breakdown (git blame meets 404)
    <div id="github-404-breakdown-git-blame-meets-404" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#github-404-breakdown-git-blame-meets-404" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://github.com/SidneyNemzer/github-404-breakdown"  target="_blank" rel="noreferrer">Source Code</a><br>
<a href="https://chrome.google.com/webstore/detail/github-404-breakdown/pnhdlhabpckpibnkkddmgcimdejbljge"  target="_blank" rel="noreferrer">Chrome</a></p>
<p>When you come across a broken GitHub link, you get a 404 - no surprises there. The problem is, the 404 page doesn&rsquo;t tell you if the entire repo is gone, or the branch that was linked was merged back to master, or just the linked file was deleted. Your forced to do some digging to figure out which might be true.</p>
<p>If everything goes well and you never come across a broken link, then you&rsquo;ll never even notice <a href="https://chrome.google.com/webstore/detail/github-404-breakdown/pnhdlhabpckpibnkkddmgcimdejbljge"  target="_blank" rel="noreferrer">GitHub 404 Breakdown</a>. But when you inevitably <em>do</em> hit a broken link, then this nice little addon tests every portion of the URL and lets you know exactly where things broke down.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/breakdown1.png"
    width="1621"
      height="982"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/breakdown2-1.png"
    width="1621"
      height="981"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/breakdown3-1.png"
    width="1620"
      height="980"></figure>
<hr>

<h2 class="relative group">GitHub Issue Link Status (inline issue and PR statuses)
    <div id="github-issue-link-status-inline-issue-and-pr-statuses" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#github-issue-link-status-inline-issue-and-pr-statuses" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://github.com/fregante/github-issue-link-status"  target="_blank" rel="noreferrer">Source Code</a><br>
<a href="https://chrome.google.com/webstore/detail/github-issue-link-status/nbiddhncecgemgccalnoanpnenalmkic"  target="_blank" rel="noreferrer">Chrome</a> | <a href="https://addons.mozilla.org/en-US/firefox/addon/github-issue-link-status/"  target="_blank" rel="noreferrer">Firefox</a></p>
<p>It&rsquo;s amazing how something as straight-forward as adding a little color can instantly tell you something important, eliminating the need to have to spend a couple extra brain cycles thinking about it.</p>
<p><a href="https://github.com/fregante/github-issue-link-status"  target="_blank" rel="noreferrer">GitHub Issue Link Status</a> does just that, adding different colors and icons to issues and PRs, dependent on whether they&rsquo;re open, closed, or merged. And it seems to work all over the GitHub interface, not just the issue or PR screens.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/issuelinkstatus1.png"
    width="1192"
      height="186"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/issuelinkstatus2.png"
    width="1199"
      height="182"></figure>
<hr>

<h2 class="relative group">GitHub Custom Tab Size
    <div id="github-custom-tab-size" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#github-custom-tab-size" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://github.com/lukechilds/github-custom-tab-size"  target="_blank" rel="noreferrer">Source Code</a><br>
<a href="https://chrome.google.com/webstore/detail/github-custom-tab-size/jcjfkmdkcaopkioccnpbhiemfcmpnghe"  target="_blank" rel="noreferrer">Chrome</a></p>
<p>Commit a file with tabs in it, and then open it on GitHub. Oddly, if you <em>edit</em> the file right on GitHub, you can choose between a few sizes for tabs. But if you just want to view the file (far more likely), you get 8 spaces per tab. Maybe it&rsquo;s GitHub&rsquo;s way of showing you the truth&hellip; spaces <strong>are</strong> better! <em><em>ducks</em></em></p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/tabs3.png"
    width="865"
      height="339"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/tabs2.png"
    width="870"
      height="347"></figure>
<p>When editing a file, you can choose 2, 4, or 8 spaces for tabs&hellip; but not when <em>viewing!s</em></p>
<p>The <a href="https://chrome.google.com/webstore/detail/github-custom-tab-size/jcjfkmdkcaopkioccnpbhiemfcmpnghe"  target="_blank" rel="noreferrer">GitHub Custom Tab Size</a> addon gives you an easy way to change that, especially helpful if you&rsquo;re working in a codebase with deeply nested code. Yeah yeah, this can be a sign of a code smell.. but until you manage to refactor that old codebase that&rsquo;s 20 levels deep, this addon might help preserve your sanity.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/W3TJs5alK3.gif"
    width="946"
      height="604"></figure>
<hr>

<h2 class="relative group">Better Pull Request for GitHub (navigation for large PRs)
    <div id="better-pull-request-for-github-navigation-for-large-prs" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#better-pull-request-for-github-navigation-for-large-prs" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://github.com/berzniz/github_pr_tree"  target="_blank" rel="noreferrer">Source Code</a><br>
<a href="https://chrome.google.com/webstore/detail/better-pull-request-for-g/nfhdjopbhlggibjlimhdbogflgmbiahc"  target="_blank" rel="noreferrer">Chrome</a></p>
<p>If you&rsquo;ve been doing PRs as a team for any length of time, sooner or later you&rsquo;ll come across a simply massive PR. It might be 2 lines changed in each of 100 files, or a project that kept increasing in scope across too many sprints. Whatever happened, it&rsquo;ll be daunting to scroll through, in part due to the fact that GitHub just lays out all the file changes without any sort of navigation or table of contents.</p>
<p><a href="https://chrome.google.com/webstore/detail/better-pull-request-for-g/nfhdjopbhlggibjlimhdbogflgmbiahc/related"  target="_blank" rel="noreferrer">Better Pull Request for GitHub</a> generates a table of contents (only when you&rsquo;re viewing a PR) that looks very similar to the navigational explorer window you&rsquo;d find in nearly any IDE. Clicking a link takes you right to the file in question, and as you scroll the looooong PR, the selected file actually changes depending on where you are on screen.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/f8obsQf4Mn.gif"
    width="1290"
      height="653"></figure>
<p>Although Octotree is collapsed here, it plays nicely with the navigational tree that Octotree creates too. Better Pull Request inserts its panel into the PR page smoothly, making it appear like it&rsquo;s just another part of the UI.</p>
<hr>

<h2 class="relative group">OctoLinker (links libraries to their official docs)
    <div id="octolinker-links-libraries-to-their-official-docs" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#octolinker-links-libraries-to-their-official-docs" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://octolinker.now.sh/"  target="_blank" rel="noreferrer">About</a> | <a href="https://github.com/OctoLinker/OctoLinker"  target="_blank" rel="noreferrer">Source Code</a><br>
<a href="https://chrome.google.com/webstore/detail/octolinker/jlmafbaeoofdegohdhinkhilhclaklkp"  target="_blank" rel="noreferrer">Chrome</a><br>
<a href="https://github.com/settings/tokens/new?scopes=public_repo&amp;description=OctoLinker"  target="_blank" rel="noreferrer">Public Access Token</a> | <a href="https://github.com/settings/tokens/new?scopes=repo&amp;description=OctoLinker"  target="_blank" rel="noreferrer">Private Access Token</a></p>
<p>This one definitely follows in the suit of other addons that enhance GitHub to make it more of an IDE experience right in your browser. Every application you write, regardless of the language, will depend on other libraries. If you see a reference to a library, you have to manually look it up.</p>
<p><a href="https://octolinker.now.sh/"  target="_blank" rel="noreferrer">OctoLinker</a> looks at the file type, and if it&rsquo;s a <a href="https://octolinker.now.sh/#languages"  target="_blank" rel="noreferrer">supported language</a> (like Python, Ruby, JavaScript, etc), it inserts links for any libraries it finds to their respective docs.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/brave_6bbcZx6Wa4.png"
    width="378"
      height="220"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/brave_fQHJfcdTTG.png"
    width="413"
      height="280"></figure>
<p>It&rsquo;s supposed to work for relative file references between your files too, but the only thing I had to try it on were some Python scripts, and it didn&rsquo;t work with those.</p>
<hr>

<h2 class="relative group">Notifier for GitHub (simple notification count)
    <div id="notifier-for-github-simple-notification-count" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#notifier-for-github-simple-notification-count" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://github.com/sindresorhus/notifier-for-github"  target="_blank" rel="noreferrer">Source Code</a><br>
<a href="https://chrome.google.com/webstore/detail/notifier-for-github/lmjdlojahmbbcodnpecnjnmlddbkjhnn"  target="_blank" rel="noreferrer">Chrome</a> | <a href="https://addons.mozilla.org/en-US/firefox/addon/notifier-for-github/"  target="_blank" rel="noreferrer">Firefox</a><br>
<a href="https://github.com/settings/tokens/new?scopes=notifications&amp;description=Notifier%20for%20GitHub%20extension"  target="_blank" rel="noreferrer">Public Access Token</a> | <a href="https://github.com/settings/tokens/new?scopes=notifications,repo&amp;description=Notifier%20for%20GitHub%20extension"  target="_blank" rel="noreferrer">Private Access Token</a></p>
<p>GitHub has a spiffy <a href="https://github.com/notifications"  target="_blank" rel="noreferrer">Notifications</a> area that alerts you to <a href="https://help.github.com/en/github/managing-subscriptions-and-notifications-on-github/about-notifications#default-subscriptions"  target="_blank" rel="noreferrer">all kinds of things</a> like assigned issues and PR&rsquo;s, threads you commented on, other people @mentioning you, etc. I&rsquo;d suggest going into your <a href="https://github.com/settings/notifications"  target="_blank" rel="noreferrer">notification settings</a> and deciding what exactly you even want to be notified on. You&rsquo;ll notice they come in 2 flavors - via the UI if you have GitHub open onscreen, and via email.</p>
<p>If you want to know about new notifications, but you neither need more emails nor leave GitHub visible all day, then checkout <a href="https://github.com/sindresorhus/notifier-for-github"  target="_blank" rel="noreferrer">Notifier for GitHub</a>. It&rsquo;s so simple there&rsquo;s nothing really to show.. it displays an icon in your toolbar with a notification count, and clicking on it opens the GitHub notifications page.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/brave_AfaBFyoLnv.png"
    width="1302"
      height="910"></figure>
<p>Oh look, I found something to show anyway. 1 unread item is offscreen.</p>
<p>It&rsquo;s just making a call to the <a href="https://developer.github.com/v3/activity/notifications/#list-notifications-for-the-authenticated-user"  target="_blank" rel="noreferrer">Notifications API</a>, which returns a JSON block with detailed info about your notifications, and then it counts the number of returned items. Actually, since the author has access to all that data, I wish there was an option to hover over the toolbar icon and see a brief summary of the newest notifications, but oh well. It&rsquo;s nice anyway, if you&rsquo;re trying to stay on top of alerts.</p>
<p><strong>Alternative:</strong> There&rsquo;s another addon that actually <em>does</em> show information about the notifications, called <a href="https://chrome.google.com/webstore/detail/notifications-preview-for/kgilejfahkjidpaclkepbdoeioeohfmj/related"  target="_blank" rel="noreferrer">Notifications Preview for GitHub</a>, but <a href="https://github.com/tanmayrajani/notifications-preview-github/issues/79"  target="_blank" rel="noreferrer">it seems to be broken with the new beta interface</a>&hellip; which probably means it traverses the DOM instead of using the API, and the new UI likely completely changed the DOM.</p>
<hr>

<h2 class="relative group">Honorable Mentions
    <div id="honorable-mentions" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#honorable-mentions" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Some addons look promising, but aren&rsquo;t quite ready for prime-time yet. Others are popular, but try to fix everything instead of focusing on one thing. Here are some addons I came across that are worth a look, but I</p>

<h3 class="relative group">Paint GitHub (a picture is worth a thousand words)
    <div id="paint-github-a-picture-is-worth-a-thousand-words" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#paint-github-a-picture-is-worth-a-thousand-words" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p><a href="https://chrome.google.com/webstore/detail/paint-github/dmcjbappfnlamankemdmmdjiecnclapl"  target="_blank" rel="noreferrer">Chrome</a></p>
<p>A picture is worth a thousand words, especially when you&rsquo;re trying to describe a problem you&rsquo;re having. <a href="https://chrome.google.com/webstore/detail/paint-github/dmcjbappfnlamankemdmmdjiecnclapl"  target="_blank" rel="noreferrer">Paint GitHub</a> adds a new tab with a canvas to the &ldquo;New Issue&rdquo; screen, allowing you to create a freehand drawing and then &ldquo;upload&rdquo; it to GitHub for attachment. If you want to draw over an existing image, like maybe a screen capture of the problem you&rsquo;re having, you can insert it with &ldquo;Insert image&rdquo;.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/paint2.png"
    width="1374"
      height="948"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/paint3.png"
    width="1380"
      height="661"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/paint4.png"
    width="1672"
      height="1404"></figure>
<p>It appears to be in the early stages of development, but I hope the author continues with it. You can draw free-hand but not much else - it&rsquo;d be great to have all the basic functions of a &ldquo;paint&rdquo; style app like adding text and geometric shapes, etc. But&hellip; it works as-is and (for some cases) eliminates the need to edit an image in a separate paint app and then drag it over to the &ldquo;New Issue&rdquo; panel.</p>

<h3 class="relative group">Quick add issue to GitHub
    <div id="quick-add-issue-to-github" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#quick-add-issue-to-github" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p><a href="https://github.com/stilliard/quick-add-github-issue-browser-extension"  target="_blank" rel="noreferrer">Source Code</a><br>
<a href="https://chrome.google.com/webstore/detail/quick-add-issue-to-github/mgamfhobfmlghohfjdiecjhddoigenkk"  target="_blank" rel="noreferrer">Chrome</a> | <a href="https://github.com/settings/tokens/new?scopes=repo,read:org&amp;description=Quick%20add%20issue%20to%20GitHub"  target="_blank" rel="noreferrer">Access Token</a></p>
<p>It&rsquo;s pretty self-explanatory, allowing you to quickly add issues to a repo. Unfortunately, it only works for a single user or organization, not across <em>all</em> the organizations a person might belong too. Hope the author makes a few changes to it.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/addissue1.png"
    width="789"
      height="417"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/addissue2.png"
    width="426"
      height="503"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/13-addons-that-power-up-your-github-game/addissue3.png"
    width="1609"
      height="1032"></figure>

<h3 class="relative group">Refined GitHub
    <div id="refined-github" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#refined-github" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p><a href="https://chrome.google.com/webstore/detail/refined-github/hlepfoohegkhhmjieoechaddaejaokhf"  target="_blank" rel="noreferrer">Chrome</a></p>
<p>I didn&rsquo;t try <a href="https://chrome.google.com/webstore/detail/refined-github/hlepfoohegkhhmjieoechaddaejaokhf"  target="_blank" rel="noreferrer">Refined GitHub</a>, but the author (who also wrote <em>Notifier for GitHub,</em> above) is prolific on GitHub for his OSS, and the addon is very highly rated. It&rsquo;s definitely worth checking out, but I&rsquo;m personally not interested in an addon that fixes all the things for all the people.</p>
]]></content:encoded><media:content url="https://grantwinney.com/13-addons-that-power-up-your-github-game/feature.webp" medium="image" type="image/webp"/></item><item><title>How to find the iCal address for a public Google calendar</title><link>https://grantwinney.com/how-to-find-the-ical-address-for-a-public-google-calendar/</link><pubDate>Fri, 28 Feb 2020 03:42:32 +0000</pubDate><guid>https://grantwinney.com/how-to-find-the-ical-address-for-a-public-google-calendar/</guid><description>Every Google calendar URL has an iCal file you can use&amp;hellip; here&amp;rsquo;s how to find it.</description><content:encoded><![CDATA[<p>If you already know why you&rsquo;re here, then visit the CodePen link below, plug-in the public URL (or the calendar ID) from the calendar settings page, and click the appropriate button to get the iCal link.</p>
<p><a href="https://codepen.io/astrangegame/pen/azvazpJ"  target="_blank" rel="noreferrer">Find the iCal address for a public Google calendar</a></p>
<p>For everyone else, scroll past the text boxes for a brief explanation&hellip;</p>

<h2 class="relative group">How&rsquo;s that work?
    <div id="hows-that-work" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#hows-that-work" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Impressive, you didn&rsquo;t just run off. This won&rsquo;t take too long, I promise.</p>
<p>When someone creates a public Google calendar for the whole world to use, you&rsquo;ll see a little &ldquo;+ Google Calendar&rdquo; button in the lower-right corner. Click on that, and you can import the calendar into your own Google account.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-find-the-ical-address-for-a-public-google-calendar/google-calendar-html.png"
    width="1018"
      height="619"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-find-the-ical-address-for-a-public-google-calendar/google-calendar-add-prompt.png"
    width="1146"
      height="678"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-find-the-ical-address-for-a-public-google-calendar/google-calendar-calendar-added.png"
    width="1148"
      height="679"></figure>
<p>Unless you&rsquo;ve replaced it with another service, like I did. 😐</p>
<p>If you don&rsquo;t <em>want</em> to import a Google calendar into a Google account, you might see the &ldquo;Public URL&rdquo; and try to import that into another client. That&rsquo;s reasonable, but it won&rsquo;t work.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-find-the-ical-address-for-a-public-google-calendar/google-calendar-settings.png"
    width="1267"
      height="312"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-find-the-ical-address-for-a-public-google-calendar/import-calendar-error.png"
    width="1028"
      height="576"></figure>
<p>The reason it can&rsquo;t be imported is that the Public URL from Google is really just a link to an HTML page, and other clients don&rsquo;t know what to do with it. You need something that&rsquo;s standardized, that all clients can easily consume and do something with, and that&rsquo;s an iCalendar file, which (if you open it up in a text editor) looks something like this:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054//EN
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:PUBLISH
X-WR-TIMEZONE:UTC

BEGIN:VEVENT
DTSTART;VALUE=DATE:20210402
DTEND;VALUE=DATE:20210403
DTSTAMP:20200227T163343Z
UID:20210402_60o30dr5coo30c1g60o30dr56k@google.com
CLASS:PUBLIC
CREATED:20190517T221201Z
DESCRIPTION:Holiday or observance in: Connecticut\, Hawaii\, Delaware\, Ind
 iana\, Kentucky\, Louisiana\, New Jersey\, North Carolina\, North Dakota\, 
 Tennessee\, Texas
LAST-MODIFIED:20190517T221201Z
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Good Friday (regional holiday)
TRANSP:TRANSPARENT
END:VEVENT

BEGIN:VEVENT
DTSTART;VALUE=DATE:20200410
DTEND;VALUE=DATE:20200411
DTSTAMP:20200227T163343Z
UID:20200410_60o30dr5coo30c1g60o30dr56g@google.com
CLASS:PUBLIC
CREATED:20190517T221201Z
DESCRIPTION:Holiday or observance in: Connecticut\, Hawaii\, Delaware\, Ind
 iana\, Kentucky\, Louisiana\, New Jersey\, North Carolina\, North Dakota\, 
 Tennessee\, Texas
LAST-MODIFIED:20190517T221201Z
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Good Friday (regional holiday)
TRANSP:TRANSPARENT
END:VEVENT

...
...

BEGIN:VEVENT
DTSTART;VALUE=DATE:20191224
DTEND;VALUE=DATE:20191225
DTSTAMP:20200227T163343Z
UID:20191224_60o30dr56ko30c1g60o30dr56c@google.com
CLASS:PUBLIC
CREATED:20140108T163258Z
DESCRIPTION:
LAST-MODIFIED:20140108T163258Z
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Christmas Eve
TRANSP:TRANSPARENT
END:VEVENT

BEGIN:VEVENT
DTSTART;VALUE=DATE:20191102
DTEND;VALUE=DATE:20191103
DTSTAMP:20200227T163343Z
UID:20191102_60o30c9g6ko30c1g60o30dr56c@google.com
CLASS:PUBLIC
CREATED:20140108T163258Z
DESCRIPTION:
LAST-MODIFIED:20140108T163258Z
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:All Souls&#39; Day
TRANSP:TRANSPARENT
END:VEVENT

END:VCALENDAR</code></pre></div>
<p>As luck would have it, you can easily extract the Calendar ID from any Google calendar URL (or just use the calendar ID directly if you know it), and replace <code>{CALENDAR_ID}</code> in the following URL. Or use the script I wrote at the top of this post.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">https://calendar.google.com/calendar/ical/{CALENDAR_ID}/public/basic.ics</code></pre></div>
<p>That&rsquo;s it!</p>
]]></content:encoded><media:content url="https://grantwinney.com/how-to-find-the-ical-address-for-a-public-google-calendar/feature.webp" medium="image" type="image/webp"/></item><item><title>How to make a dark mode with CSS</title><link>https://grantwinney.com/how-to-make-a-dark-mode-with-css/</link><pubDate>Thu, 13 Feb 2020 23:50:42 +0000</pubDate><guid>https://grantwinney.com/how-to-make-a-dark-mode-with-css/</guid><description>Every time I learn some new piece of CSS I&amp;rsquo;m amazed at how flexible and powerful it is. Like how easy it is to tailor your site for your visitor&amp;rsquo;s &amp;ldquo;dark mode&amp;rdquo; preference!</description><content:encoded><![CDATA[<p>Every time I learn some new piece of CSS I&rsquo;m amazed at how flexible and powerful it is, and the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme"  target="_blank" rel="noreferrer">prefers-color-scheme media element</a> is no exception. The &ldquo;dark mode&rdquo; setting from a visitor&rsquo;s desktop or mobile device can be passed to the browser, which then applies it to your site according to your style sheets. So. Cool.</p>
<p>MDN has a good example and lots of notes, as usual <em>(I love their docs!),</em> and I created my own fun little example below. To try it out, toggle between light and dark mode on your device, and the sun should change to a moon. If it doesn&rsquo;t work for some reason, you can see what it <em>should</em> do in the screen capture at the bottom of this post. 🌞 🌜</p>
<div id="sunmoonpic"></div>

<h2 class="relative group">How&rsquo;s it work?
    <div id="hows-it-work" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#hows-it-work" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Without diving too deep, here&rsquo;s a few pieces to the puzzle&hellip;</p>

<h3 class="relative group">Responsive Design
    <div id="responsive-design" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#responsive-design" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>It&rsquo;s possible (and has been for years) to design a website that responds to the device a visitor happens to be using, such as the wildly different screen sizes between a mobile device vs a desktop. This could involve writing JavaScript, but as CSS is given more power, it&rsquo;s able to handle most layouts all by itself.</p>

<h3 class="relative group">Media Queries
    <div id="media-queries" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#media-queries" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>One element of CSS that figures into responsive design is the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries"  target="_blank" rel="noreferrer">media query</a>, which can account for things like screen resolution, orientation, and whether the visitor prefers higher contrast colors.</p>
<p>For this to work, a device has to make this data available to the browser, which in turn has to use it to apply the correct CSS layout to the page. Different layouts result in different color schemes, collapsed menus, sidebars dropping below the post, etc.</p>

<h3 class="relative group">Prefers-color-scheme Feature
    <div id="prefers-color-scheme-feature" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#prefers-color-scheme-feature" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>One of the media features, called <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme"  target="_blank" rel="noreferrer">prefers-color-scheme</a>, is used to determine whether the user prefers light mode or dark mode. It&rsquo;s based on their device settings, and <a href="https://caniuse.com/#feat=prefers-color-scheme"  target="_blank" rel="noreferrer">most browsers support it</a>.</p>
<p>You specify your &ldquo;base&rdquo; styles first - whatever you want applied no matter the device setting - and then you can override those based on whether the visitor prefers light or dark mode. Here&rsquo;s the code I used for the images above:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/css&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">#</span><span class="nn">pic</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">margin</span><span class="p">:</span> <span class="kc">auto</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">height</span><span class="p">:</span> <span class="mi">400</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">width</span><span class="p">:</span> <span class="mi">400</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">background-image</span><span class="p">:</span> <span class="nb">url</span><span class="p">(</span><span class="s2">&#34;sun.png&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">background-size</span><span class="p">:</span> <span class="mi">360</span><span class="kt">px</span> <span class="mi">360</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">background-repeat</span><span class="p">:</span> <span class="kc">no-repeat</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">		<span class="k">background-position</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">@</span><span class="k">media</span> <span class="o">(</span><span class="nt">prefers-color-scheme</span><span class="o">:</span> <span class="nt">light</span><span class="o">),</span> <span class="o">(</span><span class="nt">prefers-color-scheme</span><span class="o">:</span> <span class="nt">no-preference</span><span class="o">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="p">#</span><span class="nn">pic</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">background-color</span><span class="p">:</span> <span class="kc">skyblue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">background-image</span><span class="p">:</span> <span class="nb">url</span><span class="p">(</span><span class="s2">&#34;sun.png&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">@</span><span class="k">media</span> <span class="o">(</span><span class="nt">prefers-color-scheme</span><span class="o">:</span> <span class="nt">dark</span><span class="o">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="p">#</span><span class="nn">pic</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">background-color</span><span class="p">:</span> <span class="kc">midnightblue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">background-image</span><span class="p">:</span> <span class="nb">url</span><span class="p">(</span><span class="s2">&#34;moon.png&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;pic&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p>Notice that there&rsquo;s a setting for when they haven&rsquo;t indicated a preference, and I used the <code>,</code> to indicate an &ldquo;or&rdquo; clause, meaning (in my example) that the sun image and skyblue background will show up if they&rsquo;ve selected &ldquo;light&rdquo; mode or nothing at all.</p>
<p>And just in case it doesn&rsquo;t work for you, which probably means you&rsquo;re either using an unsupported browser or your device doesn&rsquo;t pass those settings to the browser, this is how it looks in Windows when I toggle between modes. In clockwise order is DuckDuckGo who has a whole separate theme, the MDN example I loved so much, the Windows settings for dark mode, aaaand.. some new-agey sun/moon example someone put together.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-make-a-dark-mode-with-css/dark-scheme.gif"
    width="1095"
      height="829"></figure>
]]></content:encoded><media:content url="https://grantwinney.com/how-to-make-a-dark-mode-with-css/feature.webp" medium="image" type="image/webp"/></item><item><title>Readme</title><link>https://grantwinney.com/read-me/</link><pubDate>Wed, 12 Feb 2020 18:10:41 +0000</pubDate><guid>https://grantwinney.com/read-me/</guid><description/><content:encoded><![CDATA[
<h2 class="relative group">Privacy
    <div id="privacy" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#privacy" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>My policy on privacy? It&rsquo;s good! So in the interest of full disclosure:</p>
<ul>
<li>I use privacy-friendly, analytics-and-tracking-free <a href="https://talk.hyvor.com/privacy"  target="_blank" rel="noreferrer">Hyvor Talk</a> for comments.</li>
<li>I partner with privacy-friendly <a href="https://www.ethicalads.io/privacy-policy/"  target="_blank" rel="noreferrer">EthicalAds</a>, who adheres to the <a href="https://acceptableads.com/standard/"  target="_blank" rel="noreferrer">Acceptable Ads Standard</a>.</li>
<li>I <em><strong>don&rsquo;t</strong></em> feed the Google Analytics beast.</li>
<li>I may (rarely) include affiliate links for products or services I find useful and want to share. They don&rsquo;t increase your cost, but using them helps me out. TIA.</li>
</ul>

<h2 class="relative group">License / Fair-Use
    <div id="license--fair-use" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#license--fair-use" aria-label="Anchor">#</a>
    </span>
    
</h2>

<h3 class="relative group">Text
    <div id="text" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#text" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Writing content takes a considerable investment of time, so please don&rsquo;t go beyond &ldquo;<a href="https://allendyer.com/how-copyright-law-affects-blog-posts/"  target="_blank" rel="noreferrer">fair use</a>&rdquo;. Excerpts and quotes are perfectly reasonable, but copying a whole post is not. In that case, please just link back to the original post on here.</p>

<h3 class="relative group">Code
    <div id="code" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#code" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Unless I note otherwise in individual posts, all code samples on this site are licensed with the <a href="https://opensource.org/licenses/MIT"  target="_blank" rel="noreferrer">MIT License</a> posted below. Use them for whatever you&rsquo;d like, but give credit where it&rsquo;s due. Thanks!</p>
<blockquote><p><em>MIT License</em></p>
<p><em>Copyright (c) 2025 Grant Winney</em></p>
<p><em>Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the &ldquo;Software&rdquo;), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:</em></p>
<p><em>The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.</em></p>
<p><em>THE SOFTWARE IS PROVIDED &ldquo;AS IS&rdquo;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</em></p>
</blockquote><p>Code that lives on GitHub will have its own license, but if it&rsquo;s mine then it&rsquo;s probably using the MIT license as well.</p>
]]></content:encoded><media:content url="https://grantwinney.com/read-me/feature.webp" medium="image" type="image/webp"/></item><item><title>Deploy your own RequestBin in under 5 minutes</title><link>https://grantwinney.com/requestbin-deploy-in-5-minutes/</link><pubDate>Sun, 05 Jan 2020 12:39:14 +0000</pubDate><guid>https://grantwinney.com/requestbin-deploy-in-5-minutes/</guid><description>If you need to consume a webhook from another service, or verify the payload being sent from your own REST API endpoint, RequestBin can help. It intercepts and displays the contents of any call made to it. Here&amp;rsquo;s how to deploy your own instance in just a few minutes.</description><content:encoded><![CDATA[<p>If you&rsquo;ve ever needed to consume a webhook from another service, say from <a href="https://stripe.com/docs/webhooks"  target="_blank" rel="noreferrer">Stripe</a> or <a href="https://developer.github.com/webhooks/"  target="_blank" rel="noreferrer">GitHub</a>, but you weren&rsquo;t completely sure what the payload was going to look like <em>(say, the docs are incomplete or missing),</em> a tool like RequestBin can help. By setting it as the &ldquo;target&rdquo; for the webhook, it intercepts whatever happens to be thrown its way, and displays it.</p>
<p>Same goes if you&rsquo;re developing a REST API and want to make sure that your <code>POST</code> and <code>PUT</code> actions are sending what you expect. You could develop a separate app that consumes your API the way your customers will and displays the results, but why bother with the overhead? <em>(At least, initially&hellip;)</em></p>
<p>The same team that designed RequestBin <em>(which seems to be abandoned, but more on that below)</em> used to host a public instance of it for anyone to use too, but such services don&rsquo;t seem to last, and <a href="https://web.archive.org/web/20210116160324/https://github.com/Runscope/requestbin/commit/8ca17a8ed7f603864329391f4be131c4b3355aaf"  target="_blank" rel="noreferrer">theirs didn&rsquo;t either</a>. It&rsquo;s <em>got</em> to be expensive hosting something like that for thousands <em>(tens of thousands? hundreds?)</em> of users for free. 💸</p>

<h2 class="relative group">Deploy with DigitalOcean in &lt;5 minutes
    <div id="deploy-with-digitalocean-in-5-minutes" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#deploy-with-digitalocean-in-5-minutes" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Fortunately, the makers of RequestBin also made it really easy to deploy on your own. Just create a <a href="https://m.do.co/c/448f25462030"  target="_blank" rel="noreferrer">DigitalOcean</a> droplet with <a href="https://marketplace.digitalocean.com/apps/docker"  target="_blank" rel="noreferrer">Docker</a> preinstalled; unless you know you&rsquo;re going to need more resources, the basic $5/mo plan is sufficient. It should only take a minute or so to spin up.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/requestbin-deploy-in-5-minutes/droplets-create.png"
    width="1217"
      height="875"></figure>
<p>Connect to your new VM, most likely with <code>ssh root@&lt;your-droplet-ip-address&gt;</code>, and then run the commands in the <a href="https://github.com/Runscope/requestbin/blob/master/README.md"  target="_blank" rel="noreferrer">readme</a>. The <code>build</code> command takes a few minutes on its own, but the <code>up</code> command should only take a few seconds.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-NONE" data-lang="NONE">git clone git://github.com/Runscope/requestbin.git
cd requestbin
sudo docker-compose build
sudo docker-compose up -d</code></pre></div>
<p>Assuming no errors in the output, just paste <code>&lt;your-droplet-ip-address&gt;:8000</code> into your favorite browser, and away you go!</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/requestbin-deploy-in-5-minutes/requestbin-load.png"
    width="1284"
      height="667"></figure>
<p>Create your first RequestBin and <code>POST</code> some data with a simple curl command like they suggest. Update the page and you should see your data listed.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/requestbin-deploy-in-5-minutes/requestbin-request.png"
    width="957"
      height="1029"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/requestbin-deploy-in-5-minutes/requestbin-results.png"
    width="1920"
      height="980"></figure>
<p>You can also use a tool like <a href="https://www.getpostman.com/"  target="_blank" rel="noreferrer">Postman</a> to make requests to the endpoint, and even save them for future use - something I&rsquo;ve made extensive use of while learning and writing about various <a href="https://grantwinney.com/tags/api/"  target="_blank" rel="noreferrer">APIs</a>.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/requestbin-deploy-in-5-minutes/postman-example.png"
    width="1920"
      height="1029"></figure>

<h2 class="relative group">Changing Built-in Settings (i.e. max TTL, max requests, and port)
    <div id="changing-built-in-settings-ie-max-ttl-max-requests-and-port" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#changing-built-in-settings-ie-max-ttl-max-requests-and-port" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There&rsquo;s some settings, like a max of 20 requests, that make sense if you&rsquo;ve got an environment that thousands of people will be using. But since it&rsquo;s just you, and maybe a small team, I&rsquo;d say you could safely increase those a bit.</p>
<p>If the container is up and running, <a href="https://docs.docker.com/compose/reference/down/"  target="_blank" rel="noreferrer">bring it down</a> now and verify it&rsquo;s gone.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-NONE" data-lang="NONE">root@docker-s-1vcpu-1gb-nyc3-01:~# docker container ls
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
0f9ecfdde471        requestbin_app      &#34;/bin/sh -c &#39;gunicor…&#34;   25 minutes ago      Up 25 minutes       0.0.0.0:8000-&gt;8000/tcp   requestbin_app_1
99415b11ab7c        redis               &#34;docker-entrypoint.s…&#34;   25 minutes ago      Up 25 minutes       6379/tcp                 requestbin_redis_1

root@docker-s-1vcpu-1gb-nyc3-01:~# cd ~/requestbin/

root@docker-s-1vcpu-1gb-nyc3-01:~/requestbin# sudo docker-compose down
Stopping requestbin_app_1   ... done
Stopping requestbin_redis_1 ... done
Removing requestbin_app_1   ... done
Removing requestbin_redis_1 ... done

root@docker-s-1vcpu-1gb-nyc3-01:~/requestbin# docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES</code></pre></div>
<p>Open the <code>requestbin/config.py</code> file and change some of these values.</p>
<ul>
<li>The <code>BIN_TTL</code> is the time to live in seconds, so if you want your requests to live for a year, then set <code>BIN_TTL = 365*24*3600</code></li>
<li>There&rsquo;s no reason to only hold on to 20 requests; if you like, you could set <code>MAX_REQUESTS = 2000</code> or some other value. If you set it to a million and everything crashes&hellip; not my fault.</li>
</ul>
<p>While you&rsquo;re at it, you could make it so you don&rsquo;t have to enter a port either, since presumably you&rsquo;re not running anything else on this tiny server.</p>
<ul>
<li>Edit <code>docker-compose.yml</code> and change the &ldquo;ports&rdquo; section to <code>&quot;80:8000&quot;</code></li>
<li>Edit <code>Dockerfile</code> to <code>EXPOSE 80</code></li>
<li>Remove the current <code>requestbin_app</code> image with <code>docker image rm</code></li>
<li>Run <code>sudo docker-compose up -d</code> again and verify your changes took effect</li>
</ul>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/requestbin-deploy-in-5-minutes/docker-cmdline.png"
    width="799"
      height="397"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/requestbin-deploy-in-5-minutes/requestbin-no-port.png"
    width="933"
      height="766"></figure>
<p>Some of the values are also hard-coded into the HTML page, so even after doing all the above, the <em>page</em> will probably still tell you you&rsquo;re limited to 20 requests. It lies. If you run the <code>CURL</code> command 30 times now, you&rsquo;ll see 30 requests on the page.</p>

<h2 class="relative group">Other Considerations
    <div id="other-considerations" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#other-considerations" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>So, hopefully you haven&rsquo;t been passing anything too sensitive to your RequestBin instance yet, because right now it&rsquo;s all plain-text. If you need to pass secure data, consider setting up SSL. That&rsquo;s not something I&rsquo;m delving into here - not yet, anyway.</p>
<p><a href="https://github.com/grantwinney/requestbin"  target="_blank" rel="noreferrer">I forked the original project</a> which, as I mentioned, seems to be abandoned. They shutdown the public RequestBin site (understandably), but also haven&rsquo;t merged in PRs or addressed issues for nearly two years.</p>
<p><em>Small side note:</em> If you go into &ldquo;Insights&rdquo;, &ldquo;Dependency Graph&rdquo;, and click &ldquo;Enable&rdquo;, GitHub warns you of security vulnerabilities <em>(even on a fork)</em>&hellip; and then opens PRs on your behalf, which you can merge in or close at your discretion! 👍</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/requestbin-deploy-in-5-minutes/github-fork.png"
    width="1258"
      height="445"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/requestbin-deploy-in-5-minutes/github-security-warnings.png"
    width="1252"
      height="499"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/requestbin-deploy-in-5-minutes/github-security-alerts.png"
    width="1283"
      height="580"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/requestbin-deploy-in-5-minutes/github-dependency-graph.png"
    width="806"
      height="934"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/requestbin-deploy-in-5-minutes/github-dependabot-prs.png"
    width="1258"
      height="502"></figure>
<p>I&rsquo;d love to update the dependencies (i.e. Python2 is dead), merge the pending PRs, and even try addressing some of the issues myself, but that&rsquo;s probably a fool&rsquo;s errand&hellip; at least for this fool. It&rsquo;s a complex project and I don&rsquo;t have the time to dedicate to properly understanding it and bringing it up to speed.</p>
]]></content:encoded><media:content url="https://grantwinney.com/requestbin-deploy-in-5-minutes/feature.webp" medium="image" type="image/webp"/></item><item><title>Why are websites requesting access to motion sensors... on my desktop?</title><link>https://grantwinney.com/websites-requesting-access-to-motion-sensors/</link><pubDate>Mon, 30 Dec 2019 17:49:59 +0000</pubDate><guid>https://grantwinney.com/websites-requesting-access-to-motion-sensors/</guid><description>I was checking the status of a FedEx order when Brave warned me that &amp;ldquo;this site has been blocked from accessing your motion sensors&amp;rdquo;. I&amp;rsquo;m struggling to understand why a website would need that access. Do I get a different experience if I drop my device? Tip my monitor over? Spin the mouse around?</description><content:encoded><![CDATA[<p>I was checking the status of a FedEx order in Brave, when I noticed a notification in the address bar that I&rsquo;ve never seen before. It was warning me that <em>&ldquo;this site has been blocked from accessing your motion sensors&rdquo;</em>. Wut? It doesn&rsquo;t even need to be an order status - <a href="https://www.fedex.com/"  target="_blank" rel="noreferrer">their home page</a> kicks it up too.</p>
<p>I&rsquo;m struggling to understand why a website would need access to a motion sensor on a <em>mobile</em> device, let alone the fact I was using a <em>desktop</em>. Do I get a different experience if I knock my PC off the desk? Tip my monitor on its side? Grab the mouse cord and spin it around my head really fast?</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/websites-requesting-access-to-motion-sensors/brave-motion-sensors-blocked.png"
    width="768"
      height="337"></figure>
<p>After a few cursory online searches, I&rsquo;m coming up with little other than a few threads on <a href="https://www.reddit.com/r/brave_browser/comments/e9jclw/recently_seeing_a_sensors_blocked_notification_in/"  target="_blank" rel="noreferrer">Reddit</a> and <a href="https://community.brave.com/t/motion-sensors/98594"  target="_blank" rel="noreferrer">Brave</a> that indicate people are also seeing this on <a href="http://kayosports.com.au"  target="_blank" rel="noreferrer">Kayo Sports</a> and <a href="https://www.twitch.tv/directory"  target="_blank" rel="noreferrer">Twitch</a>, as well as Experian and Tutanota.</p>
<p>Guess it&rsquo;s time to dig a little deeper.</p>
<hr>

<h2 class="relative group">What are Web APIs?
    <div id="what-are-web-apis" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-are-web-apis" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Before zeroing in on sensors, let&rsquo;s backup a sec and talk about web design and <a href="https://developer.mozilla.org/en-US/docs/Web/API"  target="_blank" rel="noreferrer">Web APIs</a>. Your browser has access to a <em>lot</em> of data via (and metadata regarding) the device you installed it on. As much as some of the websites you visit would looove to have access to all that data, any decent browser acts as a firewall, blocking that access by default and prompting you to allow it.</p>

<h3 class="relative group">Geolocation API
    <div id="geolocation-api" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#geolocation-api" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>One of the more common APIs is the one used to request your location, usually when you&rsquo;re using a websites&rsquo;s &ldquo;store locator&rdquo; to find the store nearest you.</p>
<p>Here&rsquo;s some <em>(lightly modified)</em> from MDN&rsquo;s <a href="https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API"  target="_blank" rel="noreferrer">Geolocation API</a> docs. When you click it, the JavaScript code executes a call to <a href="https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition"  target="_blank" rel="noreferrer">navigator.geolocation.getCurrentPosition()</a>, asking the browser for your location.</p>
<p><a href="https://codepen.io/astrangegame/pen/OPyoPOq"  target="_blank" rel="noreferrer">Simple Geolocation API example</a></p>
<p>Your browser prompts you to allow access, which you can deny. Yay privacy.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/websites-requesting-access-to-motion-sensors/location-prompt.png"
    width="809"
      height="402"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/websites-requesting-access-to-motion-sensors/location-notification.png"
    width="828"
      height="411"></figure>
<p>If you don&rsquo;t see the prompt but you think you&rsquo;ve allowed it, there are two different settings that control access - a global page with a list of &ldquo;blocked&rdquo; and &ldquo;allowed&rdquo; sites, and a per-site page where you can adjust all permissions for a single site. In Chrome, just replace <code>brave://</code> with <code>chrome://</code> in the address bar.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/websites-requesting-access-to-motion-sensors/general-location-settings.png"
    width="1219"
      height="495"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/websites-requesting-access-to-motion-sensors/site-details-settings.png"
    width="1227"
      height="679"></figure>

<h3 class="relative group">Notifications API
    <div id="notifications-api" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#notifications-api" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Another (unfortunately, <em>very</em>) popular API is the one used to display notifications to visitors. Using the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API"  target="_blank" rel="noreferrer">Notifications API</a>, you can request permission from a visitor with a call to <code>Notification.requestPermission()</code> and then just create a <code>new Notification()</code> to <del>annoy them</del> keep them up to date. <em>(</em><a href="https://github.com/brave/brave-browser/issues/2362"  target="_blank" rel="noreferrer"><em>May not work in Brave</em></a> <em>due to a bug.)</em></p>
<p><a href="https://codepen.io/astrangegame/pen/ByoOyYe"  target="_blank" rel="noreferrer">Simple Notifications API example</a></p>

<h3 class="relative group">Sensors API
    <div id="sensors-api" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#sensors-api" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>There&rsquo;s a (maybe sorta?) new API for requesting access to sensors in Chromium-based browsers (<a href="https://www.ghacks.net/2019/03/21/google-adds-sensor-permission-controls-to-chrome/"  target="_blank" rel="noreferrer">Ghacks</a> puts it at Chrome 75, around June 2019. It&rsquo;s not widely supported yet - according to MDN, the only major browsers that currently support it are Chrome and Opera, on desktop and mobile.</p>
<p>Check out the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Sensor_APIs"  target="_blank" rel="noreferrer">MDN docs</a>, the <a href="https://www.w3.org/TR/generic-sensor"  target="_blank" rel="noreferrer">W3C candidate recommendation</a>, the ongoing conversation over at <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=796904#c15"  target="_blank" rel="noreferrer">Chrome</a>, and Intel&rsquo;s <a href="https://intel.github.io/generic-sensor-demos/"  target="_blank" rel="noreferrer">Sensor API playground</a> for examples.</p>
<p>The following examples execute some JavaScript code to try starting up various sensors, which should trigger the sensor icon in the address bar (results may vary by browser). <em>(If an error occurs, it&rsquo;ll display below the links.)</em></p>
<p><a href="https://codepen.io/astrangegame/pen/pvjOvOy"  target="_blank" rel="noreferrer">Simple Sensor API examples</a></p>
<p>As with the geolocation and notification APIs, you can grant or deny access at the global or per-site level. What&rsquo;s kind of annoying is that all of the above sensors fall under a single &ldquo;motion sensors&rdquo; umbrella, so you can&rsquo;t easily tell <em>which</em> of those sensors a particular site is trying to access.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/websites-requesting-access-to-motion-sensors/global-sensors-access-list.png"
    width="1196"
      height="501"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/websites-requesting-access-to-motion-sensors/site-details-settings.png"
    width="1227"
      height="679"></figure>

<h2 class="relative group">Why are sites requesting the Sensors API?
    <div id="why-are-sites-requesting-the-sensors-api" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#why-are-sites-requesting-the-sensors-api" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I&rsquo;ve seen the sensor request on several sites, and others have reported more - FedEx, Lowes, Kayo Sports, Hotels.com, Anthem, Pizza Hut&hellip; to name a few. Why would sites as varied as those need access to a gyroscope or accelerometer? Like all modern development, websites are built upon layers and layers of libraries. Are they using the same one? Is some library several layers deep requesting access to an API it doesn&rsquo;t need?</p>
<p>I think I&rsquo;ve figured it out, but if you find something to the contrary, do share. 🧐</p>

<h3 class="relative group">An obfuscated file, from Akamai
    <div id="an-obfuscated-file-from-akamai" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#an-obfuscated-file-from-akamai" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>All the pages I&rsquo;ve checked out have a reference to an obfuscated file that, when removed, makes the motion sensor icon go away. The name is a 112 bit random value that offers no clues, but differs for each site, so it probably doubles as a unique identifier or account id.</p>
<ul>
<li>Lowe&rsquo;s: <code>c45ff2fedf18894428b6eae366abf1</code></li>
<li>FedEx: <code>b6c65804238fde1fae4a597ae052</code></li>
<li>Anthem: <code>c3ce05c96199f8c080a174ece11ff</code></li>
<li>&hellip; and so on.</li>
</ul>
<p>A look at the markup for the page shows it loads the script right before the end of the page, and it looks nearly identical in all cases.</p>
<p><strong>Lowe&rsquo;s</strong></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">noscript</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://www.lowes.com/akam/11/pixel_49faa00b?a=dD1jMjczMGNkMmRlNmY0NDYwY2Q5MzQ2ZGVjNWI5YWIwZjEwZDM2Nzg0JmpzPW9mZg==&#34;</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;visibility: hidden; position: absolute; left: -999px; top: -999px;&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">noscript</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span> <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">_cf</span> <span class="o">=</span> <span class="nx">_cf</span> <span class="o">||</span> <span class="p">[];</span> 
</span></span><span class="line"><span class="cl">    <span class="nx">_cf</span><span class="p">.</span><span class="nx">push</span><span class="p">([</span><span class="s1">&#39;_setFsp&#39;</span><span class="p">,</span> <span class="kc">true</span><span class="p">]);</span>  
</span></span><span class="line"><span class="cl">    <span class="nx">_cf</span><span class="p">.</span><span class="nx">push</span><span class="p">([</span><span class="s1">&#39;_setBm&#39;</span><span class="p">,</span> <span class="kc">true</span><span class="p">]);</span> 
</span></span><span class="line"><span class="cl">    <span class="nx">_cf</span><span class="p">.</span><span class="nx">push</span><span class="p">([</span><span class="s1">&#39;_setAu&#39;</span><span class="p">,</span> <span class="s1">&#39;/resources/c45ff2fedf18894428b6eae366abf1&#39;</span><span class="p">]);</span> 
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span>  <span class="na">src</span><span class="o">=</span><span class="s">&#34;/resources/c45ff2fedf18894428b6eae366abf1&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p><strong>FedEx</strong></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">noscript</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://www.fedex.com/akam/11/pixel_190b4e7f?a=dD05YmZjNzQ1Njc1YTU3MDA5OWY0MDFiYjRmOWU3YTJhMzJjNjljNjdlJmpzPW9mZg==&#34;</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;visibility: hidden; position: absolute; left: -999px; top: -999px;&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">noscript</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span> <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">_cf</span> <span class="o">=</span> <span class="nx">_cf</span> <span class="o">||</span> <span class="p">[];</span> 
</span></span><span class="line"><span class="cl">    <span class="nx">_cf</span><span class="p">.</span><span class="nx">push</span><span class="p">([</span><span class="s1">&#39;_setFsp&#39;</span><span class="p">,</span> <span class="kc">true</span><span class="p">]);</span>  
</span></span><span class="line"><span class="cl">    <span class="nx">_cf</span><span class="p">.</span><span class="nx">push</span><span class="p">([</span><span class="s1">&#39;_setBm&#39;</span><span class="p">,</span> <span class="kc">true</span><span class="p">]);</span> 
</span></span><span class="line"><span class="cl">    <span class="nx">_cf</span><span class="p">.</span><span class="nx">push</span><span class="p">([</span><span class="s1">&#39;_setAu&#39;</span><span class="p">,</span> <span class="s1">&#39;/assets/b6c65804238fde1fae4a597ae052&#39;</span><span class="p">]);</span> 
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span>  <span class="na">src</span><span class="o">=</span><span class="s">&#34;/assets/b6c65804238fde1fae4a597ae052&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p><strong>Anthem</strong></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">noscript</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://www.anthem.com/akam/11/pixel_31f28831?a=dD04MTAwN2I4YzhlYmNjYjUzYTNjMzA2OTIyMjllNjYzOTRhYjRjNzFiJmpzPW9mZg==&#34;</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;visibility: hidden; position: absolute; left: -999px; top: -999px;&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">noscript</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span> <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">_cf</span> <span class="o">=</span> <span class="nx">_cf</span> <span class="o">||</span> <span class="p">[];</span> 
</span></span><span class="line"><span class="cl">    <span class="nx">_cf</span><span class="p">.</span><span class="nx">push</span><span class="p">([</span><span class="s1">&#39;_setFsp&#39;</span><span class="p">,</span> <span class="kc">true</span><span class="p">]);</span>  
</span></span><span class="line"><span class="cl">    <span class="nx">_cf</span><span class="p">.</span><span class="nx">push</span><span class="p">([</span><span class="s1">&#39;_setBm&#39;</span><span class="p">,</span> <span class="kc">true</span><span class="p">]);</span> 
</span></span><span class="line"><span class="cl">    <span class="nx">_cf</span><span class="p">.</span><span class="nx">push</span><span class="p">([</span><span class="s1">&#39;_setAu&#39;</span><span class="p">,</span> <span class="s1">&#39;/public/c3ce05c96199f8c080a174ece11ff&#39;</span><span class="p">]);</span> 
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span>  <span class="na">src</span><span class="o">=</span><span class="s">&#34;/public/c3ce05c96199f8c080a174ece11ff&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p><strong>Hotels.com</strong></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span> <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">_cf</span> <span class="o">=</span> <span class="nx">_cf</span> <span class="o">||</span> <span class="p">[];</span> 
</span></span><span class="line"><span class="cl">    <span class="nx">_cf</span><span class="p">.</span><span class="nx">push</span><span class="p">([</span><span class="s1">&#39;_setFsp&#39;</span><span class="p">,</span> <span class="kc">true</span><span class="p">]);</span>  
</span></span><span class="line"><span class="cl">    <span class="nx">_cf</span><span class="p">.</span><span class="nx">push</span><span class="p">([</span><span class="s1">&#39;_setBm&#39;</span><span class="p">,</span> <span class="kc">true</span><span class="p">]);</span> 
</span></span><span class="line"><span class="cl">    <span class="nx">_cf</span><span class="p">.</span><span class="nx">push</span><span class="p">([</span><span class="s1">&#39;_setAu&#39;</span><span class="p">,</span> <span class="s1">&#39;/assets/0997d10d16655fda9826ab5d88ea&#39;</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span>  <span class="na">src</span><span class="o">=</span><span class="s">&#34;/assets/0997d10d16655fda9826ab5d88ea&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p><strong>Kayo Sports</strong></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">noscript</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://kayosports.com.au/akam/11/pixel_52bf70fb?a=dD05ZWY4MzI1MTFjODFmMGFmYzhkZmUzNzhkZWNmM2RiZjVmYzc5ZWVhJmpzPW9mZg==&#34;</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;visibility: hidden; position: absolute; left: -999px; top: -999px;&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">noscript</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span> <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">_cf</span> <span class="o">=</span> <span class="nx">_cf</span> <span class="o">||</span> <span class="p">[];</span> 
</span></span><span class="line"><span class="cl">    <span class="nx">_cf</span><span class="p">.</span><span class="nx">push</span><span class="p">([</span><span class="s1">&#39;_setFsp&#39;</span><span class="p">,</span> <span class="kc">true</span><span class="p">]);</span>  
</span></span><span class="line"><span class="cl">    <span class="nx">_cf</span><span class="p">.</span><span class="nx">push</span><span class="p">([</span><span class="s1">&#39;_setBm&#39;</span><span class="p">,</span> <span class="kc">true</span><span class="p">]);</span> 
</span></span><span class="line"><span class="cl">    <span class="nx">_cf</span><span class="p">.</span><span class="nx">push</span><span class="p">([</span><span class="s1">&#39;_setAu&#39;</span><span class="p">,</span> <span class="s1">&#39;/assets/662c194b202d4b929be8d06c3195&#39;</span><span class="p">]);</span> <span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span>  <span class="na">src</span><span class="o">=</span><span class="s">&#34;/assets/662c194b202d4b929be8d06c3195&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p>Since 4 of the 5 sites included a call to a URL with &ldquo;akam/11/pixel&rdquo; in it immediately prior, I assume it&rsquo;s related.. possibly some kind of <a href="https://www.digitalmarketer.com/blog/what-is-tracking-pixel/"  target="_blank" rel="noreferrer">tracking pixel</a> (one of the reasons your email provider blocks images by default). A search of <code>akam/11/pixel</code> (short for Akamai?) turns up loads of other sites that all cause the sensor icon to display too.</p>

<h3 class="relative group">Deobfuscating it leads to code that suggests Akamai
    <div id="deobfuscating-it-leads-to-code-that-suggests-akamai" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#deobfuscating-it-leads-to-code-that-suggests-akamai" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The randomly named js file is always the same, <a href="https://gist.github.com/grantwinney/4f8d8bf7693ab0a4b3733b8710261a51"  target="_blank" rel="noreferrer">but obfuscated</a>. I was able to <a href="https://gist.github.com/grantwinney/7e72df102373e721971edf09cde458ba"  target="_blank" rel="noreferrer">deobfuscate it</a> a bit with an online tool and then further by scripting a search and replace with that <code>_ac</code> array that has 711 elements in it, but that only gets a person so far. Figuring out what this does would be a huge challenge (the reason for obfuscating a file in the first place), but searching for bits and pieces of code turned up a <a href="https://security.stackexchange.com/q/182895"  target="_blank" rel="noreferrer">couple</a> <a href="https://stackoverflow.com/a/59874462"  target="_blank" rel="noreferrer">threads</a> suggesting it&rsquo;s the Akamai <a href="https://www.akamai.com/us/en/products/security/bot-manager.jsp"  target="_blank" rel="noreferrer">bot detection service</a>.</p>
<p>The values in the <code>_ac</code> array might have some clues in it, and some of the entries sound suspicious. There&rsquo;s loads of references to various plugins, and no shortage of references to sensors (gyroscope, magnetometer, accelerometer and accelerationIncludingGravity, ambient-light-sensor, rotationRate, deviceorientation and DeviceOrientationEvent, DeviceMotionEvent, and sensor_data), and other odd stuff (startTracking and requestWakeLock).</p>
<p><a href="https://gist.github.com/grantwinney/b13ad26472d2748ca4e7a69ada134efc.js"  target="_blank" rel="noreferrer">https://gist.github.com/grantwinney/b13ad26472d2748ca4e7a69ada134efc.js</a></p>
<p>Any requests to use those sensors, or even a check to see if a device supports them, would probably cause the sensor icon to show like it does, just like in the sensor examples I wrote up at the top of this post.</p>
<p>The next 30 lines after that one have some possibly-interesting stuff too. A version number, some counters, a URL for some kind of analytics, an api key, and something (maybe a flag?) called <code>sensor_data</code> that&rsquo;s set to 0.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">_cf</span> <span class="o">=</span> <span class="nx">_cf</span> <span class="o">||</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">bmak</span> <span class="o">=</span> <span class="nx">bmak</span> <span class="o">||</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">ver</span><span class="o">:</span> <span class="mf">1.54</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">ke_cnt_lmt</span><span class="o">:</span> <span class="mi">150</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">mme_cnt_lmt</span><span class="o">:</span> <span class="mi">100</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">mduce_cnt_lmt</span><span class="o">:</span> <span class="mi">75</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">pme_cnt_lmt</span><span class="o">:</span> <span class="mi">25</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">pduce_cnt_lmt</span><span class="o">:</span> <span class="mi">25</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">tme_cnt_lmt</span><span class="o">:</span> <span class="mi">25</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">tduce_cnt_lmt</span><span class="o">:</span> <span class="mi">25</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">doe_cnt_lmt</span><span class="o">:</span> <span class="mi">10</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">dme_cnt_lmt</span><span class="o">:</span> <span class="mi">10</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">vc_cnt_lmt</span><span class="o">:</span> <span class="mi">100</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">doa_throttle</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">dma_throttle</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">session_id</span><span class="o">:</span> <span class="nx">default_session</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">js_post</span><span class="o">:</span> <span class="o">!</span><span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">loc</span><span class="o">:</span> <span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">cf_url</span><span class="o">:</span> <span class="p">(</span><span class="nx">https</span><span class="o">:</span> <span class="o">===</span> <span class="nb">document</span><span class="p">[</span><span class="nx">location</span><span class="p">][</span><span class="nx">protocol</span><span class="p">]</span> <span class="o">?</span> <span class="nx">https</span><span class="o">:</span><span class="c1">// : http://) + apid.cformanalytics.com/api/v1/attempt,
</span></span></span><span class="line"><span class="cl">        <span class="nx">params_url</span><span class="o">:</span> <span class="p">(</span><span class="nx">https</span><span class="o">:</span> <span class="o">===</span> <span class="nb">document</span><span class="p">[</span><span class="nx">location</span><span class="p">][</span><span class="nx">protocol</span><span class="p">]</span> <span class="o">?</span> <span class="nx">https</span><span class="o">:</span><span class="c1">// : http://) + document[location][hostname] + /get_params,
</span></span></span><span class="line"><span class="cl">        <span class="nx">auth</span><span class="o">:</span> <span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">api_public_key</span><span class="o">:</span> <span class="nx">afSbep8yjnZUjq3aL010jO15Sawj2VZfdYK8uY90uxq</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">aj_lmt_doact</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">aj_lmt_dmact</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">aj_lmt_tact</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">ce_js_post</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">init_time</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">informinfo</span><span class="o">:</span> <span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">prevfid</span><span class="o">:</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">fidcnt</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">sensor_data</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span></span></span></code></pre></div></div>
<p>One function in the mess of code stood out. It seems to be hitting tons of permissions and sensors to see if the browser prompts you for each one, or simply grants or denies them without prompting. You can see from my example code near the top of this post that prompting a user for access is enough to show the icon, and that seems to be what this is doing.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">np</span><span class="o">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">        <span class="nx">t</span> <span class="o">=</span> <span class="p">[</span><span class="nx">geolocation</span><span class="p">,</span> <span class="nx">notifications</span><span class="p">,</span> <span class="nx">push</span><span class="p">,</span> <span class="nx">midi</span><span class="p">,</span> <span class="nx">camera</span><span class="p">,</span> <span class="nx">microphone</span><span class="p">,</span> <span class="nx">speaker</span><span class="p">,</span> <span class="nx">device</span><span class="o">-</span><span class="nx">info</span><span class="p">,</span> <span class="nx">background</span><span class="o">-</span><span class="nx">sync</span><span class="p">,</span> <span class="nx">bluetooth</span><span class="p">,</span> <span class="nx">persistent</span><span class="o">-</span><span class="nx">storage</span><span class="p">,</span> <span class="nx">ambient</span><span class="o">-</span><span class="nx">light</span><span class="o">-</span><span class="nx">sensor</span><span class="p">,</span> <span class="nx">accelerometer</span><span class="p">,</span> <span class="nx">gyroscope</span><span class="p">,</span> <span class="nx">magnetometer</span><span class="p">,</span> <span class="nx">clipboard</span><span class="p">,</span> <span class="nx">accessibility</span><span class="o">-</span><span class="nx">events</span><span class="p">,</span> <span class="nx">clipboard</span><span class="o">-</span><span class="nx">read</span><span class="p">,</span> <span class="nx">clipboard</span><span class="o">-</span><span class="nx">write</span><span class="p">,</span> <span class="nx">payment</span><span class="o">-</span><span class="nx">handler</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">navigator</span><span class="p">[</span><span class="nx">permissions</span><span class="p">])</span> <span class="k">return</span> <span class="mi">6</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="kd">var</span> <span class="nx">e</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">return</span> <span class="nx">navigator</span><span class="p">[</span><span class="nx">permissions</span><span class="p">][</span><span class="nx">query</span><span class="p">]({</span>
</span></span><span class="line"><span class="cl">                    <span class="nx">name</span><span class="o">:</span> <span class="nx">t</span>
</span></span><span class="line"><span class="cl">                <span class="p">})[</span><span class="nx">then</span><span class="p">](</span><span class="kd">function</span> <span class="p">(</span><span class="nx">t</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="k">switch</span> <span class="p">(</span><span class="nx">t</span><span class="p">[</span><span class="nx">state</span><span class="p">])</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="k">case</span> <span class="nx">prompt</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">                        <span class="nx">a</span><span class="p">[</span><span class="nx">e</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                    <span class="k">case</span> <span class="nx">granted</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">                        <span class="nx">a</span><span class="p">[</span><span class="nx">e</span><span class="p">]</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                    <span class="k">case</span> <span class="nx">denied</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">                        <span class="nx">a</span><span class="p">[</span><span class="nx">e</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                    <span class="k">default</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">                        <span class="nx">a</span><span class="p">[</span><span class="nx">e</span><span class="p">]</span> <span class="o">=</span> <span class="mi">5</span>
</span></span><span class="line"><span class="cl">                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="p">})[</span><span class="k">catch</span><span class="p">](</span><span class="kd">function</span> <span class="p">(</span><span class="nx">t</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nx">a</span><span class="p">[</span><span class="nx">e</span><span class="p">]</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span> <span class="o">!==</span> <span class="nx">t</span><span class="p">[</span><span class="nx">message</span><span class="p">][</span><span class="nx">indexOf</span><span class="p">](</span><span class="nx">is</span> <span class="nx">not</span> <span class="nx">a</span> <span class="nx">valid</span> <span class="kr">enum</span> <span class="nx">value</span> <span class="k">of</span> <span class="nx">type</span> <span class="nx">PermissionName</span><span class="p">)</span> <span class="o">?</span> <span class="mi">4</span> <span class="o">:</span> <span class="mi">3</span>
</span></span><span class="line"><span class="cl">                <span class="p">})</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nx">n</span> <span class="o">=</span> <span class="nx">t</span><span class="p">[</span><span class="nx">map</span><span class="p">](</span><span class="kd">function</span> <span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">t</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">return</span> <span class="nx">e</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">t</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="p">});</span>
</span></span><span class="line"><span class="cl">        <span class="nb">Promise</span><span class="p">[</span><span class="nx">all</span><span class="p">](</span><span class="nx">n</span><span class="p">)[</span><span class="nx">then</span><span class="p">](</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">bmak</span><span class="p">[</span><span class="nx">nav_perm</span><span class="p">]</span> <span class="o">=</span> <span class="nx">a</span><span class="p">[</span><span class="nx">join</span><span class="p">]()</span>
</span></span><span class="line"><span class="cl">        <span class="p">})</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">a</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="mi">7</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span></span></span></code></pre></div></div>

<h3 class="relative group">A reference to cformanalytics.com, registered to Akamai
    <div id="a-reference-to-cformanalyticscom-registered-to-akamai" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#a-reference-to-cformanalyticscom-registered-to-akamai" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>A quick <a href="https://www.godaddy.com/whois/results.aspx?domain=cformanalytics.com&amp;recaptchaResponse=03AGdBq24eyK0uRaDnfXOW-OaXJfCs4zaVCoDncffKdblhZOYPG37AFikWlmendU9wMCkvn8Wd2F6hzJnDBsK_xwDwUEmvuBvwoymf71KpUFnuJ4t_F2-FjR0Qyj6mK7-1ubyGft5lmMdmw03K_4y7OGPuDjafUjZDLSj9szXmGVYSZjXS3f9PLfS9tHPh759j5SlAI79CDIFmoCblvlaNnuNBd7o4Bx0RwZaTtV1X1a8pvGwxQf5h4VzO6VWXdCcfVSApJLCQSjgZXLfXe_ehbvbyMz8e90CVwf7W-qFZok671nS1keCkz5GhM7gPe3-G-S2CICtYvnvkBQHQ1xONyQul9XNAqiMDDJrW-Wx9aJzRnb9hgLYP1cpzzWO9czyvxCySwNopi7_X"  target="_blank" rel="noreferrer">whois</a> on <code>cformanalytics.com</code> from the <code>cf_url</code> key above suggests it belongs to Akamai&hellip; they just keep coming up.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">Domain Name: CFORMANALYTICS.COM
Registry Domain ID: 1897860898_DOMAIN_COM-VRSN
Registrar WHOIS Server: whois.akamai.com
Registrar URL: http://www.akamai.com
Updated Date: 2020-04-07T18:35:33Z
Creation Date: 2015-01-24T01:00:53Z
Registry Expiry Date: 2022-01-24T01:00:53Z
Registrar: Akamai Technologies, Inc.
Registrar IANA ID: 2480
Registrar Abuse Contact Email: registrar-abuse@akamai.com
Registrar Abuse Contact Phone: +1.6174443076</code></pre></div>

<h2 class="relative group">It seems safe to assume it&rsquo;s Akamai
    <div id="it-seems-safe-to-assume-its-akamai" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#it-seems-safe-to-assume-its-akamai" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>At this point, I feel fairly confident it&rsquo;s Akamai&rsquo;s script, and it probably <em>is</em> some kind of bot detection service. I&rsquo;m not sure why a bot detection service would need to check sensors, but maybe it&rsquo;s just one signal in a myriad of signals to detect if a requestor is a human or a bot? Or maybe it&rsquo;s being used as part of fingerprinting to track and individually identify visitors?</p>
<p>I spent a little time digging around the Akamai site, and while most of their documentation is locked behind having an actual account, I stumbled on <a href="https://developer.akamai.com/tools/sdk/bot-manager"  target="_blank" rel="noreferrer">this</a> regarding their mobile device capabilities:</p>
<blockquote><p>The Akamai Bot Manager Premier software development kit (BMP SDK) takes the fundamental technology of <a href="https://developer.akamai.com/akamai-bot-manager"  target="_blank" rel="noreferrer">Akamai Bot Manager</a> and applies it to native mobile apps. The BMP SDK collects behavioral data while the user is interacting with the application. This behavioral data, also known as sensor data, includes the device characteristics, device orientation, accelerometer data, touch events, etc. Akamai BMP SDK provides a simple API to detect bot activities and defend against malicious bot and account takeover.</p>
</blockquote><p>It includes this graphic, which just seems to reinforce the above description, that their bot detection service(s) uses sensor data like accelerometer capabilities to determine whether a requestor is a bot or not.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/websites-requesting-access-to-motion-sensors/infographic_overview_human-request_redo.png"
    width="1200"
      height="561"></figure>
<p>source: <a href="https://developer.akamai.com/tools/sdk/bot-manager"  target="_blank" rel="noreferrer">Bot Manager Premier SDK</a></p>
<p>I feel fairly confident that&rsquo;s the answer, but it leaves at least one more question&hellip;</p>

<h3 class="relative group">But why do they expect desktops to have an accelerometer?
    <div id="but-why-do-they-expect-desktops-to-have-an-accelerometer" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#but-why-do-they-expect-desktops-to-have-an-accelerometer" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>I think maybe it&rsquo;s an oversight, but I can&rsquo;t really prove it. Akamai has every incentive to keep their presence hidden. From how well they seem to have obfuscated their files, I think they&rsquo;d agree. Since I can&rsquo;t prove anything, I&rsquo;ll try to make some educated guesses.</p>

<h3 class="relative group">Guess 1: Their script doesn&rsquo;t handle responsive websites correctly
    <div id="guess-1-their-script-doesnt-handle-responsive-websites-correctly" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#guess-1-their-script-doesnt-handle-responsive-websites-correctly" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p><a href="https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Responsive_Design"  target="_blank" rel="noreferrer">Most websites are responsive</a>, meaning they use CSS to adjust dynamically for desktop devices with large screens, tablets with medium screens, and mobile devices with tiny screens. So unless Akamai detects the screen size and loads a different obfuscated file for each (and what about mobile users that choose the &ldquo;view desktop site&rdquo; option, or tablets with a very high resolution?), odds are the same script that runs on mobile devices is running on desktops.</p>

<h3 class="relative group">Guess 2: They provide two scripts, but companies only implement one
    <div id="guess-2-they-provide-two-scripts-but-companies-only-implement-one" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#guess-2-they-provide-two-scripts-but-companies-only-implement-one" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Or perhaps their <a href="https://developer.akamai.com/tools/sdk/bot-manager"  target="_blank" rel="noreferrer">Akamai Bot Manager Premier SDK</a> service that builds on the basic &ldquo;<a href="https://developer.akamai.com/akamai-bot-manager"  target="_blank" rel="noreferrer">Akamai Bot Manager</a>&rdquo; service is only meant to be loaded on mobile versions of sites, leaving the onus of implementation to individual sites. I can imagine most businesses, upon hearing that they have to implement two libraries, and then realizing that one is just a more enhanced version of the other, instructing the development team to just reference the &ldquo;premier&rdquo; SDK everywhere.</p>
<p>If anyone hears differently or knows more, please share in the comments. I&rsquo;ve been updating this periodically over the last 6 months. I think there&rsquo;s a lot of inquiring minds who&rsquo;d love to know more!</p>
]]></content:encoded><media:content url="https://grantwinney.com/websites-requesting-access-to-motion-sensors/feature.webp" medium="image" type="image/webp"/></item><item><title>Hands-on Ansible, using two DigitalOcean Ubuntu droplets</title><link>https://grantwinney.com/hands-on-ansible-using-digitalocean-ubuntu-droplets/</link><pubDate>Wed, 18 Dec 2019 22:21:00 +0000</pubDate><guid>https://grantwinney.com/hands-on-ansible-using-digitalocean-ubuntu-droplets/</guid><description>Today I&amp;rsquo;m wrapping my head around a build tool called Ansible, used for deploying machines in a scriptable, repeatable manner. Follow along as I step through an excellent tutorial from DigitalOcean, applying what I learn to a couple DO Ubuntu VMs&amp;hellip; the $5/mo ones - nothing fancy needed!</description><content:encoded><![CDATA[<p>For the uninitiated, Docker allows you to build VMs in a predictable, repeatable manner as a series of layers called images. Automation is where it&rsquo;s at – if you think you&rsquo;ll have to deploy a box several times, your future self will thank you for scripting it out.</p>
<p>I only started learning about it recently, and today I&rsquo;m wrapping my head around another tool for building machines called Ansible. Note that Ansible is not an alternative for Docker, but <a href="https://www.ansible.com/integrations/containers/docker"  target="_blank" rel="noreferrer">it can actually complement it</a>. I&rsquo;ll post some resources later, but right now I&rsquo;m just stepping through a tutorial I found on DigitalOcean. But first&hellip;</p>

<h2 class="relative group">Create two basic Ubuntu VMs
    <div id="create-two-basic-ubuntu-vms" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#create-two-basic-ubuntu-vms" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://m.do.co/c/448f25462030"  target="_blank" rel="noreferrer">Create a DigitalOcean account</a> and spin up two Ubuntu droplets (the green &ldquo;create&rdquo; button in the upper-right). A bottom-tier machine runs $5/mo, so even if you play with these for the rest of the day it&rsquo;ll only run ya 33¢. 🤑</p>
<p>Normally I&rsquo;d leave <em>&ldquo;SSH keys&rdquo;</em> selected for authentication, but for now you can just select <em>&ldquo;one-time password&rdquo;</em>. You&rsquo;ll get an email for each machine with a temp password, and then you can just open a terminal, type in <code>ssh root@111.111.111.111</code> using whatever IP address you&rsquo;re assigned, and change the password.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/hands-on-ansible-using-digitalocean-ubuntu-droplets/ubuntu-vm-at-do-1.png"
    width="1006"
      height="772"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/hands-on-ansible-using-digitalocean-ubuntu-droplets/ansible-sandbox.png"
    width="577"
      height="542"></figure>

<h2 class="relative group">Install Ansible on one of them
    <div id="install-ansible-on-one-of-them" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#install-ansible-on-one-of-them" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>After you&rsquo;ve logged into both machines, <a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-ansible-on-ubuntu-18-04"  target="_blank" rel="noreferrer">follow along with this tutorial</a>. Pick one machine to be the &ldquo;controller node&rdquo;, where you&rsquo;ll install Ansible. The other machine will be the &ldquo;host&rdquo; that the controller node will eventually send commands to. Everything is in the tutorial.</p>

<h3 class="relative group">Setup the inventory (hosts file)
    <div id="setup-the-inventory-hosts-file" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#setup-the-inventory-hosts-file" aria-label="Anchor">#</a>
    </span>
    
</h3>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/hands-on-ansible-using-digitalocean-ubuntu-droplets/1-ansible-hosts-setup-1.png"
    width="1232"
      height="337"></figure>
<p>After installing Ansible, I setup the /etc/ansible/hosts file&hellip;</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/hands-on-ansible-using-digitalocean-ubuntu-droplets/2-ansible-inventory.png"
    width="1223"
      height="217"></figure>
<p>&hellip; and then verified it with the ansible-inventory command</p>

<h3 class="relative group">Create an SSH key on the controller node
    <div id="create-an-ssh-key-on-the-controller-node" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#create-an-ssh-key-on-the-controller-node" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>You&rsquo;ll need to create an SSH keypair on the same machine where you installed Ansible (the controller node). Just type <code>ssh-keygen</code>, accept all the defaults, then use <code>ssh-copy-id</code> to copy the public key you just created to the other machine (the host). That allows the node controller to communicate with the host.</p>
<p>Follow <a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-ssh-keys-on-ubuntu-1804#step-1-%E2%80%94-create-the-rsa-key-pair"  target="_blank" rel="noreferrer">step 1</a> and <a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-ssh-keys-on-ubuntu-1804#step-2-%E2%80%94-copy-the-public-key-to-ubuntu-server"  target="_blank" rel="noreferrer">step 2</a>, both from <a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-ssh-keys-on-ubuntu-1804"  target="_blank" rel="noreferrer">this tutorial</a>.</p>
<p>Here&rsquo;s some output from my node controller, as I was running commands. I color-coded it to make it easier to understand, but basically&hellip;</p>
<ul>
<li>I tried pinging the host, which failed because SSH wasn&rsquo;t setup yet. <em>(red)</em></li>
<li>I created an SSH keypair on the controller node. <em>(green)</em></li>
<li>I verified that the keypair was created, and <code>id_rsa.pub</code> was present. <em>(purple)</em></li>
<li>I copied the public key from the node controller to the host. <em>(orange)</em></li>
<li>I ran the first command again, to ping the host. Success! <em>(blue)</em></li>
</ul>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/hands-on-ansible-using-digitalocean-ubuntu-droplets/ansible-node-controller-setup.png"
    width="814"
      height="981"></figure>

<h3 class="relative group">Verify that you can run Ansible commands
    <div id="verify-that-you-can-run-ansible-commands" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#verify-that-you-can-run-ansible-commands" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The authors of the tutorial suggest running the following command from the controller node, just to see that you can run commands against the host(s) you setup - although the <code>ping</code> command above already did that.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ansible all -a &#34;df -h&#34; -u root</span></span></code></pre></div></div>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/hands-on-ansible-using-digitalocean-ubuntu-droplets/ansible-df-h.png"
    width="1132"
      height="283"></figure>
<p>Checking host disk usage locally, and remotely from the controller</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/hands-on-ansible-using-digitalocean-ubuntu-droplets/change-time.png"
    width="1244"
      height="253"></figure>
<p>Checking the host date from the controller, before and after changing the host timezone</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/hands-on-ansible-using-digitalocean-ubuntu-droplets/host-uptime.png"
    width="1205"
      height="182"></figure>
<p>Checking the uptime on a host machine</p>

<h2 class="relative group">What&rsquo;s next?
    <div id="whats-next" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whats-next" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Okay, that wasn&rsquo;t nearly as bad as I thought it&rsquo;d be! If you were doing this in a production environment, you&rsquo;d want to do <em>way</em> more - creating a non-root sudo user and configuring UFW to allow only the ports you need (like 22) come to mind.</p>
<p>Now that I&rsquo;ve got the servers setup and communicating, I plan on going through the rest of <a href="https://www.digitalocean.com/community/users/erikaheidi"  target="_blank" rel="noreferrer">Erika&rsquo;s guides</a>. I&rsquo;ll save these for another day though.</p>
<ul>
<li><a href="https://www.digitalocean.com/community/tutorials/how-to-use-ansible-to-automate-initial-server-setup-on-ubuntu-18-04"  target="_blank" rel="noreferrer">How to Use Ansible to Automate Initial Server Setup on Ubuntu 18.04 | DigitalOcean</a></li>
<li><a href="https://www.digitalocean.com/community/tutorials/configuration-management-101-writing-ansible-playbooks"  target="_blank" rel="noreferrer">Configuration Management 101: Writing Ansible Playbooks | DigitalOcean</a></li>
<li><a href="https://www.digitalocean.com/community/cheatsheets/how-to-use-ansible-cheat-sheet-guide"  target="_blank" rel="noreferrer">How to Use Ansible: A Reference Guide | DigitalOcean</a></li>
</ul>

<h3 class="relative group">Other Resources
    <div id="other-resources" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#other-resources" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>I said I&rsquo;d post other resources, and I don&rsquo;t want to break such an important promise. So.. here&rsquo;s the official <a href="https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#latest-releases-via-apt-ubuntu"  target="_blank" rel="noreferrer">ansible docs</a>. I find most of the posts on DO to be of high quality, but I&rsquo;m not sure anyone&rsquo;s written guides for other flavors of Unix. If you&rsquo;re not using Ubuntu, the docs have steps for quite a few other systems, so check them out.</p>
<p>If you have access to a <a href="https://www.skillsoft.com/platform-solution/percipio/"  target="_blank" rel="noreferrer">Percipio</a> account, I found the courses created by Joseph Khoury last year to be pretty easy to understand. I have access to it through my workplace, but I don&rsquo;t know if you can access it as an individual like Pluralsight et al.</p>
<p>And of course there&rsquo;s YouTube, a semi-popular video streaming site.</p>
<ul>
<li><a href="https://www.youtube.com/watch?v=wgQ3rHFTM4E"  target="_blank" rel="noreferrer">Ansible Playbook Tutorial For Beginners | Simplilearn - YouTube</a> <em>(quick overview)</em></li>
<li><a href="https://www.youtube.com/watch?v=icR-df2Olm8&amp;embeds_referring_euri=https%3A%2F%2Fgrantwinney.com%2F"  target="_blank" rel="noreferrer">Ansible - A Beginner&rsquo;s Tutorial, Part 1 - YouTube</a> <em>(this is a 5-part series)</em></li>
</ul>
]]></content:encoded><media:content url="https://grantwinney.com/hands-on-ansible-using-digitalocean-ubuntu-droplets/feature.webp" medium="image" type="image/webp"/></item><item><title>Replacing Gmail (and its office suite) with Mailbox.org</title><link>https://grantwinney.com/replacing-gmail-with-mailbox-org/</link><pubDate>Mon, 09 Dec 2019 23:44:00 +0000</pubDate><guid>https://grantwinney.com/replacing-gmail-with-mailbox-org/</guid><description>Google provides some amazing tools, but at what cost to privacy? One of the biggest blockers in eliminating them has been finding a reliable and affordable replacement for email (and ideally, calendar, documents, tasks, etc too). Well I may have finally found it, in Mailbox.org.</description><content:encoded><![CDATA[<p>I had a gmail account for 10 years, and was perfectly happy but for the nagging issue of privacy and <a href="https://www.nytimes.com/2019/11/11/business/google-ascension-health-data.html"  target="_blank" rel="noreferrer">gobbling up</a> <a href="https://www.cnbc.com/2019/11/01/google-to-acquire-fitbit-valuing-the-smartwatch-maker-at-about-2point1-billion.html"  target="_blank" rel="noreferrer">our data</a>. Finding a reliable and affordable replacement with a full suite of office tools wasn&rsquo;t easy though.</p>
<p>After trying a few others, I discovered <a href="https://mailbox.org/en/"  target="_blank" rel="noreferrer">mailbox.org</a>, an email provider that values enhanced security and privacy over serving up ads. After spending the last week getting it setup the way I want it, I think I&rsquo;ve finally found my replacement for gmail et al.</p>
<hr>

<h2 class="relative group">Getting Started
    <div id="getting-started" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#getting-started" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://register.mailbox.org/en"  target="_blank" rel="noreferrer">Register for an account</a> and off you go.. easy peasy. Once you&rsquo;re in, click the gear icon and change your features. There&rsquo;s a nice little slider to increase mail and cloud (document) storage, and it shows what other features are enabled to. Pay is &ldquo;as you go&rdquo;, and there&rsquo;s quite a few options depending on just how much of a geek you are&hellip; or what your privacy requirements are.</p>
<p>As you can see, I have a whopping $2 in my account, good for 2 months of basic usage. The free 30 day account ain&rsquo;t bad for trying it out, but hey I&rsquo;m rolling in washingtons over here. 💸</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/replacing-gmail-with-mailbox-org/mail.org-contract-1.png"
    width="1008"
      height="819"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/replacing-gmail-with-mailbox-org/mail.org-contract-2.png"
    width="1006"
      height="796"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/replacing-gmail-with-mailbox-org/mailbox.org-add-credit.png"
    width="1005"
      height="938"></figure>
<hr>

<h2 class="relative group">Using Your Own Domain
    <div id="using-your-own-domain" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#using-your-own-domain" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Setup is far easier if you just use the default mailbox.org domain, buuuut.. I love to complicate things. Seriously though, after changing gmail.com to protonmail.com and now to mailbox.org (and being realistic that I may want to change services again someday, and since I already have a domain for this blog), it just makes sense to make this the last email address change I&rsquo;ll ever have to make.</p>
<p>Mailbox.org provides a detailed doc on <a href="https://kb.mailbox.org/en/private/custom-domains/using-e-mails-with-a-custom-domain/"  target="_blank" rel="noreferrer">setting up e-mail addresses for your domain</a>. You&rsquo;ll definitely have to be comfortable configuring your own domain setup, but it&rsquo;s not <em>that</em> hard if you&rsquo;re willing to do some research. I can only share what I do personally.</p>
<p>My domain name is currently registered with Namecheap, which I configured to <a href="https://www.digitalocean.com/community/tutorials/how-to-point-to-digitalocean-nameservers-from-common-domain-registrars"  target="_blank" rel="noreferrer">forward all traffic to the DigitalOcean nameservers</a>, where I host this blog. Everything (web traffic, emails, etc) goes through Namecheap to DO, where the actual config is done.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/replacing-gmail-with-mailbox-org/namecheap-forwarding-1.png"
    width="968"
      height="415"></figure>
<p>The first thing you&rsquo;ll see in Mailbox.org when you try to setup an alias to your own domain is a warning telling you to add a TXT record to your DNS settings. They have to do that to verify you have access to the domain, otherwise <em>anyone</em> could claim <em>any</em> domain for their own. <a href="https://www.youtube.com/watch?v=9wrEEd1ajz4&amp;t=28"  target="_blank" rel="noreferrer">That would be Bad</a>.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/replacing-gmail-with-mailbox-org/add-txt-record-warning.png"
    width="756"
      height="324"></figure>
<p>After you&rsquo;ve proven you&rsquo;re the owner, you see this instead. Now you can add some MX records to your DNS settings, directing all traffic from (in my case) DigitalOcean to Mailbox.org&rsquo;s several servers (note the &ldquo;priority&rdquo; setting). 🐢🐢🐢🌎</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/replacing-gmail-with-mailbox-org/add-mx-records-1.png"
    width="779"
      height="302"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/replacing-gmail-with-mailbox-org/add-mx-records-2-1.png"
    width="1092"
      height="267"></figure>
<p><em>But wait, there&rsquo;s more!</em> You may have proven who you are to Mailbox.org, but that&rsquo;s not enough for other providers like Gmail, who will mark your messages as possible spam and unceremoniously dump them in the recipient&rsquo;s spam folder. Fun.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/replacing-gmail-with-mailbox-org/spf-04.png"
    width="705"
      height="126"></figure>
<p>To fix that, you need another DNS entry. Use a tool like <a href="https://mxtoolbox.com/SuperTool.aspx?action=spf"  target="_blank" rel="noreferrer">this one</a> to check for an SPF record - if you don&rsquo;t find one, you need one, and you can <a href="https://kb.mailbox.org/en/private/custom-domains/using-e-mails-with-a-custom-domain/#Usinge-mailaddressesofyourdomain-Step3:HowtosettheSPFrecords"  target="_blank" rel="noreferrer">find instructions here</a>.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/replacing-gmail-with-mailbox-org/spf-2.png"
    width="1469"
      height="283"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/replacing-gmail-with-mailbox-org/spf-1.png"
    width="1088"
      height="460"></figure>
<p>Give it time to take effect, then check it with the website again, and everything should be much greener and happier. Either this tells Gmail how to check Mailbox.org to verify you, or it causes Mailbox.org to attach some meta data to requests that Gmail uses, or&hellip; whatever. I don&rsquo;t care, my stuff ain&rsquo;t going to spam anymore. 💚</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/replacing-gmail-with-mailbox-org/spf-3.png"
    width="1509"
      height="917"></figure>
<p>Here&rsquo;s my full DNS records, which might help someone. Not sure why the bottom 3 NS records are in there, but I think they were there by default so I&rsquo;m leaving &rsquo;em. There&rsquo;s also a reference in their docs to adding a record for <a href="https://blog.woodpecker.co/cold-email/spf-dkim/#dkim"  target="_blank" rel="noreferrer">DKIM</a>, but that doesn&rsquo;t seem to be necessary&hellip; maybe I&rsquo;ll revisit it later.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/replacing-gmail-with-mailbox-org/spf-5.png"
    width="1358"
      height="908"></figure>
<hr>

<h2 class="relative group">Syncing Mail, Calendar, Contacts to Your Phone
    <div id="syncing-mail-calendar-contacts-to-your-phone" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#syncing-mail-calendar-contacts-to-your-phone" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>To sync everything with Android, you&rsquo;ll need to take advantage of the CardDAV and CalDAV protocols, which are standards for synchronizing contacts and calendar/todo items, respectively. The <a href="https://play.google.com/store/apps/details?id=at.bitfire.davdroid"  target="_blank" rel="noreferrer">DAVx⁵</a> app can do all that for you - it&rsquo;s working great so far.</p>

<h3 class="relative group">Install DAVx⁵
    <div id="install-davx" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#install-davx" aria-label="Anchor">#</a>
    </span>
    
</h3>
<ol>
<li>Disable &ldquo;battery optimization&rdquo; per the app&rsquo;s suggestion, and allow the app to run in the background, otherwise I don&rsquo;t see how it&rsquo;ll allow updates.</li>
<li>On the right bottom, press the orange &ldquo;+&rdquo; symbol.</li>
<li>Choose “Login with URL and user name”.<br>
base url: <a href="https://dav.mailbox.org/"  target="_blank" rel="noreferrer">https://dav.mailbox.org</a><br>
user name: your mailbox.org primary email<br>
password: your mailbox.org password <em>(not thrilled with having to supply my password instead of a per-app code where permissions can be restricted)</em></li>
<li>Give the name an account if it prompts you (I can&rsquo;t remember..).</li>
<li>You should be prompted to allow certain permissions, like calendar, tasks and contacts. Might as well, otherwise it probably can&rsquo;t do what it needs to do.</li>
<li>You should see CardDAV (for syncing contacts) and CalDAV (for syncing calendars and tasks).</li>
<li>Choose the folders to sync.</li>
</ol>

<h3 class="relative group">Install ICSx⁵
    <div id="install-icsx" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#install-icsx" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>While DAVx⁵ by itself can sync calendars, you&rsquo;ll need one more piece if you want to subscribe to webcal calendars (with an .ics extension), and that&rsquo;s <a href="https://play.google.com/store/apps/details?id=at.bitfire.icsdroid"  target="_blank" rel="noreferrer">ICSx⁵</a>. You can <a href="https://www.davx5.com/faq/subscribe-ics-file"  target="_blank" rel="noreferrer">read about the difference in protocols</a> here, but in a nutshell DAVx⁵ lets you sync events (2-way), and ICSx⁵ lets you subscribe to events (1-way).</p>

<h3 class="relative group">Test mail
    <div id="test-mail" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#test-mail" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>When I used the default &ldquo;email&rdquo; app that came with the flavor of Android running on a Huawei phone, it automatically sync&rsquo;d up with emails based on whatever magic DAVx5 does. After changing phones and losing access to whatever default app that was, I changed over to <a href="https://play.google.com/store/apps/details?id=me.bluemail.mail"  target="_blank" rel="noreferrer">BlueMail</a> and like it so far.</p>
<p>You&rsquo;ll have to enter your email and password (I really wish they implemented oauth or similar, to grant them access solely to email), but according to BlueMail, that data never lands in their hands, nor do emails land on their servers:</p>
<ul>
<li>BlueMail uses SSL, STARTTLS and OAuth enforcing certificate check by default.</li>
<li>BlueMail sends and receives emails directly to and from the user&rsquo;s email server.</li>
<li>Passwords are never transferred to the BlueMail’s servers.</li>
<li>Emails are never stored on BlueMail&rsquo;s servers.</li>
</ul>

<h3 class="relative group">Test the Calendar and Contact apps
    <div id="test-the-calendar-and-contact-apps" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#test-the-calendar-and-contact-apps" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>You should be able to use the Google Calendar app like you would for any other calendar, instead of the ugly default Android calendar app. Create an &ldquo;test&rdquo; calendar entry on Mailbox.org, let it sync (you can adjust settings in the mobile app), and make sure it carried over. Same goes for contacts too&hellip;</p>
<p>Don&rsquo;t forget to go into the Google Calendar app settings (if you use it) and uncheck the calendar built into Gmail, otherwise you&rsquo;ll have two personal calendars showing. I&rsquo;ve been using <a href="https://play.google.com/store/apps/details?id=com.appgenix.bizcal"  target="_blank" rel="noreferrer">Business Calendar 2</a> - it&rsquo;s got more features than the stock app.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/replacing-gmail-with-mailbox-org/calendar-test-1.png"
    width="1618"
      height="679"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="400"
    src="/replacing-gmail-with-mailbox-org/calendar-test-3.png"
    width="1080"
      height="1920"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="400"
    src="/replacing-gmail-with-mailbox-org/calendar-test-4.png"
    width="1080"
      height="1920"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/replacing-gmail-with-mailbox-org/contact-test-1.png"
    width="1509"
      height="897"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="400"
    src="/replacing-gmail-with-mailbox-org/contact-test-2.png"
    width="1080"
      height="954"></figure>
<hr>

<h2 class="relative group">Migrating to Mailbox.org
    <div id="migrating-to-mailboxorg" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#migrating-to-mailboxorg" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you decide to make the move, here&rsquo;s how. Ymmv of course.</p>

<h3 class="relative group">Migrate Calendar
    <div id="migrate-calendar" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#migrate-calendar" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Export your calendar from Gmail and upload the file into Mailbox.org to import it. Although you can&rsquo;t delete your primary Gmail calendar, you can click the &ldquo;Delete&rdquo; button to wipe out all events in one go.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/replacing-gmail-with-mailbox-org/calendar-export.png"
    width="961"
      height="687"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/replacing-gmail-with-mailbox-org/mb-calendar.png"
    width="790"
      height="737"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/replacing-gmail-with-mailbox-org/calendar-delete.png"
    width="1270"
      height="582"></figure>

<h3 class="relative group">Migrate Contacts
    <div id="migrate-contacts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#migrate-contacts" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Export your contacts in CSV format, and Mailbox.org can consume them pretty easily. There was a little cleanup to do, but nothing major.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/replacing-gmail-with-mailbox-org/gmail-contacts.png"
    width="1088"
      height="694"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/replacing-gmail-with-mailbox-org/mb-contacts.png"
    width="1042"
      height="741"></figure>

<h3 class="relative group">Migrate Gmail
    <div id="migrate-gmail" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#migrate-gmail" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>There&rsquo;s no getting around this one - you&rsquo;ll have to login to each billing provider and service and update your email. If you don&rsquo;t want to send out a blanket email to everyone in your address book, or if you&rsquo;re worried you might&rsquo;ve missed a few services, here&rsquo;s 3 filters I setup that might help you too:</p>
<ul>
<li>After deleting my calendar events, the system sends daily reminders that I have absolutely nothing planned. Don&rsquo;t need those..</li>
<li>Everything sent to me that <em>isn&rsquo;t</em> flagged as spam is forwarded to my new email.</li>
<li>Everything that&rsquo;s spam is deleted.</li>
</ul>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/replacing-gmail-with-mailbox-org/gmail-forwardemails.jpg"
    width="943"
      height="380"></figure>
<p>You probably want to wait 6 months to make sure there wasn&rsquo;t some random bill or service you forgot to change, but when you&rsquo;re ready, you can <a href="https://myaccount.google.com/deleteservices"  target="_blank" rel="noreferrer">delete your Gmail account</a> while leaving the rest of your Google account intact. There might be someone you know who tries sending email and it fails, but then you probably weren&rsquo;t that close anyway. You&rsquo;re just doing all kinds of purging today, aintcha?</p>
<p>Be sure to <a href="https://takeout.google.com/?hl=en"  target="_blank" rel="noreferrer">download your data first</a>, at least your emails&hellip; to their credit, Google does a nice job of making it easy for you to download all your data for all their services.</p>
<hr>

<h2 class="relative group">Other Features (disposable addresses, security, etc)
    <div id="other-features-disposable-addresses-security-etc" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#other-features-disposable-addresses-security-etc" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>They also provide a bunch of other awesome sauce right out of the box. If you&rsquo;ve ever pondered what happens to your digital life when you pass away, well, so have they apparently. I don&rsquo;t know how they confirm someone&rsquo;s died, but presumably once they receive a request to release it, they can (at your instruction) grant it or delete it. Not sure how it&rsquo;d hold up in court, but it&rsquo;s interesting.</p>
<p>The built-in disposable addresses are awesome as well. I spent a few hours porting my online accounts to my new email, and closed quite a few that I hadn&rsquo;t used in a long time. That is an <em>incredibly</em> painful and frustrating experience, especially when there&rsquo;s no obvious way to even close the account. So, at the very least, I spun up a disposable address, changed it in those services and confirmed it, then deleted the address. At that point, they&rsquo;re effectively dead to me. 👍</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/replacing-gmail-with-mailbox-org/aliases.png"
    width="1033"
      height="712"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/replacing-gmail-with-mailbox-org/digital-legacy.png"
    width="1014"
      height="880"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/replacing-gmail-with-mailbox-org/disposable-addresses.png"
    width="1008"
      height="524"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/replacing-gmail-with-mailbox-org/hack-alert.png"
    width="1020"
      height="795"></figure>
<p>While enabling 2FA, I noticed this message. Since SMS is the <a href="https://authy.com/blog/security-of-sms-for-2fa-what-are-your-options/"  target="_blank" rel="noreferrer">least</a> <a href="https://www.theverge.com/2017/9/18/16328172/sms-two-factor-authentication-hack-password-bitcoin"  target="_blank" rel="noreferrer">secure</a> way to 2FA (and also won&rsquo;t work if you go outside your coverage area), I was actually glad to see it.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/replacing-gmail-with-mailbox-org/no-sms-2fa.png"
    width="1078"
      height="79"></figure>
<p>So far I&rsquo;m super happy with this service, and consider $1/mo a steal for an ad-free, creepy-dig-through-my-data-free experience. If I need more aliases, cloud space, or convince my wife to jump on board with the &ldquo;team&rdquo; settings, I&rsquo;ll gladly play $2.50/mo for it too.</p>
]]></content:encoded><media:content url="https://grantwinney.com/replacing-gmail-with-mailbox-org/feature.webp" medium="image" type="image/webp"/></item><item><title>Add and subtract time from a DateTime structure in Erlang</title><link>https://grantwinney.com/how-do-i-add-seconds-minutes-or-hours-to-a-datetime-structure-in-erlang/</link><pubDate>Sun, 01 Dec 2019 01:51:59 +0000</pubDate><guid>https://grantwinney.com/how-do-i-add-seconds-minutes-or-hours-to-a-datetime-structure-in-erlang/</guid><description>I was trying to add times in Erlang, but couldn&amp;rsquo;t find an existing function, so I wrote my own.</description><content:encoded><![CDATA[<p>Some languages, like Ruby, give you 12 ways to do the same thing. Other languages, like Erlang, make it tough to find 1 way to do something.</p>
<p>Awhile back, I was trying to add a period of time to an existing <code>DateTime</code> value (in <code>{{Y,M,D},{H,m,s}}</code> format), but I couldn&rsquo;t find a function (such as in the <code>Calendar</code> module) that allowed me to manipulate a <code>DateTime</code> value directly.</p>
<p>If you&rsquo;re looking too, the <code>Calendar</code> module can be used to convert a <code>DateTime</code> to seconds, which makes it easier to add seconds, minutes, hours, etc.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="nv">Date</span> <span class="o">=</span> <span class="p">{{</span><span class="mi">2018</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">14</span><span class="p">},{</span><span class="mi">13</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span><span class="mi">25</span><span class="p">}}.</span>
</span></span><span class="line"><span class="cl"><span class="nv">DateInSec</span> <span class="o">=</span> <span class="nn">calendar</span><span class="p">:</span><span class="nf">datetime_to_gregorian_seconds</span><span class="p">(</span><span class="nv">Date</span><span class="p">).</span>  <span class="c">% 63701471425
</span></span></span><span class="line"><span class="cl"><span class="nv">NewDateInSec</span> <span class="o">=</span> <span class="nv">DateInSec</span> <span class="o">+</span> <span class="mi">10</span><span class="p">.</span>                             <span class="c">% 63701471435
</span></span></span><span class="line"><span class="cl"><span class="nn">calendar</span><span class="p">:</span><span class="nf">gregorian_seconds_to_datetime</span><span class="p">(</span><span class="nv">NewDateInSec</span><span class="p">).</span>      <span class="err">%</span> <span class="p">{{</span><span class="mi">2018</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">14</span><span class="p">},{</span><span class="mi">13</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span><span class="mi">35</span><span class="p">}}</span></span></span></code></pre></div></div>
<p>Adding 10 seconds</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="nv">Date</span> <span class="o">=</span> <span class="p">{{</span><span class="mi">2018</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">14</span><span class="p">},{</span><span class="mi">13</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span><span class="mi">25</span><span class="p">}}.</span>
</span></span><span class="line"><span class="cl"><span class="nv">DateInSec</span> <span class="o">=</span> <span class="nn">calendar</span><span class="p">:</span><span class="nf">datetime_to_gregorian_seconds</span><span class="p">(</span><span class="nv">Date</span><span class="p">).</span>  <span class="c">% 63701471425
</span></span></span><span class="line"><span class="cl"><span class="nv">NewDateInSec</span> <span class="o">=</span> <span class="nv">DateInSec</span> <span class="o">+</span> <span class="p">(</span><span class="mi">10</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">60</span><span class="p">).</span>                 <span class="c">% 63701507425 (10 hours)
</span></span></span><span class="line"><span class="cl"><span class="nn">calendar</span><span class="p">:</span><span class="nf">gregorian_seconds_to_datetime</span><span class="p">(</span><span class="nv">NewDateInSec</span><span class="p">).</span>      <span class="err">%</span> <span class="p">{{</span><span class="mi">2018</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">14</span><span class="p">},{</span><span class="mi">23</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span><span class="mi">25</span><span class="p">}}</span></span></span></code></pre></div></div>
<p>Adding 10 minutes and 10 hours, with a little math</p>
<p>To make life easier, I created a function to add additional time to (or subtract time from) an existing <code>DateTime</code>:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">type</span> <span class="n">datetime</span><span class="p">()</span> <span class="p">::</span> <span class="p">{{</span><span class="n">non_neg_integer</span><span class="p">(),</span> <span class="n">pos_integer</span><span class="p">(),</span> <span class="n">pos_integer</span><span class="p">()},</span>
</span></span><span class="line"><span class="cl">                     <span class="p">{</span><span class="n">non_neg_integer</span><span class="p">(),</span> <span class="n">non_neg_integer</span><span class="p">(),</span> <span class="n">non_neg_integer</span><span class="p">()}}.</span>
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">type</span> <span class="n">timespan</span><span class="p">()</span> <span class="p">::</span> <span class="p">{</span><span class="n">integer</span><span class="p">(),</span> <span class="n">integer</span><span class="p">(),</span> <span class="n">integer</span><span class="p">()}.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">add_time_to_datetime</span><span class="p">(</span><span class="n">datetime</span><span class="p">(),</span> <span class="n">timespan</span><span class="p">())</span> <span class="o">-&gt;</span> <span class="n">datetime</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">add_time_to_datetime</span><span class="p">(</span><span class="nv">Date</span><span class="p">,</span> <span class="p">{</span><span class="nv">Hour</span><span class="p">,</span> <span class="nv">Min</span><span class="p">,</span> <span class="nv">Sec</span><span class="p">})</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">DateInSeconds</span> <span class="o">=</span> <span class="nn">calendar</span><span class="p">:</span><span class="nf">datetime_to_gregorian_seconds</span><span class="p">(</span><span class="nv">Date</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nv">NewDateInSeconds</span> <span class="o">=</span> <span class="nv">DateInSeconds</span> <span class="o">+</span> <span class="p">(</span><span class="nv">Hour</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">60</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="nv">Min</span> <span class="o">*</span> <span class="mi">60</span><span class="p">)</span> <span class="o">+</span> <span class="nv">Sec</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nn">calendar</span><span class="p">:</span><span class="nf">gregorian_seconds_to_datetime</span><span class="p">(</span><span class="nv">NewDateInSeconds</span><span class="p">).</span></span></span></code></pre></div></div>
<p>This is trivial to write, but then it&rsquo;s trivial for them to just include too. Add it to the things I miss in a modern framework like .NET.</p>
]]></content:encoded><media:content url="https://grantwinney.com/how-do-i-add-seconds-minutes-or-hours-to-a-datetime-structure-in-erlang/feature.webp" medium="image" type="image/webp"/></item><item><title>Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement</title><link>https://grantwinney.com/only-assignment-call-increment-decrement-await-and-new-object-expressions-can-be-used-as-a-statement/</link><pubDate>Thu, 14 Nov 2019 17:25:00 +0000</pubDate><guid>https://grantwinney.com/only-assignment-call-increment-decrement-await-and-new-object-expressions-can-be-used-as-a-statement/</guid><description>This error might look a little cryptic at first glance, but it&amp;rsquo;s fairly descriptive in explaining what&amp;rsquo;s wrong. You&amp;rsquo;re likely to come across this one before your first cup of coffee.</description><content:encoded><![CDATA[<p>This error might look a little cryptic at first, but what it&rsquo;s basically telling you is that what you typed isn&rsquo;t a valid C# statement. It probably looks really close though, because usually you just have a small typo.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/only-assignment-call-increment-decrement-await-and-new-object-expressions-can-be-used-as-a-statement/valid-statements-error.png"
    width="923"
      height="224"></figure>
<p>First though, what&rsquo;s a <a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/statements"  target="_blank" rel="noreferrer">statement</a>? Well, it&rsquo;s every valid line (or in some cases, block) of code that makes up your program, for example:</p>
<ul>
<li>Assignments: <code>string name = &quot;my string&quot;;</code></li>
<li>Calls: <code>MyOtherFunction();</code></li>
<li>Increments: <code>x++;</code></li>
<li>Decrements: <code>x--;</code></li>
<li>Await: <code>await myLongTask;</code></li>
<li>New object expressions: <code>new Person();</code></li>
</ul>
<p>In general, most statements should either modify a variable&rsquo;s value in-place, perform some side-effect (like a <code>foreach</code> block), or at least do something with the return value.</p>
<p>So if you get this error, double-check the line it&rsquo;s complaining about to make sure it&rsquo;s a valid statement, specifically one of the types listed in the error message itself.</p>

<h2 class="relative group">What should you check for?
    <div id="what-should-you-check-for" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-should-you-check-for" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Are you missing a set of parentheses?</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span></span></span></code></pre></div></div>
<p>Did you use <code>==</code> instead of <code>=</code>?</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="kt">string</span> <span class="n">name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">name</span> <span class="p">==</span> <span class="n">Grant</span><span class="p">;</span></span></span></code></pre></div></div>
<p>Did you combine elements of a property and method?</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kt">string</span> <span class="n">Name</span><span class="p">()</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span></span></span></code></pre></div></div>
<p>Does your statement only return a value, but you&rsquo;re doing nothing with it?</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">hi</span> <span class="p">=</span> <span class="s">&#34;Hello, &#34;</span><span class="p">;</span> <span class="n">hi</span> <span class="p">+</span> <span class="s">&#34; Grant&#34;</span><span class="p">;</span><span class="err">`</span></span></span></code></pre></div></div>
<p>If none of those do it for you, feel free to leave a comment below. Heck, post the offending line, and we&rsquo;ll debug it together - maybe I&rsquo;ll have something else to add to this list.</p>
]]></content:encoded><media:content url="https://grantwinney.com/only-assignment-call-increment-decrement-await-and-new-object-expressions-can-be-used-as-a-statement/feature.webp" medium="image" type="image/webp"/></item><item><title>Assign C# code to a variable and then run it</title><link>https://grantwinney.com/csharp-assign-code-to-a-variable/</link><pubDate>Wed, 13 Nov 2019 17:45:00 +0000</pubDate><guid>https://grantwinney.com/csharp-assign-code-to-a-variable/</guid><description>Did you know most languages have a way to pass around code to other functions, so you can call (invoke) it in other parts of your application? In C#, it&amp;rsquo;s called a delegate.</description><content:encoded><![CDATA[<p>It&rsquo;d be ridiculous for a language to not have a way for you to reference a particular value, so you could pass it around in your application.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">string</span> <span class="n">name</span> <span class="p">=</span> <span class="s">&#34;Grant&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">height</span> <span class="p">=</span> <span class="m">71</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">bool</span> <span class="n">isMale</span> <span class="p">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">Employee</span> <span class="n">e</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Employee</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">height</span><span class="p">,</span> <span class="n">isMale</span><span class="p">);</span></span></span></code></pre></div></div>
<p>But did you know most languages have a way to pass around references to <em>code</em> too, so you can pass the code around and call (invoke) it in other parts of your application?</p>
<p>In C#, the type that lets you reference a method is called a <a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/"  target="_blank" rel="noreferrer">delegate</a>, and there are several different constructs that allow you to define a delegate&hellip; depending on what exactly you&rsquo;d like to do.</p>

<h2 class="relative group">Action
    <div id="action" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#action" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The <a href="https://docs.microsoft.com/en-us/dotnet/api/system.action"  target="_blank" rel="noreferrer">Action delegate</a> lets you reference a method that does <em><strong>not</strong></em> return a value.</p>

<h3 class="relative group">Single Line
    <div id="single-line" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#single-line" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>For example, you might define a single-line method that displays a message (with or without parameters).</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">genericHi</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Action</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;Hello World!&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="n">genericHi</span><span class="p">();</span>  <span class="c1">// Hello World!</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">personalizedHi</span> <span class="p">=</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span> <span class="n">Action</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">string</span><span class="p">&gt;((</span><span class="n">firstName</span><span class="p">,</span> <span class="n">lastName</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Hello, {firstName} {lastName}!&#34;</span><span class="p">));</span>		
</span></span><span class="line"><span class="cl"><span class="n">personalizedHi</span><span class="p">(</span><span class="s">&#34;Katie&#34;</span><span class="p">,</span> <span class="s">&#34;Smith&#34;</span><span class="p">);</span>  <span class="c1">// Hello, Katie Smith!</span></span></span></code></pre></div></div>

<h3 class="relative group">Multiple Lines
    <div id="multiple-lines" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#multiple-lines" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Or you could define a method that has several lines:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">sayHiToEveryone</span> <span class="p">=</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span> <span class="n">Action</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">string</span><span class="p">,</span> <span class="kt">string</span><span class="p">&gt;((</span><span class="n">name1</span><span class="p">,</span> <span class="n">name2</span><span class="p">,</span> <span class="n">name3</span><span class="p">)</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">                                       <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                           <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Hi {name1}!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                                           <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Hi {name2}!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                                           <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Hi {name3}!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                                       <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="n">sayHiToEveryone</span><span class="p">(</span><span class="s">&#34;Larry&#34;</span><span class="p">,</span> <span class="s">&#34;Curly&#34;</span><span class="p">,</span> <span class="s">&#34;Moe&#34;</span><span class="p">);</span></span></span></code></pre></div></div>

<h3 class="relative group">Note
    <div id="note" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#note" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>You can also eliminate the <code>new Action</code> part, but then you can&rsquo;t use <code>var</code>, so not sure this is really any better. To each their own&hellip;</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">Action</span> <span class="n">genericHi</span> <span class="p">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;Hello World!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">genericHi</span><span class="p">();</span>  <span class="c1">// Hello World!</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Action</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">string</span><span class="p">&gt;</span> <span class="n">personalizedHi</span> <span class="p">=</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="n">firstName</span><span class="p">,</span> <span class="n">lastName</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Hello, {firstName} {lastName}!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">personalizedHi</span><span class="p">(</span><span class="s">&#34;Katie&#34;</span><span class="p">,</span> <span class="s">&#34;Smith&#34;</span><span class="p">);</span>  <span class="c1">// Hello, Katie Smith!</span></span></span></code></pre></div></div>

<h2 class="relative group">Func
    <div id="func" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#func" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The <a href="https://docs.microsoft.com/en-us/dotnet/api/system.func-1"  target="_blank" rel="noreferrer">Func delegate</a> is very similar to Action, except that it lets you reference a method that <em><strong>does</strong></em> return a value.</p>

<h3 class="relative group">Single Line
    <div id="single-line-1" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#single-line-1" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Again, you can define a single-line method with or without parameters.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">getNowMessage</span> <span class="p">=</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span> <span class="n">Func</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;(()</span> <span class="p">=&gt;</span> <span class="s">$&#34;The time is now: {DateTime.Now.ToString(&#34;</span><span class="n">h</span><span class="p">:</span><span class="n">mm</span> <span class="n">tt</span><span class="s">&#34;)}&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">getNowMessage</span><span class="p">());</span>               <span class="c1">// The time is now: 8:24 PM</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">getTimeMessage</span> <span class="p">=</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span> <span class="n">Func</span><span class="p">&lt;</span><span class="n">DateTime</span><span class="p">,</span> <span class="kt">string</span><span class="p">&gt;((</span><span class="n">date</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="s">$&#34;The time is now: {date.ToString(&#34;</span><span class="n">h</span><span class="p">:</span><span class="n">mm</span> <span class="n">tt</span><span class="s">&#34;)}&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">getTimeMessage</span><span class="p">(</span><span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span><span class="p">));</span>  <span class="c1">// The time is now: 8:24 PM</span></span></span></code></pre></div></div>

<h3 class="relative group">Multiple Lines
    <div id="multiple-lines-1" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#multiple-lines-1" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>And you can define methods with several lines:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">getDrink</span> <span class="p">=</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span> <span class="n">Func</span><span class="p">&lt;</span><span class="n">DateTime</span><span class="p">,</span> <span class="kt">string</span><span class="p">&gt;((</span><span class="n">date</span><span class="p">)</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">                               <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                   <span class="k">if</span> <span class="p">(</span><span class="n">date</span><span class="p">.</span><span class="n">DayOfWeek</span> <span class="p">==</span> <span class="n">DayOfWeek</span><span class="p">.</span><span class="n">Saturday</span> <span class="p">||</span> <span class="n">date</span><span class="p">.</span><span class="n">DayOfWeek</span> <span class="p">==</span> <span class="n">DayOfWeek</span><span class="p">.</span><span class="n">Sunday</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                                       <span class="k">return</span> <span class="s">&#34;🍺&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                                   <span class="k">else</span>
</span></span><span class="line"><span class="cl">                                       <span class="k">return</span> <span class="s">&#34;☕&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                               <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Time for a {getDrink(DateTime.Now)}.&#34;</span><span class="p">);</span>  <span class="c1">// Time for a ☕.</span></span></span></code></pre></div></div>

<h3 class="relative group">Note
    <div id="note-1" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#note-1" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>And finally, you can eliminate the <code>new Func</code> part, but once again that prevents you from using <code>var</code>, so it&rsquo;s not any shorter.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">Func</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="n">getNowMessage2</span> <span class="p">=</span>
</span></span><span class="line"><span class="cl">    <span class="p">()</span> <span class="p">=&gt;</span> <span class="s">$&#34;The time is now: {DateTime.Now.ToString(&#34;</span><span class="n">h</span><span class="p">:</span><span class="n">mm</span> <span class="n">tt</span><span class="s">&#34;)}&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">getNowMessage2</span><span class="p">());</span>               <span class="c1">// The time is now: 8:24 PM</span>
</span></span><span class="line"><span class="cl">		
</span></span><span class="line"><span class="cl"><span class="n">Func</span><span class="p">&lt;</span><span class="n">DateTime</span><span class="p">,</span> <span class="kt">string</span><span class="p">&gt;</span> <span class="n">getTimeMessage2</span> <span class="p">=</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="n">date</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="s">$&#34;The time is now: {date.ToString(&#34;</span><span class="n">h</span><span class="p">:</span><span class="n">mm</span> <span class="n">tt</span><span class="s">&#34;)}&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">getTimeMessage2</span><span class="p">(</span><span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span><span class="p">));</span>  <span class="c1">// The time is now: 8:24 PM</span></span></span></code></pre></div></div>

<h2 class="relative group">Try it yourself
    <div id="try-it-yourself" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#try-it-yourself" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>You can play with these yourself on .NET Fiddle:</p>
<ul>
<li><a href="https://dotnetfiddle.net/Widget/3kpajq"  target="_blank" rel="noreferrer">Action delegate demo | .NET Fiddle</a></li>
<li><a href="https://dotnetfiddle.net/Widget/VbkB8z"  target="_blank" rel="noreferrer">Func delegate demo | .NET Fiddle</a></li>
</ul>
<p>If you found this content useful, and would like to learn more about a variety of <a href="https://grantwinney.com/tags/csharp/"  target="_blank" rel="noreferrer">C#</a> features, check out my <a href="https://github.com/grantwinney/CSharpDotNetFeatures"  target="_blank" rel="noreferrer">CSharpDotNetFeatures repo</a>, where you&rsquo;ll find links to plenty more blog posts and practical examples!</p>
]]></content:encoded><media:content url="https://grantwinney.com/csharp-assign-code-to-a-variable/feature.webp" medium="image" type="image/webp"/></item><item><title>How can I find the state of NumLock, CapsLock or ScrollLock in WPF?</title><link>https://grantwinney.com/how-can-i-find-the-state-of-numlock-capslock-scrolllock-in-wpf/</link><pubDate>Tue, 12 Nov 2019 00:30:00 +0000</pubDate><guid>https://grantwinney.com/how-can-i-find-the-state-of-numlock-capslock-scrolllock-in-wpf/</guid><description>If you&amp;rsquo;re writing a WPF application and need to find the state of the Num Lock, Caps Lock, or Scroll Lock keys, you&amp;rsquo;re in luck - there&amp;rsquo;s a method for that.</description><content:encoded><![CDATA[<p>If you&rsquo;re writing a WPF application and need to find the state of the Num Lock, Caps Lock, or Scroll Lock keys, you can use the <a href="https://msdn.microsoft.com/en-us/library/system.windows.input.keyboard.iskeytoggled%5C%28v=vs.110%5C%29.aspx"  target="_blank" rel="noreferrer">Keyboard.IsToggled</a> method (introduced in .NET 3.0):</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">isNumLockToggled</span> <span class="p">=</span> <span class="n">Keyboard</span><span class="p">.</span><span class="n">IsKeyToggled</span><span class="p">(</span><span class="n">Key</span><span class="p">.</span><span class="n">NumLock</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">isCapsLockToggled</span> <span class="p">=</span> <span class="n">Keyboard</span><span class="p">.</span><span class="n">IsKeyToggled</span><span class="p">(</span><span class="n">Key</span><span class="p">.</span><span class="n">CapsLock</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">isScrollLockToggled</span> <span class="p">=</span> <span class="n">Keyboard</span><span class="p">.</span><span class="n">IsKeyToggled</span><span class="p">(</span><span class="n">Key</span><span class="p">.</span><span class="n">Scroll</span><span class="p">);</span></span></span></code></pre></div></div>
<p>Add this <code>using</code> directive to the top of your class, if it&rsquo;s not already there:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">System.Windows.Input</span><span class="p">;</span></span></span></code></pre></div></div>
<p>Internally, the <a href="http://referencesource.microsoft.com/#PresentationCore/Core/CSharp/System/Windows/Input/Keyboard.cs,22f8500adfc561fb"  target="_blank" rel="noreferrer">IsToggled()</a> method checks to see whether or not a <a href="http://referencesource.microsoft.com/#PresentationCore/Core/CSharp/System/Windows/Input/KeyStates.cs,78ceabc4eeaa31fc"  target="_blank" rel="noreferrer"><code>KeyStates.Toggled</code></a> flag is set for the specified key.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="na">[Flags]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">enum</span> <span class="n">KeyStates</span> <span class="p">:</span> <span class="kt">byte</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">None</span> <span class="p">=</span> <span class="p">(</span><span class="kt">byte</span><span class="p">)</span> <span class="m">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">Down</span> <span class="p">=</span> <span class="p">(</span><span class="kt">byte</span><span class="p">)</span> <span class="m">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">Toggled</span> <span class="p">=</span> <span class="p">(</span><span class="kt">byte</span><span class="p">)</span> <span class="m">2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
]]></content:encoded><media:content url="https://grantwinney.com/how-can-i-find-the-state-of-numlock-capslock-scrolllock-in-wpf/feature.webp" medium="image" type="image/webp"/></item><item><title>What is the opposite of Any in LINQ?</title><link>https://grantwinney.com/what-is-the-opposite-method-of-any-t-in-linq/</link><pubDate>Mon, 11 Nov 2019 23:36:00 +0000</pubDate><guid>https://grantwinney.com/what-is-the-opposite-method-of-any-t-in-linq/</guid><description>One of the many nice functions in LINQ is a single word that iterates through a collection, returning true if at least one item in the collection matches the condition you specify. But what&amp;rsquo;s the opposite of the Any keyword in LINQ?</description><content:encoded><![CDATA[<p>If you are (or hope to be) a .NET developer, knowing <a href="https://learn.microsoft.com/en-us/dotnet/csharp/linq/"  target="_blank" rel="noreferrer">LINQ</a> is a <em>huge</em> time-saver. It&rsquo;s a syntax that allows you to manipulate data in a fashion that&rsquo;ll be familiar to anyone who&rsquo;s worked in a database.</p>

<h2 class="relative group">Any
    <div id="any" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#any" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>One of the many nice functions in LINQ is a single word that iterates through a collection, returning <code>true</code> if at least one item in the collection matches the condition you specify.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">currencies</span> <span class="p">=</span> <span class="k">new</span><span class="p">[]</span> <span class="p">{</span> <span class="s">&#34;USD&#34;</span><span class="p">,</span> <span class="s">&#34;EUR&#34;</span><span class="p">,</span> <span class="s">&#34;JPY&#34;</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">currencies</span><span class="p">.</span><span class="n">Any</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span> <span class="p">==</span> <span class="s">&#34;MXN&#34;</span><span class="p">));</span>  <span class="c1">// False</span></span></span></code></pre></div></div>
<p>But what&rsquo;s the <em>opposite</em> of <code>Any&lt;T&gt;()</code>?</p>
<p>What if, instead of finding out whether the list of currencies includes &ldquo;peso&rdquo;, you wanted to make sure the list of currencies did <em>not</em> include &ldquo;peso&rdquo;? You could negate the above, but you might think that reads a bit funny&hellip; and I&rsquo;d agree.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">currencies</span> <span class="p">=</span> <span class="k">new</span><span class="p">[]</span> <span class="p">{</span> <span class="s">&#34;USD&#34;</span><span class="p">,</span> <span class="s">&#34;EUR&#34;</span><span class="p">,</span> <span class="s">&#34;JPY&#34;</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(!</span><span class="n">currencies</span><span class="p">.</span><span class="n">Any</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span> <span class="p">==</span> <span class="s">&#34;MXN&#34;</span><span class="p">));</span>  <span class="c1">// True</span></span></span></code></pre></div></div>

<h2 class="relative group">All
    <div id="all" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#all" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The only way to make sure that a list <em>doesn&rsquo;t</em> include a particular value, or that <em>no</em> item in the collection matches a particular condition, is to check every single item in the collection&hellip; and that&rsquo;s what <code>All&lt;T&gt;()</code> is for.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">currencies</span> <span class="p">=</span> <span class="k">new</span><span class="p">[]</span> <span class="p">{</span> <span class="s">&#34;USD&#34;</span><span class="p">,</span> <span class="s">&#34;EUR&#34;</span><span class="p">,</span> <span class="s">&#34;JPY&#34;</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">currencies</span><span class="p">.</span><span class="n">All</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span> <span class="p">!=</span> <span class="s">&#34;MXN&#34;</span><span class="p">));</span>          <span class="c1">// True</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">currencies</span><span class="p">.</span><span class="n">All</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="p">!</span><span class="n">x</span><span class="p">.</span><span class="n">StartsWith</span><span class="p">(</span><span class="s">&#34;M&#34;</span><span class="p">)));</span>  <span class="c1">// True</span></span></span></code></pre></div></div>

<h2 class="relative group">Try it out
    <div id="try-it-out" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#try-it-out" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://dotnetfiddle.net/Widget/Sxr0Yz"  target="_blank" rel="noreferrer">Try it out yourself here</a>!</p>
]]></content:encoded><media:content url="https://grantwinney.com/what-is-the-opposite-method-of-any-t-in-linq/feature.webp" medium="image" type="image/webp"/></item><item><title>Enable logging for an AWS Lambda job</title><link>https://grantwinney.com/where-are-my-logs-in-aws-lambda/</link><pubDate>Thu, 07 Nov 2019 18:04:08 +0000</pubDate><guid>https://grantwinney.com/where-are-my-logs-in-aws-lambda/</guid><description>In a new AWS Lambda function, logging is initially disabled. Lets see how to enable it, for those times where additional detail is needed.</description><content:encoded><![CDATA[<p>I setup an AWS Lambda job recently, and then added a trigger to run it every morning. I checked it one morning and realized:</p>
<ol>
<li>The job failed for some reason.</li>
<li>I had no idea what that reason <em>was,</em> because nothing was written to the logs.</li>
</ol>
<p>According to the <a href="https://docs.aws.amazon.com/lambda/latest/dg/dotnet-logging.html"  target="_blank" rel="noreferrer">docs</a>, all <code>Console.WriteLine</code> statements are logged:</p>
<blockquote><p>To output logs from your function code, you can use methods on the Console class, or any logging library that writes to stdout or stderr.</p>
</blockquote><p>But all I got on the logs page was an error message&hellip; an awful, unhelpful message. I needed more detail into the failure, but how?</p>
<blockquote><p>There was an error loading Log Streams. Please try again by refreshing this page.</p>
</blockquote><p>When creating a new Lambda job, logging is not configured by default. I&rsquo;m sure there&rsquo;s reasons for it, but considering this is a service where jobs run headless, it seems pretty important to be able to jump in and quickly see <em>exactly</em> why a job is failing. The metrics screens aren&rsquo;t enough.</p>
<p>So, if you&rsquo;ve just setup a job and you&rsquo;re running into this same problem, check the execution role for your job, which &ldquo;defines the permissions of your function&rdquo;. These permissions include the ability to write out to the logs (or a &ldquo;log group&rdquo; as they call it).</p>
<p>Look for this panel halfway down the screen for your function. Click the &ldquo;View role&rdquo; link:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/where-are-my-logs-in-aws-lambda/enable_logging1.png"
    width="794"
      height="350"></figure>
<p>You want to add a new policy, so click the big button that says &ldquo;Attach policies&rdquo;.:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/where-are-my-logs-in-aws-lambda/enable_logging2.png"
    width="692"
      height="308"></figure>
<p>Type &ldquo;cloudwatchlogs&rdquo; into the filter, select &ldquo;CloudWatchLogsFullAccess&rdquo;, and attach it:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/where-are-my-logs-in-aws-lambda/enable_logging3.png"
    width="1075"
      height="868"></figure>
<p>Verify the new policy shows up on the previous screen.:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/where-are-my-logs-in-aws-lambda/enable_logging4.png"
    width="803"
      height="347"></figure>
<p><em>Run your job again</em>, then click &ldquo;View logs in CloudWatch&rdquo;.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/where-are-my-logs-in-aws-lambda/enable_logging5.png"
    width="1268"
      height="349"></figure>
<p>You should see an entry, assuming your job wrote anything out.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/where-are-my-logs-in-aws-lambda/enable_logging6.png"
    width="677"
      height="163"></figure>
<p>Yay, logs. 🎉</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/where-are-my-logs-in-aws-lambda/enable_logging7.png"
    width="1114"
      height="417"></figure>
<p>My issue ended up being two separate problems:</p>
<ul>
<li>I targeted .NET Core 2.1 when I created the Lambda function (because that&rsquo;s the only one available), but my C# project targeted .NET Core 3.0. Oops.</li>
<li>I also forgot to add a reference to <code>Amazon.Lambda.Core</code>, which is really easy to do since it&rsquo;s not used in the project nor required by any part of the project, but its absence will cause the job to fail when it runs on AWS. 🤦‍♂️</li>
</ul>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/where-are-my-logs-in-aws-lambda/aws-lambda-core.png"
    width="1579"
      height="585"></figure>
<p>If that didn&rsquo;t do it for you, or you already had a comparable permission selected, here&rsquo;s some more helpful suggestions in this post by Dora Hodanic:</p>
<p><a href="https://blogs.perficient.com/2018/02/12/error-loading-log-streams/"  target="_blank" rel="noreferrer">Amazon Connect and Lambda logs: Error loading Log Streams / Blogs / Perficient</a></p>
]]></content:encoded><media:content url="https://grantwinney.com/where-are-my-logs-in-aws-lambda/feature.webp" medium="image" type="image/webp"/></item><item><title>What's the GitHub Package Registry?</title><link>https://grantwinney.com/first-look-github-package-registry-beta/</link><pubDate>Sun, 06 Oct 2019 03:55:19 +0000</pubDate><guid>https://grantwinney.com/first-look-github-package-registry-beta/</guid><description>Most of us host something (and some of us everything) on GitHub, especially since they host private repos for free too now. I&amp;rsquo;ve been eager to try the GitHub Package Registry since they announced it last May. Well, I just got access to the beta, so let&amp;rsquo;s see what we can do!</description><content:encoded><![CDATA[<p>A few prerequisites before we dig in&hellip;</p>
<ul>
<li>If you&rsquo;re new to all this, check out <em>&quot;</em><a href="https://grantwinney.com/whats-a-package-manager/"  target="_blank" rel="noreferrer"><em>What&rsquo;s a package manager?</em></a><em>&quot;</em></li>
<li>If you want to upload a package, install the <a href="https://www.nuget.org/downloads"  target="_blank" rel="noreferrer">NuGet command line tools</a>. <em>(nuget.exe isn&rsquo;t a setup file - just save it somewhere and</em> <a href="https://helpdeskgeek.com/windows-10/add-windows-path-environment-variable/"  target="_blank" rel="noreferrer"><em>add it to your path</em></a><em>)</em></li>
<li>If you want to reference your package, install <a href="https://visualstudio.microsoft.com/downloads/"  target="_blank" rel="noreferrer">Visual Studio</a>. <em>(it&rsquo;s free)</em></li>
<li>When you&rsquo;re done here, consider <a href="https://grantwinney.com/keeping-your-github-code-secure/"  target="_blank" rel="noreferrer">securing your GitHub account</a> too.</li>
</ul>
<p>Most of us host something <em>(and some of us everything)</em> on GitHub, especially since they host private repos for free too now. I&rsquo;ve been eager to try the <a href="https://help.github.com/en/articles/about-github-package-registry"  target="_blank" rel="noreferrer">GitHub Package Registry</a> since they announced it last May - I just got access to the beta.</p>
<p>In their own words, GPR <em>&ldquo;allows you to host your packages and code in one place. You can host software packages privately or publicly and use them as dependencies in your projects.&rdquo;</em> That doesn&rsquo;t really answer any of my questions though, such as:</p>
<ul>
<li>Will it streamline the current process of uploading packages to NuGet?</li>
<li>Is it meant as a backup to the many package registries already available?</li>
<li>Or do they hope it&rsquo;ll become &ldquo;the one registry to rule them all&rdquo;?</li>
</ul>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/first-look-github-package-registry-beta/frodo.jpg"
    width="571"
      height="226"></figure>
<hr>

<h2 class="relative group">Create a personal access token
    <div id="create-a-personal-access-token" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#create-a-personal-access-token" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Everything that follows pretty much came out of these docs, so I&rsquo;d recommend checking them out later, and keeping them close at hand as you read through this.</p>
<ul>
<li><a href="https://help.github.com/en/articles/about-github-package-registry"  target="_blank" rel="noreferrer">About GitHub Package Registry</a></li>
<li><a href="https://help.github.com/en/articles/configuring-nuget-for-use-with-github-package-registry"  target="_blank" rel="noreferrer">Configuring NuGet for use with GitHub Package Registry</a></li>
<li><a href="https://github.com/features/package-registry"  target="_blank" rel="noreferrer">GitHub Package Registry: Your packages, at home with their code</a></li>
</ul>
<p>The first step, no matter which language you&rsquo;re using to connect to the GPR, is to <a href="https://help.github.com/en/articles/configuring-nuget-for-use-with-github-package-registry#authenticating-to-github-package-registry"  target="_blank" rel="noreferrer">create a personal access token</a>. Think of it this way - you want a third party to be able to access your data on GitHub. You <em>could</em> just give them your username and password and trust that they&rsquo;ll only access what they need. Don&rsquo;t do that. Ever! 🤬</p>
<p>Instead, create a token that grants <em>exactly</em> what the third party says it needs access to, and nothing more. Then it&rsquo;s GitHub&rsquo;s job to make sure it actually happens. Even though the app were granting access to is <em>also</em> a GitHub service, they want us to treat GPR just like anything else. It&rsquo;s not a bad idea actually.</p>
<p>So, <a href="https://github.com/settings/tokens/new"  target="_blank" rel="noreferrer">create a new token</a> and select the <code>read:packages</code> and <code>write:packages</code> scopes. <strong>Leave the</strong> <em><code>*repo*</code></em> <strong>scope selected!</strong> Technically, if you&rsquo;re repo is public you shouldn&rsquo;t need it&hellip; but if you&rsquo;re going to try using the package in VS you&rsquo;ll need it. I&rsquo;ll elaborate later. Oh, and <strong>copy the token it generates</strong> after you hit &ldquo;Generate Token&rdquo; or you&rsquo;ll be doing it over again in the next step. 😅</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/first-look-github-package-registry-beta/new-token.png"
    width="1249"
      height="824"></figure>
<hr>

<h2 class="relative group">Push your first package
    <div id="push-your-first-package" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#push-your-first-package" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>In order to do this yourself, you&rsquo;ll need something to publish. If you don&rsquo;t have a project in mind, just make a simple console app in VS that prints out &ldquo;hello world!&rdquo; and push it to GitHub. I created a one-off repo called &ldquo;github-package-repo-first-try&rdquo; for this purpose.</p>
<p>Checkout the &ldquo;packages&rdquo; tab for your repo on GitHub. When there aren&rsquo;t any yet, you&rsquo;ll get a reminder of the commands to run for pushing your first package.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/first-look-github-package-registry-beta/empty-packages-screen.png"
    width="1258"
      height="922"></figure>
<p>First, tell NuGet it can use GPR as a source, and give it the credentials to use:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">nuget sources Add -Name &#34;GPR&#34; \
     -Source &#34;https://nuget.pkg.github.com/OWNER/index.json&#34; \
     -UserName USERNAME -Password TOKEN</code></pre></div>
<p>So for me, that&rsquo;d be:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">nuget sources Add -Name &#34;GPR&#34; -Source &#34;https://nuget.pkg.github.com/grantwinney/index.json&#34; -UserName grantwinney -Password &lt;my_token&gt;</code></pre></div>
<p>If all goes well, you&rsquo;ll get a confirmation message:</p>
<blockquote><p>Package source with Name: GPR added successfully.</p>
</blockquote><p>Then push the package via the command line. At this point, you should open your project and run <strong>Build</strong> / <strong>Pack</strong>, or open my project, open the project properties and in the &ldquo;Package&rdquo; tab change the package version to &ldquo;1.0.1&rdquo;, and then build it.</p>
<p>Change to the directory where the package was published, probably under <code>bin/debug</code>, or provide the full path.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">&gt; nuget push HelloWorld.1.0.0.nupkg -Source &#34;GPR&#34;

Pushing HelloWorld.1.0.0.nupkg to &#39;https://nuget.pkg.github.com/grantwinney&#39;...
  PUT https://nuget.pkg.github.com/grantwinney/
  OK https://nuget.pkg.github.com/grantwinney/ 1392ms

Your package was pushed.</code></pre></div>
<p>If you forgot to change the version number and try pushing the package, you&rsquo;ll get a conflict message. Just change the package number and try again.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">&gt; nuget push HelloWorld.1.0.0.nupkg -Source &#34;GPR&#34;

Pushing HelloWorld.1.0.0.nupkg to &#39;https://nuget.pkg.github.com/grantwinney&#39;...
  PUT https://nuget.pkg.github.com/grantwinney/
  
WARNING: Error: Version HelloWorld of &#34;1.0.0&#34; has already been pushed.
  Conflict https://nuget.pkg.github.com/grantwinney/ 807ms
See help for push option to automatically skip duplicates.
Response status code does not indicate success: 409 (Conflict).</code></pre></div>
<p>That&rsquo;s it! Here&rsquo;s <a href="https://github.com/grantwinney/GhostSharp/packages"  target="_blank" rel="noreferrer">what it looks like on GitHub</a> after pushing packages for my GhostSharp project. I uploaded two versions of GhostSharp - <a href="https://github.com/grantwinney/GhostSharp/packages/30438?version=1.0.2"  target="_blank" rel="noreferrer">1.0.2</a> and <a href="https://github.com/grantwinney/GhostSharp/packages/30438?version=1.0.4"  target="_blank" rel="noreferrer">1.0.4</a> - and you can see them listed in the lower-right corner.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/first-look-github-package-registry-beta/first-package-upload.png"
    width="1315"
      height="925"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/first-look-github-package-registry-beta/package-previous-version.png"
    width="1325"
      height="925"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/first-look-github-package-registry-beta/package-details.png"
    width="1325"
      height="925"></figure>
<hr>

<h2 class="relative group">Reference your package in VS
    <div id="reference-your-package-in-vs" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#reference-your-package-in-vs" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>This, unfortunately, was a crappier experience than I&rsquo;d hoped for. I&rsquo;m not sure if it&rsquo;s a problem with the GitHub Package Registry or something else, but referencing the new package from GitHub didn&rsquo;t work right away. Let me back up a few steps - here&rsquo;s how things <em>should</em> work.</p>
<p>Create a new project, which you&rsquo;ll use to consume the package you just pushed to the GPR. Or if you&rsquo;re using the project I created, there&rsquo;s a couple in there already - one targets .NET Core 2.2 and the other targets .NET Framework 4.7.</p>
<p>Right-click your project&rsquo;s dependencies and choose <em>&ldquo;Manage NuGet Packages&hellip;&rdquo;,</em> then switch the &ldquo;Package source&rdquo; to GPR. You should see anything you&rsquo;ve uploaded for any of your personal projects. I ran into problems with this at first, but I&rsquo;ll explain all that later.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/first-look-github-package-registry-beta/gpr-nuget-source-package-repo.png"
    width="1529"
      height="926"></figure>
<p>I can view the 2 packages I uploaded to the GPR</p>

<h3 class="relative group">Including assembly files (modifying the nuspec)
    <div id="including-assembly-files-modifying-the-nuspec" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#including-assembly-files-modifying-the-nuspec" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>This is <em>all</em> I&rsquo;ve ever had to do when referencing a NuGet.org package, including my own GhostSharp package. GhostSharp is a .NET Standard project, and selecting it on this screen just <em>works</em>.</p>
<p>Unfortunately, referencing my &ldquo;test&rdquo; .NET Standard package from the GPR didn&rsquo;t work. I tried it with a .NET Core app and a .NET Framework app, but nada. It&rsquo;s a .NET Standard app, so it should work in both of these. 😕</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/first-look-github-package-registry-beta/netcore-install-error.png"
    width="1529"
      height="926"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/first-look-github-package-registry-beta/netframework-install-error.png"
    width="1529"
      height="926"></figure>
<p>Referencing the package from .NET Core (left) and .NET Framework (right) apps 😔</p>
<p>Restarting VS, clearing the NuGet caches, wiping out the bin/obj folders - none of it fixed it. I started suspecting something was missing from the <code>.nuspec</code> file VS generated but when I compared it to the GhostSharp package on NuGet.org, the layout was the same.</p>
<p>What ended up fixing it, although I&rsquo;m still not sure why it&rsquo;s needed, was to <a href="https://docs.microsoft.com/en-us/nuget/reference/nuspec#including-assembly-files"  target="_blank" rel="noreferrer">include assembly files</a>. I opened up the nupkg file that VS built, added a <code>files</code> node to the .nuspec file per <a href="https://stackoverflow.com/a/37178144/301857"  target="_blank" rel="noreferrer">this suggestion</a>, then upped the version number to 1.0.1 (and renamed the nupkg file to match), and then ran the earlier command to the push it to the GPR.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="cp">&lt;?xml version=&#34;1.0&#34; encoding=&#34;utf-8&#34;?&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;package</span> <span class="na">xmlns=</span><span class="s">&#34;http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;metadata&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;id&gt;</span>HelloWorld<span class="nt">&lt;/id&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;version&gt;</span>1.0.1<span class="nt">&lt;/version&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;authors&gt;</span>HelloWorld<span class="nt">&lt;/authors&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;owners&gt;</span>HelloWorld<span class="nt">&lt;/owners&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;requireLicenseAcceptance&gt;</span>false<span class="nt">&lt;/requireLicenseAcceptance&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;description&gt;</span>A simple app for use with the GitHub package repository.<span class="nt">&lt;/description&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;repository</span> <span class="na">url=</span><span class="s">&#34;https://github.com/grantwinney/github-package-repo-first-try&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;dependencies&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;group</span> <span class="na">targetFramework=</span><span class="s">&#34;.NETStandard2.0&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/dependencies&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;/metadata&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;files&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;file</span> <span class="na">src=</span><span class="s">&#34;bin\Release\*.*&#34;</span> <span class="na">target=</span><span class="s">&#34;lib/net45&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;/files&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/package&gt;</span></span></span></code></pre></div></div>
<p>The result? Everything. Works. WTF.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/first-look-github-package-registry-beta/everything-works-wtf.png"
    width="1920"
      height="1030"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/first-look-github-package-registry-beta/expected-output.png"
    width="1920"
      height="1030"></figure>
<p>References work? ✅ Expected output? ✅</p>
<p>Oooookay. My GhostSharp package on NuGet.org does not have that <code>files</code> node. And when I download my &ldquo;test&rdquo; package and compare them, before and after adding the <code>files</code> node, there&rsquo;s no change at all to the package other than the .nuspec file itself. It didn&rsquo;t actually <em>include</em> anything else in the package. Yet everything works. Welcome to modern development folks.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/first-look-github-package-registry-beta/code_meme.jpg"
    width="941"
      height="341"></figure>

<h3 class="relative group">Include repo scope on your token - even for public repos
    <div id="include-repo-scope-on-your-token---even-for-public-repos" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#include-repo-scope-on-your-token---even-for-public-repos" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>This was the other issue I ran into, although if you left <code>repo</code> scope selected on your token like I told you too, hopefully you didn&rsquo;t run into this one.</p>
<p>When I initially tried to list packages from the GPR in Visual Studio, it prompted me for a password. I tried my GitHub password, then the token string - nothing. The error message in the console was&hellip; less than helpful.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/first-look-github-package-registry-beta/manage-nuget-packages.png"
    width="598"
      height="335"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/first-look-github-package-registry-beta/select-package-source.png"
    width="1538"
      height="383"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/first-look-github-package-registry-beta/2019-10-04-23_16_45-HelloWorld---Microsoft-Visual-Studio.png"
    width="1529"
      height="926"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/first-look-github-package-registry-beta/2019-10-04-23_17_22-HelloWorld---Microsoft-Visual-Studio.png"
    width="1529"
      height="926"></figure>
<p>It did at least show me the URI it was trying to access, and when I entered that directly into a browser window I get the same authentication prompt. I entered my GitHub token string again, and got a much better response:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span><span class="nt">&#34;errors&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="p">[{</span><span class="nt">&#34;code&#34;</span><span class="p">:</span><span class="s2">&#34;Your token has not been granted the required scopes to execute this query. The &#39;name&#39; field requires one of the following scopes&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;message&#34;</span><span class="p">:</span><span class="s2">&#34; [&#39;repo&#39;], but your token has only been granted the: [&#39;read:packages&#39;, &#39;write:packages&#39;] scopes. Please modify your token&#39;s scopes at: https://github.com/settings/tokens.&#34;</span><span class="p">}]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Note the part about the additional scope. I initially thought that modifying the token to include the <code>public_repo</code> scope would be enough, since that allows a third party to &ldquo;access public repositories&rdquo;, but nopedy nope:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span><span class="nt">&#34;errors&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="p">[{</span><span class="nt">&#34;code&#34;</span><span class="p">:</span><span class="s2">&#34;Your token has not been granted the required scopes to execute this query. The &#39;name&#39; field requires one of the following scopes&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;message&#34;</span><span class="p">:</span><span class="s2">&#34; [&#39;repo&#39;], but your token has only been granted the: [&#39;public_repo&#39;, &#39;read:packages&#39;, &#39;write:packages&#39;] scopes. Please modify your token&#39;s scopes at: https://github.com/settings/tokens.&#34;</span><span class="p">}]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The solution was to leave the <code>repo</code> scope selected in the first place, which is why I told you to do it earlier. The docs said somewhere that that scope is only needed for private repos, but <em>apparently</em> not. I added the additional scope and entered my token as the password, and this was the response:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;data&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;@type&#34;</span><span class="p">:</span> <span class="s2">&#34;Package&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;authors&#34;</span><span class="p">:</span> <span class="s2">&#34;grant&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;copyright&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;description&#34;</span><span class="p">:</span> <span class="s2">&#34;This is a C# wrapper around the Ghost RESTful Content API, documented here: https://docs.ghost.org/api/content/&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;iconUrl&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;GhostSharp&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;isPrerelease&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;language&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;licenseUrl&#34;</span><span class="p">:</span> <span class="s2">&#34;https://aka.ms/deprecateLicenseUrl&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;requireLicenseAcceptance&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;summary&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;tags&#34;</span><span class="p">:</span> <span class="s2">&#34;ghost api rest api-wrapper netstandard wrapper-api wrapper&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;title&#34;</span><span class="p">:</span> <span class="s2">&#34;GhostSharp&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;totalDownloads&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;verified&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;1.0.4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;versions&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="p">[</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;1.0.4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;downloads&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;@id&#34;</span><span class="p">:</span> <span class="s2">&#34;https://nuget.pkg.github.com/grantwinney/GhostSharp/1.0.4.json&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;1.0.2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;downloads&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;@id&#34;</span><span class="p">:</span> <span class="s2">&#34;https://nuget.pkg.github.com/grantwinney/GhostSharp/1.0.2.json&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;@type&#34;</span><span class="p">:</span> <span class="s2">&#34;Package&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;authors&#34;</span><span class="p">:</span> <span class="s2">&#34;HelloWorld&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;copyright&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;description&#34;</span><span class="p">:</span> <span class="s2">&#34;A simple app for use with the GitHub package repository.&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;iconUrl&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;HelloWorld&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;isPrerelease&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;language&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;licenseUrl&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;requireLicenseAcceptance&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;summary&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;tags&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;title&#34;</span><span class="p">:</span> <span class="s2">&#34;HelloWorld&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;totalDownloads&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;verified&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;1.0.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;versions&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="p">[</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;1.0.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;downloads&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;@id&#34;</span><span class="p">:</span> <span class="s2">&#34;https://nuget.pkg.github.com/grantwinney/HelloWorld/1.0.0.json&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;totalHits&#34;</span><span class="p">:</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Tried it again in Visual Studio, and the rest is history.</p>
<p>If you&rsquo;d like to see a similar tutorial but for npm instead, there were two recent posts on The DEV:</p>
<ul>
<li><a href="https://dev.to/jgierer12/how-to-publish-packages-to-the-github-package-repository-4bai"  target="_blank" rel="noreferrer">How to publish packages to the GitHub Package Registry</a></li>
<li><a href="https://dev.to/dalenguyen/create-your-first-github-package-564f"  target="_blank" rel="noreferrer">Create Your First Github Package</a></li>
</ul>
<hr>

<h2 class="relative group">Concerns
    <div id="concerns" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#concerns" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>A few issues and concerns I have about the GPR&hellip;</p>

<h3 class="relative group">Ease of Use
    <div id="ease-of-use" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#ease-of-use" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>I ran into a couple irritating issues. One is a documentation issue, but the other (with the <code>files</code> node) I&rsquo;m not sure about yet. I may test it more, or submit a bug report&hellip; or just let it go and hope someone from Microsoft discovers this post.</p>

<h3 class="relative group">Discoverability
    <div id="discoverability" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#discoverability" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>When I&rsquo;m looking for a package to use, I use NuGet.org or RubyGems - not GitHub. I might end up there after clicking a link on one of the other sites. So will they make it easy to search the GPR globally somehow? Or is this really just intended as a backup to existing package management sites?</p>

<h3 class="relative group">Community
    <div id="community" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#community" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>What about registries that provide some aspect of community-building, or rallying around a particular language or framework? Will the GPR have something similar? I&rsquo;m not really sure what I&rsquo;m looking for here&hellip;</p>

<h3 class="relative group">Reliability
    <div id="reliability" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#reliability" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Regarding deleting packages you&rsquo;ve uploaded, they state:</p>
<blockquote><p>To avoid breaking projects that may depend on your packages, GitHub Package Registry does not support package deletion or deleting a version of a package.</p>
<p>Under special circumstances, such as for legal reasons or to conform with GDPR standards, you can request deleting a package through GitHub Support.</p>
</blockquote><p>This seems reasonable, and in line with other package management sites like <a href="https://docs.microsoft.com/en-us/nuget/nuget-org/policies/deleting-packages"  target="_blank" rel="noreferrer">NuGet.org</a> and <a href="https://blog.npmjs.org/post/141905368000/changes-to-npms-unpublish-policy"  target="_blank" rel="noreferrer">npm</a>. Failure to do this has <a href="https://www.theregister.co.uk/2016/03/23/npm_left_pad_chaos/"  target="_blank" rel="noreferrer">wreaked havoc</a> before. But wait a sec&hellip;</p>
<p>GitHub allows you to <a href="https://help.github.com/en/articles/deleting-a-repository"  target="_blank" rel="noreferrer">delete a repository</a>, and repositories contain your packages. So what happens then? I tried deleting (but didn&rsquo;t) the repository I was using to test this, and it sure seems like it would. Do the packages float around detached? 😕</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/first-look-github-package-registry-beta/delete-test-repo.png"
    width="1299"
      height="796"></figure>
<p>Anyway, if you get beta access or they go live with everything, I&rsquo;d love to hear about your experiences too. Good luck, and have fun!</p>
]]></content:encoded><media:content url="https://grantwinney.com/first-look-github-package-registry-beta/feature.webp" medium="image" type="image/webp"/></item><item><title>What's a package manager?</title><link>https://grantwinney.com/whats-a-package-manager/</link><pubDate>Thu, 03 Oct 2019 17:09:27 +0000</pubDate><guid>https://grantwinney.com/whats-a-package-manager/</guid><description>If you&amp;rsquo;ve heard the term package manager, especially with GitHub announcing their own, you might be wondering what exactly it is. Well, it&amp;rsquo;s a way to find, reuse, and share code, among other things.</description><content:encoded><![CDATA[<p>None of us writes code in a vacuum.</p>
<p>You might&rsquo;ve started out in school writing one-off apps in Java or Python. You may have created a basic website, cobbled together with HTML and the JavaScript saveur du jour. Either way, your code sits upon layers of fundamental building blocks, developed by thousands of others.</p>
<p>Eventually, you&rsquo;ll get to a point where you want to reuse someone else&rsquo;s code - or share your own! Hosting on a site like GitHub is only part of the solution, as you&rsquo;d still have to include instructions on how to compile it, integrate it, etc.</p>
<p>If only we had a way to package up our code. 😐</p>

<h2 class="relative group">What&rsquo;s a package?
    <div id="whats-a-package" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whats-a-package" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Every app you use depends on other software, some of it bundled with the app itself, and some of it installed with your OS. For example, Notepad++ depends on <a href="https://github.com/curl/curl"  target="_blank" rel="noreferrer">libcurl.dll</a> for transferring data during updates. The authors of Notepad++ didn&rsquo;t write libcurl, but it depends on it. If you rename it, then Notepad++ can&rsquo;t work, because it can&rsquo;t find that bundle of code.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/whats-a-package-manager/missing-dll.png"
    width="1920"
      height="588"></figure>
<p>Removing or renaming the code an app depends on has predictable results&hellip;</p>
<p>It&rsquo;s the same with the code we write.</p>
<p>Take .NET for example. When you build a solution in Visual Studio, it compiles your code, zips it up into a series of DLL files (usually one per project), and dumps them into a &ldquo;bin&rdquo; directory (along with some other files, but let&rsquo;s ignore those).</p>
<p>Here&rsquo;s one of my projects, <a href="https://grantwinney.com/ghostsharp/"  target="_blank" rel="noreferrer">GhostSharp</a>. After building, I get &ldquo;GhostSharp.dll&rdquo; and &ldquo;GhostSharp.Tests.dll&rdquo; files <em>(for the code I wrote)</em> and &ldquo;<a href="https://www.nuget.org/packages/NUnit3TestAdapter/"  target="_blank" rel="noreferrer">NUnit3.TestAdapter.dll</a>&rdquo; <em>(because my test project depends on it)</em>.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/whats-a-package-manager/bin-dir.png"
    width="838"
      height="464"></figure>
<p>All of these various DLL files <em>are</em> packages - that is, they&rsquo;re just bundles of related code. And while I used .NET and libraries in my example, other languages have their own lingo - Ruby gems, Perl modules, Java JARs, etc.</p>
<p>You can send these files to a friend, email them, upload them to Dropbox, or post them on your blog. You can upload the source code to GitHub, with instructions on how to manually compile them. But then how do people find them, report bugs, get notified of updates, target a specific version&hellip;?</p>
<p>If only we had some way to <em>manage</em> all these packages. 😏</p>
<hr>

<h2 class="relative group">What&rsquo;s a package manager?
    <div id="whats-a-package-manager" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whats-a-package-manager" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Your (library, gem, module, JAR, whatever) can be shared with other projects, which reference them and call their publicly accessible functions <em>(kind of like an API),</em> but you have to decide how to make that code available in the first place.</p>
<ul>
<li>Provide the source code, so they can manually compile it.</li>
<li>Provide the compiled code, so they can just drop it in their project.</li>
<li>Provide a <em>link</em> to the source code (GitHub). Some tools, like <a href="https://www.rebar3.org/docs/configuration/dependencies/"  target="_blank" rel="noreferrer">rebar3</a> for Erlang, can reference a GitHub link directly and include it in the build process, even targeting a specific branch or tag.</li>
<li>Host the compiled code in some central location, where others can discover it, reference it, be notified of updates, maybe even discuss it and get help.</li>
</ul>
<p>This is the problem a package manager solves, to one degree or another. It maintains each version of your code, along with metadata you provide about it, and makes it accessible to others. You could set one up on your machine, or on a corporate intranet, but there&rsquo;s a lot of good public ones out there, usually organized around the language you&rsquo;re working in.</p>
<ul>
<li>For .NET, there&rsquo;s <a href="https://docs.microsoft.com/en-us/nuget/what-is-nuget"  target="_blank" rel="noreferrer">NuGet packages</a> and <a href="https://www.nuget.org/"  target="_blank" rel="noreferrer">NuGet.org</a>.</li>
<li>For Ruby, there&rsquo;s <a href="https://guides.rubygems.org/what-is-a-gem/"  target="_blank" rel="noreferrer">gems</a> and <a href="https://rubygems.org"  target="_blank" rel="noreferrer">RubyGems.org</a>.</li>
<li>nodejs has <a href="https://www.npmjs.com/"  target="_blank" rel="noreferrer">npm</a>, Python <a href="https://pypi.org/"  target="_blank" rel="noreferrer">PyPi</a>, Java <a href="https://search.maven.org/"  target="_blank" rel="noreferrer">Maven</a>, PHP <a href="https://packagist.org/"  target="_blank" rel="noreferrer">Composer</a>, Perl <a href="https://www.cpan.org/"  target="_blank" rel="noreferrer">CPAN</a>, <em>etc&hellip;</em></li>
</ul>
<hr>

<h2 class="relative group">A few words about NuGet
    <div id="a-few-words-about-nuget" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#a-few-words-about-nuget" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>NuGet is the defacto package manager for the .NET ecosystem, and the one I&rsquo;m most familiar with. Actually, the term &ldquo;NuGet&rdquo; refers to a few things, all closely related.</p>
<ul>
<li>It&rsquo;s a package management system for .NET.</li>
<li>NuGet.org is the site that hosts these packages.</li>
<li>It&rsquo;s also a Visual Studio extension that&rsquo;s used to reference those packages, installed in VS by default.</li>
</ul>
<p>When you reference a NuGet package from Visual Studio and then build, VS helpfully pulls down the projects you&rsquo;re depending on, as well as any projects that <em>those</em> projects depend on, etc, etc.</p>
<p>If you try publishing your own NuGet package, you can expect to see something like this. The first two images are in Visual Studio, while the last two show the contents of the generated <code>nupkg</code> file, and the <code>nuspec</code> file it contains.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/whats-a-package-manager/nuget-package-config-in-project-properties.png"
    width="1202"
      height="867"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/whats-a-package-manager/nuget-config-edit-project.png"
    width="1383"
      height="841"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/whats-a-package-manager/nuget-generated-package.png"
    width="1165"
      height="480"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/whats-a-package-manager/nuget-nuspec-file.png"
    width="1144"
      height="627"></figure>
<p>Generating a NuGet package with Visual Studio</p>
<p>The metadata file in the upper right is typical of most package managers. You have to be able to tell the site, and people who might want to use your code, a little bit about your code. This might be a version, description, some tags the site could use to categorize it, etc.</p>
<p>If you want to learn more, Microsoft has a great set of intro docs covering the basics of how to use it. I found them informative even though I&rsquo;m already familiar with it. You&rsquo;ll need <a href="https://visualstudio.microsoft.com/vs/"  target="_blank" rel="noreferrer">Visual Studio</a> to get the most out of it.</p>
<ul>
<li><a href="https://docs.microsoft.com/en-us/nuget/what-is-nuget"  target="_blank" rel="noreferrer">What is NuGet and what does it do?</a></li>
<li><a href="https://docs.microsoft.com/en-us/nuget/quickstart/install-and-use-a-package-in-visual-studio"  target="_blank" rel="noreferrer">Install and use a NuGet package in Visual Studio</a></li>
<li><a href="https://docs.microsoft.com/en-us/nuget/quickstart/create-and-publish-a-package-using-visual-studio?tabs=netcore-cli"  target="_blank" rel="noreferrer">Create and publish a .NET Standard NuGet package - visual-studio on Windows</a></li>
<li><a href="https://docs.microsoft.com/en-us/nuget/reference/nuspec"  target="_blank" rel="noreferrer">.nuspec File Reference for NuGet</a></li>
</ul>
<p>If you have any questions or need something clarified, just leave a comment below and I&rsquo;ll try to help out. Happy coding!</p>
]]></content:encoded><media:content url="https://grantwinney.com/whats-a-package-manager/feature.webp" medium="image" type="image/webp"/></item><item><title>Using the GraphiQL IDE to access a GraphQL API</title><link>https://grantwinney.com/using-graphiql-to-access-a-graphql-api/</link><pubDate>Thu, 26 Sep 2019 03:38:30 +0000</pubDate><guid>https://grantwinney.com/using-graphiql-to-access-a-graphql-api/</guid><description>GraphQL is bundled with GraphiQL, which lets us run queries right in the browser! Let&amp;rsquo;s see how GitHub uses it and try kicking the tires.</description><content:encoded><![CDATA[<p>In <a href="https://grantwinney.com/what-is-graphql-and-how-does-it-differ-from-rest/"  target="_blank" rel="noreferrer">my last post</a>, I just wanted to understand what GraphQL is versus REST. I learned that it&rsquo;s about flexibility, and getting exactly what you need in the format you need it. Now I want to look at an actual implementation.</p>
<p>Facebook spent years developing GraphQL and then open-sourced it. When GitHub began moving to a new version of their API several years ago, <a href="https://github.blog/2016-09-14-the-github-graphql-api/"  target="_blank" rel="noreferrer">they migrated from REST to GraphQL</a>. Their reasoning is very similar to what I&rsquo;ve read elsewhere and experienced myself.</p>
<blockquote><p>Our responses were bloated and filled with all sorts of <code>*_url</code> hints in the JSON responses to help people continue to navigate through the API to get what they needed. Despite all the information we provided, we heard from integrators that our REST API also wasn’t very flexible. It sometimes required two or three separate calls to assemble a complete view of a resource. It seemed like our responses simultaneously sent too much data <em>and</em> didn’t include data that consumers needed.</p>
</blockquote>
<h2 class="relative group">Queries
    <div id="queries" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#queries" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>One of the tools available with GraphQL is <a href="https://github.com/graphql/graphiql/tree/master/packages/graphiql#readme"  target="_blank" rel="noreferrer">GraphiQL</a>, which allows users of your API to design queries right in the browser and see immediate results. This is a tremendous time-saver!</p>
<p>With REST, I&rsquo;ve always used <a href="https://www.getpostman.com/"  target="_blank" rel="noreferrer">Postman</a> to manage my queries without having to have a fullblown app in place from the get-go, but it still involves trial and error. I read the API&rsquo;s docs, figure out what to call and how, get the result and inspect it, make adjustments, make more calls to other endpoints, etc. Occasionally, an API provider produces their own &ldquo;API Explorer&rdquo; of sorts, if we&rsquo;re lucky.</p>
<p>GraphiQL is a ready-to-go &ldquo;API Explorer&rdquo;. It integrates API docs right into the experience with &ldquo;typeaheads&rdquo; (similar to intellisense in Visual Studio), which helps us figure out what to query and then shows us the results. Let&rsquo;s try out <a href="https://developer.github.com/v4/explorer/"  target="_blank" rel="noreferrer">GitHub&rsquo;s API explorer</a>.</p>
<ul>
<li>Allow it to access your GitHub account&hellip; even though it <em>is</em> GitHub. 🤨</li>
<li>Click the &ldquo;Execute Query&rdquo; triangle in the upper-left to run the default query&hellip; info about you!</li>
<li>Click the &ldquo;Docs&rdquo; button on the right side to view the API documentation. Note the two root types - query and mutation. A query is similar to a REST <code>GET</code>, while mutation is similar to <code>POST</code> or <code>DELETE</code>. Stick with query for now.</li>
<li>As you drill down, you&rsquo;ll see objects to query, parameters to restrict your queries, and other child objects. It&rsquo;s like you&rsquo;re getting to browse their database!</li>
</ul>
<p>Here&rsquo;s a few queries I tried running&hellip;</p>
<p>First, the &ldquo;Hello World!&rdquo; of GraphQL queries:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-graphiql-to-access-a-graphql-api/default-query.png"
    width="1920"
      height="908"></figure>
<p>A query for my own repos&rsquo; URLs, and the homepages of repos I&rsquo;ve forked:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-graphiql-to-access-a-graphql-api/repos.png"
    width="1920"
      height="948"></figure>
<p>My bio, my followers, my <em>followers&rsquo;</em> followers bios&hellip; why? Because I can. 😑</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-graphiql-to-access-a-graphql-api/followers-all-the-way-down.png"
    width="1920"
      height="948"></figure>

<h2 class="relative group">Mutations
    <div id="mutations" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#mutations" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Once you&rsquo;ve run a few queries, try out mutations. You&rsquo;ve already granted access to everything in your account to the tool, so you can update (mutate) pretty much anything in your account. Here&rsquo;s a short screen capture where I&rsquo;m performing two actions:</p>
<ol>
<li>A query to get the ID associated with an open issue in one of my repos</li>
<li>A mutation to add a few reactions to the issue</li>
</ol>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-graphiql-to-access-a-graphql-api/first_mutation.gif"
    width="1893"
      height="794"></figure>
<p>As with running the queries, having the documentation on the right side is great. I was able to drill down and see that <code>addReaction</code> requires an <code>AddReactionInput</code> type, which consists of three things - and only two are required (notice the <code>!</code> ).</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-graphiql-to-access-a-graphql-api/mutation-docs.png"
    width="1345"
      height="678"></figure>
<p>The only thing that seemed unintuitive was the requirement to have a body in the mutation, as if it&rsquo;s required to return <em>something</em> even though if we were doing a REST <code>POST</code> we wouldn&rsquo;t care about anything except a return code of <code>200 OK</code>.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-graphiql-to-access-a-graphql-api/mutation_nobody.png"
    width="682"
      height="259"></figure>
]]></content:encoded><media:content url="https://grantwinney.com/using-graphiql-to-access-a-graphql-api/feature.webp" medium="image" type="image/webp"/></item><item><title>What is GraphQL and how does it differ from REST?</title><link>https://grantwinney.com/what-is-graphql-and-how-does-it-differ-from-rest/</link><pubDate>Sat, 21 Sep 2019 02:48:40 +0000</pubDate><guid>https://grantwinney.com/what-is-graphql-and-how-does-it-differ-from-rest/</guid><description>GraphQL is an alternative for REST, not a replacement. Let&amp;rsquo;s take a brief look at how they differ.</description><content:encoded><![CDATA[<p>Finding a new <a href="https://grantwinney.com/what-is-an-api/"  target="_blank" rel="noreferrer">API</a> can be like discovering a gateway to a vast amount of data that might be otherwise inaccessible. A couple of my favorites have led to <a href="https://grantwinney.com/what-is-nasa-api/"  target="_blank" rel="noreferrer">photos from the Mars Rover</a> and <a href="https://grantwinney.com/searching-historical-newspapers-with-the-chronicling-america-api/"  target="_blank" rel="noreferrer">300 year old newspapers</a>. These APIs are often implemented using <a href="https://en.wikipedia.org/wiki/Representational_state_transfer"  target="_blank" rel="noreferrer">REST</a>, the standard for making web resources accessible for over a decade, but there&rsquo;s a different way to access resources called <a href="https://en.wikipedia.org/wiki/GraphQL"  target="_blank" rel="noreferrer">GraphQL</a>.</p>
<p>The first time I heard about GraphQL was a few years ago in some article about Facebook, but I didn&rsquo;t pay much attention to it at the time. Thanks to frequent data breaches, one doesn&rsquo;t really need an API to access Facebook&rsquo;s data.. but I digress.</p>
<p>I didn&rsquo;t realize until recently that Facebook actually developed it and, a few years ago, open sourced it too. Let&rsquo;s take a closer look at what it is and how we might use it.</p>

<h2 class="relative group">GraphQL vs REST
    <div id="graphql-vs-rest" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#graphql-vs-rest" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>As much as we&rsquo;d like to think there&rsquo;s one &ldquo;best&rdquo; way to do things, GraphQL is an alternative for REST, not a replacement. And while <a href="https://phil.tech/api/2017/01/24/graphql-vs-rest-overview"  target="_blank" rel="noreferrer">there&rsquo;s quite a few differences between them</a>, the major difference is that GraphQL lets us build a query to get exactly (and only) the data we&rsquo;re interested in.</p>
<p>An API that implements the REST interface allows us to (for example) very easily <code>GET</code> some data about an entity. By default, we get the whole shebang. If we can limit or otherwise customize the returned dataset somehow, it&rsquo;s only because the developers wrote code to explicitly support those limits and customizations. How that looks will differ with every API.</p>
<p>Take the <a href="https://docs.ghost.org/content-api"  target="_blank" rel="noreferrer">Ghost API</a> as an example, which is built into the blog engine I use for this site. You can request data on individual posts, authors, etc, which is all standard fare. On top of that though, the devs provided a few query parameters to affect the returned data:</p>
<ul>
<li><code>include</code> returns <em>more</em> data, like full author details for a post</li>
<li><code>fields</code> returns <em>less</em> data, by specifying which fields should be returned</li>
<li><code>formats</code> returns <em>more</em> data, by returning data in multiple formats</li>
<li><code>filter</code> returns <em>less</em> data, by filtering by certain attributes</li>
<li><code>limit</code> and <code>page</code> return <em>less</em> data by implementing paging</li>
<li><code>order</code> doesn&rsquo;t even filter data, but it affects paging results so it&rsquo;s tacked on</li>
</ul>
<p>Each of those items had to be explicitly coded, and while the Ghost API offers more customization than a lot of other APIs I&rsquo;ve seen, they can only offer so much. If I want to &ldquo;filter&rdquo; by an attribute they don&rsquo;t support, I have to request more data than I need and filter it out locally. If I want to &ldquo;include&rdquo; some other entity they didn&rsquo;t plan for, I have to make multiple requests and stitch things together client side.</p>
<p>The flexibility in GraphQL is that it allows (forces, really) a client to create their own query to get <em>just</em> the data they want, in <em>just</em> the way they want it. And it also provides certain tools to enable the server to provide that data and only that data. In other words, GraphQL out-of-the-box returns the smallest amount of data needed, whereas REST returns the largest.</p>
<p>There seems to be a pretty rich toolset for GraphQL, including:</p>
<ul>
<li>Some sort of <a href="https://github.com/graphql/graphiql"  target="_blank" rel="noreferrer">IDE</a> in the browser to play around with GraphQL</li>
<li>Server libraries in <a href="https://graphql.org/code/#c-net"  target="_blank" rel="noreferrer">C#</a>, <a href="https://graphql.org/code/#python"  target="_blank" rel="noreferrer">Python</a>, and more <em>(what&rsquo;s a server library?)</em></li>
<li>GraphQL clients for <a href="https://graphql.org/code/#c-net-1"  target="_blank" rel="noreferrer">C#</a> and <a href="https://graphql.org/code/#python-1"  target="_blank" rel="noreferrer">Python</a> <em>(what do they mean by client??)</em></li>
<li>Various other <a href="https://graphql.org/code/#tools"  target="_blank" rel="noreferrer">tools</a> <em>(what do these do?!?)</em></li>
</ul>
<p>Not to mention there&rsquo;s an entire <a href="https://github.com/graphql/graphql-spec"  target="_blank" rel="noreferrer">GraphQL spec</a> to check out and, uh, <a href="https://github.com/chentsulin/awesome-graphql"  target="_blank" rel="noreferrer"><em>this</em></a>. 😵</p>
<p>Lastly, there&rsquo;s a <a href="https://www.youtube.com/watch?v=VjXb3PRL9WI"  target="_blank" rel="noreferrer">quick overview of GraphQL in 15 minutes</a> on YouTube, and here&rsquo;s a piece I wrote about <a href="https://grantwinney.com/using-graphiql-to-access-a-graphql-api/"  target="_blank" rel="noreferrer">accessing the GraphQL API</a>.</p>
]]></content:encoded><media:content url="https://grantwinney.com/what-is-graphql-and-how-does-it-differ-from-rest/feature.webp" medium="image" type="image/webp"/></item><item><title>Create a TOTP 2FA code for your app</title><link>https://grantwinney.com/how-to-create-a-2fa-code-for-your-app/</link><pubDate>Tue, 13 Aug 2019 03:54:37 +0000</pubDate><guid>https://grantwinney.com/how-to-create-a-2fa-code-for-your-app/</guid><description>I use 2FA on every site that supports it, but I&amp;rsquo;d never given much thought to how a 2FA code is generated. Let&amp;rsquo;s learn how!</description><content:encoded><![CDATA[<p>I&rsquo;ve been using 2FA on <a href="https://twofactorauth.org/"  target="_blank" rel="noreferrer">every site that supports it</a> for quite some time, but I&rsquo;ve never given much thought to how a 2FA code is created. I enable it, scan the QR code, and print the backup codes. The rest is magic. 🧙‍♂️</p>
<p>But no more! Today is the day we figure out how to generate a 2FA code&hellip;</p>

<h2 class="relative group">A few basics&hellip;
    <div id="a-few-basics" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#a-few-basics" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you&rsquo;ve used 2FA and know what TOTP is, skip this section. For everyone else&hellip;</p>

<h3 class="relative group">What is 2FA?
    <div id="what-is-2fa" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-is-2fa" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>For the totally uninitiated it just means &ldquo;two factor authentication&rdquo;, or a second way to authenticate yourself, often by generating a random code using an app on your phone. More generically, it means proving who you are by providing a combination of something you <em>have,</em> something you <em>know,</em> something you <em>are,</em> etc.</p>
<p>Even if you don&rsquo;t think you&rsquo;ve ever used 2FA before, if you have&hellip;</p>
<ul>
<li>Ever inserted an ATM card (have) <em>and</em> entered a PIN (know)?</li>
<li>Or inserted a credit card (have) <em>and</em> entered a zip code (know)?</li>
<li>Presented your insurance card (have) <em>and</em> verified your birth date (know)?</li>
<li>Shown your top-secret government id (have) <em>and</em> submitted to a retinal scan (are)?</li>
</ul>
<p>This stuff used to apply to relatively few things though - a bank account and a few credit cards. Now we have dozens (or hundreds) of logins, each of which secures a site that holds some of our private data, and the more ways you have to prove who you are, the less likely someone can pretend to be you.</p>
<p>There&rsquo;s <a href="https://auth0.com/learn/two-factor-authentication/"  target="_blank" rel="noreferrer">a lot of ways to do 2FA</a>, but some are more common than others&hellip;</p>
<ul>
<li>Enter a password (know), and answer canned questions (know). <em>(awful - check out how many</em> <a href="https://haveibeenpwned.com/PwnedWebsites"  target="_blank" rel="noreferrer"><em>known data breaches</em></a> <em>included security questions and answers, which most people reuse)</em></li>
<li>Enter a password (know), and answer custom questions (know). <em>(meh)</em></li>
<li>Enter a pw (know), then a code that&rsquo;s texted to your phone (have). <em>(</em><a href="https://www.wired.com/2016/06/hey-stop-using-texts-two-factor-authentication/"  target="_blank" rel="noreferrer"><em>not the best</em></a><em>)</em></li>
<li>Enter a pw (know), then a randomly generated, one-time code from your phone that expires in 30 seconds (have), aka TOTP.</li>
</ul>

<h3 class="relative group">What is TOTP?
    <div id="what-is-totp" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-is-totp" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>A time-based one-time password (TOTP) is just one way to do 2FA&hellip; but it&rsquo;s very common. A TOTP only works for a certain period of time (usually 30 seconds), and it only works once (so trying to log in twice with the same password should fail).</p>

<h3 class="relative group">What&rsquo;s it look like?
    <div id="whats-it-look-like" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whats-it-look-like" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>A website generates a QR code to scan with an app like <a href="https://github.com/andOTP/andOTP"  target="_blank" rel="noreferrer">andOTP</a>, <a href="https://support.microsoft.com/en-us/account-billing/download-microsoft-authenticator-351498fc-850a-45da-b7b6-27e523b8702a"  target="_blank" rel="noreferrer">Microsoft Authenticator</a>, <a href="https://support.1password.com/one-time-passwords/"  target="_blank" rel="noreferrer">1Password</a>, etc. The QR code represents a URI with a few pieces of data, including a random string that&rsquo;s unique to you, which is encoded in base32 <em>(more on that later)</em>. From then on, the app generates a new random code every 30 seconds, which you use at login. And in case your <a href="https://time.com/4485396/samsung-note-7-battery-fire-why/"  target="_blank" rel="noreferrer">phone catches fire</a>, they provide some recovery codes to print and save.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-create-a-2fa-code-for-your-app/qr-code-setup.png"
    width="1206"
      height="1000"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-create-a-2fa-code-for-your-app/totp-backup-codes.png"
    width="1342"
      height="958"></figure>
<p>If someone guesses your password, or hacks your email and requests a password recovery, they still can&rsquo;t login unless they also have access to your 2FA app (which usually means having physical access to your phone as well).</p>
<hr>

<h2 class="relative group">How are they generated?
    <div id="how-are-they-generated" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#how-are-they-generated" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>From the user&rsquo;s perspective, it starts with a QR code that represents a URI, as I mentioned above. Using a service like <a href="https://zxing.org/w/decode.jspx"  target="_blank" rel="noreferrer">ZXing Decoder</a> on one of those QR codes, we see it holds a few pieces of data, as outlined here: <a href="https://github.com/google/google-authenticator/wiki/Key-Uri-Format"  target="_blank" rel="noreferrer">Key URI Format</a></p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-create-a-2fa-code-for-your-app/qr-code-decrypted.png"
    width="1004"
      height="489"></figure>

<h3 class="relative group">Dissecting a QR code
    <div id="dissecting-a-qr-code" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#dissecting-a-qr-code" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Let&rsquo;s take a minute to break that <em>&ldquo;Raw text&rdquo;</em> line down:</p>
<ul>
<li>Type: The &ldquo;totp&rdquo; indicates this is a time-based one-time code.</li>
<li>Label: The &ldquo;csinfotest&rdquo; is a label used to identify the code in the authenticator app.</li>
<li>Issuer: The &ldquo;Namecheap - &hellip;.&rdquo; param is also a label. <em>(</em><a href="https://github.com/google/google-authenticator/wiki/Key-Uri-Format#issuer"  target="_blank" rel="noreferrer"><em>because reasons</em></a><em>&hellip;)</em></li>
<li>Secret: A base32 encoded key <em>(more on that in a moment)</em> that&rsquo;s unique for each user, and used in conjunction with the current time to generate the TOTP code.</li>
</ul>
<p><em>&ldquo;But&rdquo;,</em> you might say, <em>&ldquo;that Key URI Format webpage mentions other fields too!&rdquo;</em></p>
<p>Yep, there are optional parameters; but when I tested a code that used them, the results were inconsistent. Google&rsquo;s and Microsoft&rsquo;s apps ignored the extra params, while andOTP and LastPass show an 8 digit number for 15 seconds, but not the <em>same</em> number. 🤔</p>
<p>For now, I&rsquo;d suggest just setting the 4 basic fields, to generate a code that&rsquo;ll work everywhere.</p>

<h3 class="relative group">What is base32 encoding?
    <div id="what-is-base32-encoding" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-is-base32-encoding" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Of the four pieces of data mentioned above, the one that makes this whole thing work is the base32 &ldquo;secret&rdquo; string, so let&rsquo;s review what base32 encoding / decoding is.</p>
<p>Basically, it means converting a string that could have any data in it to a string that&rsquo;s a set of 32 characters. The exact set of 32 characters can vary depending on the use case, but they&rsquo;re often selected for specific reasons, such as:</p>
<ul>
<li>a legacy system that can&rsquo;t handle certain characters,</li>
<li>the need to be easily human readable (so not using <code>O</code> and <code>0</code>, <code>l</code> and <code>1</code>, etc),</li>
<li>use as a file name (so not <code>\</code> or <code>/</code>)</li>
</ul>
<p>If you&rsquo;re having trouble sleeping, check out <a href="https://tools.ietf.org/html/rfc4648#section-3.3"  target="_blank" rel="noreferrer">RFC 4648</a> concerning base16, base32, and base64, particularly section 3.4 on choosing the alphabet. It&rsquo;s riveting.</p>
<p>For a more practical example, let&rsquo;s check out a <a href="https://www.browserling.com/tools/base32-encode"  target="_blank" rel="noreferrer">base32 encoder</a> that uses the set of standard ASCII characters <code>[a-z0-9]</code> except for the letters <code>i</code>, <code>l</code>, <code>o</code>, and <code>s</code> (for readability). <a href="https://www.browserling.com/tools/base32-encode"  target="_blank" rel="noreferrer">Encode a string</a> with <em>extended</em> ASCII characters, such as <em>&ldquo;Écrire, c&rsquo;est une façon de parler sans être interrompu.&rdquo;</em>, and we&rsquo;ll get a string like:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">t5hq4ubjcmp20rt7cntq883ndtjj0tk1wxqpw834cmg70rbjdhjq483kc5q7687aeht6a839dtu6awkjdxpq0x9e</code></pre></div>
<p><a href="https://www.browserling.com/tools/base32-decode"  target="_blank" rel="noreferrer">Decode it</a> and we should get back the original result.</p>

<h3 class="relative group">Generating our own codes
    <div id="generating-our-own-codes" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#generating-our-own-codes" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Hopefully it&rsquo;s becoming apparent that creating our own 2FA codes isn&rsquo;t terribly complicated. The important part is generating a good random string for each user (which we&rsquo;ll store), and being able to base32 encode it.</p>
<p>Looking at those 4 basic fields again, here&rsquo;s what we&rsquo;ll use:</p>
<ul>
<li>Type: &ldquo;totp&rdquo;</li>
<li>Issuer: A product name, like &ldquo;Acme&rdquo;</li>
<li>Label: The format &ldquo;Product:Account Name&rdquo;, like &ldquo;Acme:jdoe@gmail.com&rdquo;</li>
<li>Secret: A random string <em>(Google calls it an &ldquo;arbitrary key value&rdquo;),</em> base32 encoded so that users who can&rsquo;t scan the QR code can still type the secret in manually</li>
</ul>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">otpauth://totp/${encodeURI(label)}?secret=${secret}&amp;issuer=${encodeURI(issuer)};</code></pre></div>
<p>Once we&rsquo;ve got everything, we need a library that can convert it to a QR code. For JavaScript, check out the <a href="https://stefansundin.github.io/2fa-qr/"  target="_blank" rel="noreferrer">2FA QR code generator</a>. For C#, there&rsquo;s the <a href="https://www.nuget.org/packages/QRCoder/"  target="_blank" rel="noreferrer">QRCoder</a> library. We should avoid reinventing the wheel where we can, and look for established libraries when possible.</p>
<p>A user scans the QR code, at which point their app and our system will both be storing the secret code. No matter what 2FA app they used to scan the code originally, it should be capable of combining the secret with the current time and generating a single-use code that&rsquo;s good for 30 seconds.</p>

<h3 class="relative group">Authenticating users (verifying TOTP)
    <div id="authenticating-users-verifying-totp" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#authenticating-users-verifying-totp" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The very last part of the process is validating that the code is good. When someone enters a 2FA code from their app, we need to retrieve their secret from the database and generate the same code according to a certain algorithm.</p>
<p>You can try to <a href="https://en.wikipedia.org/wiki/Time-based_One-time_Password_algorithm#Algorithm"  target="_blank" rel="noreferrer">implement that algorithm</a> yourself (not recommended) or just look for a reliable library in the language of your choice like <a href="https://jsfiddle.net/russau/ch8PK/"  target="_blank" rel="noreferrer">JavaScript</a> or <a href="https://github.com/kspearrin/Otp.NET"  target="_blank" rel="noreferrer">Otp.NET in C#</a>. Since the time on a server versus a user&rsquo;s device may be slightly out of sync, it&rsquo;s possible they&rsquo;ll get a code that&rsquo;s different than the one we calculate, so it&rsquo;s a good idea to calculate several codes (one for the <em>previous</em> 30 seconds, and one for the <em>next</em> 30 seconds) and validate for all of them. It&rsquo;s also worth noting that if they&rsquo;re using a device that has the wrong time, their generated 2FA codes might never validate.</p>
<p>If you&rsquo;d like to see an implementation written in C#, <a href="https://grantwinney.com/"  target="_blank" rel="noreferrer">check this out</a>.</p>
<hr>

<h2 class="relative group">A practical example in C#
    <div id="a-practical-example-in-c" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#a-practical-example-in-c" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I create an example app in C#. The label, issuer and secret will be prepopulated at startup, but feel free to change them. As you do, the QR code is regenerated.</p>
<p>When you&rsquo;re ready, scan it with your phone to add it like any other 2FA code.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-create-a-2fa-code-for-your-app/phonescanqrcode.jpg"
    width="1080"
      height="1179"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-create-a-2fa-code-for-your-app/phoneqrcodeadded.jpg"
    width="1080"
      height="686"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-create-a-2fa-code-for-your-app/codevalid.png"
    width="1036"
      height="706"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-create-a-2fa-code-for-your-app/codeinvalid.png"
    width="1036"
      height="706"></figure>
<p>Enter the code from your phone into the bottom field (left image) to verify it&rsquo;s valid; enter an invalid TOTP code (right image) and it tells you.</p>
<p>The numbers at the very bottom, in parentheses, represent the number of steps, or possible codes that could&rsquo;ve been generated since the Unix epoch in 1970:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">= seconds since unix epoch / time between codes, usually 30 seconds</code></pre></div>
<p>One of the recommendations, as I mentioned previously, is to allow ±1 step to handle a case where the server and the user&rsquo;s device have slightly different times, or the user is a bit slow to enter the code and it changes in the meantime. That&rsquo;s why I display three codes <em>(past, current, next).</em></p>
<p>During the elapsed time between the two screenshots, you can see a new &ldquo;Current Code&rdquo; has been generated, and the previous &ldquo;Current Code&rdquo; is now the current &ldquo;Previous Code&rdquo;. lol</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-create-a-2fa-code-for-your-app/spaceballs.png"
    width="461"
      height="257"></figure>
]]></content:encoded><media:content url="https://grantwinney.com/how-to-create-a-2fa-code-for-your-app/feature.webp" medium="image" type="image/webp"/></item><item><title>Hide Comments Everywhere</title><link>https://grantwinney.com/hide-comments-everywhere/</link><pubDate>Mon, 08 Jul 2019 20:14:34 +0000</pubDate><guid>https://grantwinney.com/hide-comments-everywhere/</guid><description>Hides various commenting systems across the web, including (but not limited to) Disqus, YouTube, various news sites and forums, etc.</description><content:encoded><![CDATA[<p>The comments sections on most major news outlets and social media sites are full of vitriol. I just wanted the content, so I wrote an extension that hides various commenting systems across the web, including (but not limited to) Disqus, YouTube, Instagram, replies on Reddit and Twitter, etc.</p>
<p><em>(Getting it to work on Facebook was a pain, and required constant updates, so I don&rsquo;t really bother trying anymore&hellip; I recommend trying</em> <a href="https://chrome.google.com/webstore/detail/undistracted-hide-faceboo/pjjgklgkfeoeiebjogplpnibpfnffkng"  target="_blank" rel="noreferrer"><em>UnDistracted</em></a><em>.)</em></p>
<p>Available for <a href="https://addons.mozilla.org/en-US/firefox/addon/hide-comments-everywhere/"  target="_blank" rel="noreferrer">Firefox</a> and <a href="https://chrome.google.com/webstore/detail/hide-comments-everywhere/bmhkdngdngchlneelllmdennfpmepbnc"  target="_blank" rel="noreferrer">Chrome</a>, and works on Brave <em>(<a href="https://support.brave.com/hc/en-us/articles/360017909112-How-can-I-add-extensions-to-Brave-"  target="_blank" rel="noreferrer">natively</a>)</em>, Opera, and Edge too.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/hide-comments-everywhere/hide-comments-2.jpg"
    width="640"
      height="400"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/hide-comments-everywhere/popup.png"
    width="451"
      height="206"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/hide-comments-everywhere/options1.png"
    width="869"
      height="800"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/hide-comments-everywhere/options2.png"
    width="884"
      height="800"></figure>

<h2 class="relative group">How&rsquo;s it work?
    <div id="hows-it-work" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#hows-it-work" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>It checks whether or not comments should be blocked, based on a global set of definitions, in combination with your own preferences about which sites should show or hide comments.</p>
<p>Click on the icon in the toolbar to toggle showing/hiding comments for a site. You can choose whether your selections are remembered when you visit the site again with a setting on the options page.</p>
<p>When the page is loaded, the appropriate style sheet for the site is injected into the DOM, and then the style sheet is either enabled or disabled based on whether the comments should be shown or not.</p>
<p>If you&rsquo;re interested, check out the <a href="https://github.com/grantwinney/hide-comments-everywhere/"  target="_blank" rel="noreferrer">source code</a>.</p>

<h2 class="relative group">Contributions / Questions
    <div id="contributions--questions" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#contributions--questions" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you notice a commenting system that could be blocked by default, <a href="https://github.com/grantwinney/hide-comments-everywhere/issues/new"  target="_blank" rel="noreferrer">open an issue</a>. Include the website where you noticed it, and I&rsquo;ll try to tackle it as time permits.</p>
<p>If you&rsquo;re comfortable with RegEx and HTML/CSS, you could just create a pull request against <a href="https://github.com/grantwinney/hide-comments-everywhere/blob/master/sites.json"  target="_blank" rel="noreferrer">the file that defines which sites and html elements are blocked</a>.</p>
<p>Have a question, comment or request? <a href="https://github.com/grantwinney/hide-comments-everywhere/issues/new"  target="_blank" rel="noreferrer">Open a new issue</a> with as many details as possible. The more you let me know upfront, the less I&rsquo;ll have to ask later. I&rsquo;ll get to it as time permits.</p>

<h2 class="relative group">FAQs
    <div id="faqs" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#faqs" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Okay, these aren&rsquo;t <em>really</em> frequently asked questions. I mean, someone somewhere probably asked them.. at least once. They&rsquo;re definitely questions though.</p>

<h3 class="relative group">What permissions does it need?
    <div id="what-permissions-does-it-need" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-permissions-does-it-need" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>You&rsquo;ll be notified that it can &ldquo;read and change all your data on the websites you visit&rdquo; because that&rsquo;s how it works - it hides comments when the page loads so you don&rsquo;t have to see them.</p>

<h3 class="relative group">What&rsquo;s the order of preference?
    <div id="whats-the-order-of-preference" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whats-the-order-of-preference" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>In order to determine whether (and how) to hide or show comments on a particular site, Hide Comments Everywhere has a two-step process.</p>
<p>Step 1 is determining which styles to inject into the page.</p>
<ol>
<li>First, it checks the global definitions stored on GitHub (which are cached locally) for the current site. If a match is found, it applies the styles for that site.</li>
<li>Then, if a match isn&rsquo;t found, it applies a catch-all set of styles that are frequently used by commenting tools and content management systems.</li>
<li>Finally, it checks your personal blacklist to see if you&rsquo;ve set your own style for a particular site. If you have, it trumps anything set in step 1.</li>
</ol>
<p>Step 2 is determining whether or not to enable or disable those styles.</p>
<ol>
<li>First, it checks to see whether you&rsquo;ve previously clicked the &ldquo;toggle&rdquo; button to hide or show comments on a site.</li>
<li>Then, it checks your personal whitelist. If you&rsquo;ve included a site that should always show comments, it allows them even if you previously clicked the &ldquo;toggle&rdquo; button to hide them.</li>
<li>After that, it checks a global whitelist of sites that should always be allowed. There&rsquo;s only a few sites in that list, that don&rsquo;t play nicely with this addon, like GitHub. Anything there trumps your personal whitelist or toggling.</li>
<li>Finally, it checks your personal blacklist. If you&rsquo;ve got a site on there, it trumps everything, even if it&rsquo;s in the global whitelist. My thinking is, if I couldn&rsquo;t find a reasonable way to block comments on a few sites but you <em>did</em> somehow, well then&hellip; more power to ya.</li>
</ol>
<p>After the page loads, you can still click the &ldquo;toggle&rdquo; button (but the effect is only temporary if the site is in one of the whitelists or your blacklist, because when the page reloads, the above logic will run again).</p>

<h3 class="relative group">What kind of data does it save?
    <div id="what-kind-of-data-does-it-save" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-kind-of-data-does-it-save" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>It checks for updated definitions that I store on GitHub. If it finds updated definitions, it&rsquo;ll cache them in local storage, but it only checks once daily. You can open the Options page and force it to get the latest definitions if you want.</p>
<p>Everything else is stored in sync storage, like the settings and whitelist and blacklist on the Options page, and whether or not you toggled comments on or off for particular sites. If you don&rsquo;t use sync, those calls (iirc) just revert to local storage.</p>
<p>I don&rsquo;t use analytics or anything else to track what you&rsquo;re doing. If you want to share anything, there&rsquo;s myriad ways, like on GitHub, or by email, or just by leaving a comment below (yeah I know, kinda ironic).</p>
<p>Hope you find this addon as useful as I do!</p>
]]></content:encoded><media:content url="https://grantwinney.com/hide-comments-everywhere/feature.webp" medium="image" type="image/webp"/></item><item><title>Generate Links for Headers</title><link>https://grantwinney.com/generate-links-for-headers/</link><pubDate>Mon, 08 Jul 2019 19:16:16 +0000</pubDate><guid>https://grantwinney.com/generate-links-for-headers/</guid><description>Ever wanted to share a link, not just to a webpage, but to a particular section of a webpage? This extension automatically generates links for all headers on the page, to make it easier to share links that jump to a specific section.</description><content:encoded><![CDATA[<p>Ever wanted to share a link, not just to a webpage, but to a particular <em>section</em> of a webpage? GLfH automatically generates links for all headers on the page, to make it easier to share links that jump to a specific section.</p>
<p>It&rsquo;s available for <a href="https://addons.mozilla.org/en-US/firefox/addon/generate-links-for-headers/"  target="_blank" rel="noreferrer">Firefox</a> and <a href="https://chrome.google.com/webstore/detail/generate-links-for-header/dckfkngmahjdokkkmconmfjdmicjcmgf"  target="_blank" rel="noreferrer">Chrome</a> (so <a href="https://support.brave.com/hc/en-us/articles/360017909112-How-can-I-add-extensions-to-Brave-"  target="_blank" rel="noreferrer">Brave</a>, Opera, and Edge too).</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/generate-links-for-headers/image.png"
    width="752"
      height="227"></figure>

<h2 class="relative group">How&rsquo;s it work?
    <div id="hows-it-work" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#hows-it-work" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Most of the time, when you see a header on a webpage, it has an ID associated with it. You can use the ID to create a link to that section, but that means viewing source code, finding the header element, and appending the ID to the URL before sharing it. It doesn&rsquo;t have to be that hard.</p>
<p>This extension scans the page and generates links for all headers on the page for you, assuming they have an ID or Name assigned.</p>
<ul>
<li>Hover over any header, and an anchor link will appear.</li>
<li>Click on the 🔗 link icon to navigate to that anchor, and to copy the link to your clipboard at the same time.</li>
</ul>
<p>If there&rsquo;s no ID or Name in the header tag, it will search any elements nested inside the header tag for the first one with an ID or Name assigned. Failing that, it looks at the immediate parent element (i.e. a header nested in a DIV). If it still finds nothing, then no link will be generated or shown.</p>
<p>You can <a href="https://grantwinney.com/automatically-adding-links-next-to-all-headers-on-the-page-a-chrome-extension/"  target="_blank" rel="noreferrer">read more details here</a>.</p>

<h2 class="relative group">Source Code
    <div id="source-code" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#source-code" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The <a href="https://github.com/grantwinney/generate-links-for-headers-in-chrome"  target="_blank" rel="noreferrer">source code</a> is available on GitHub.</p>

<h2 class="relative group">Permissions
    <div id="permissions" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#permissions" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>It needs to read and modify all pages, because that&rsquo;s what it does. It uses JavaScript to scan the page for headers with IDs (or Names), and then injects one link for each header that has it present.</p>

<h2 class="relative group">Contributions / Questions
    <div id="contributions--questions" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#contributions--questions" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you have a fix, open a PR in GitHub. If you have a question or comment, <a href="https://github.com/grantwinney/generate-links-for-headers-in-chrome/issues/new"  target="_blank" rel="noreferrer">open an issue</a>. I&rsquo;ll get to things as time permits.</p>
]]></content:encoded><media:content url="https://grantwinney.com/generate-links-for-headers/feature.webp" medium="image" type="image/webp"/></item><item><title>What is minification vs obfuscation?</title><link>https://grantwinney.com/minification-vs-obfuscation/</link><pubDate>Fri, 14 Jun 2019 16:14:00 +0000</pubDate><guid>https://grantwinney.com/minification-vs-obfuscation/</guid><description>Mozilla announced they&amp;rsquo;ll no longer accept extensions with obfuscated code. It&amp;rsquo;s good news for users, maybe not so much for developers. Obfuscated code is (intentionally) nearly impossible to understand, and could easily be malicious. Let&amp;rsquo;s unpack and break down a few concepts.</description><content:encoded><![CDATA[<p>Mozilla recently announced that they&rsquo;ll <a href="https://blog.mozilla.org/addons/2019/05/02/add-on-policy-and-process-updates/"  target="_blank" rel="noreferrer">no longer accept extensions with obfuscated code</a>. This is good news for anyone who uses browser extensions in Firefox, since such code is <em>(intentionally)</em> nearly impossible to understand, and could easily (but not necessarily, as I&rsquo;ll explain later) be malicious.</p>
<p>From their <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Source_Code_Submission#Use_of_obfuscated_code"  target="_blank" rel="noreferrer">source code submission</a> guidelines:</p>
<blockquote><p>&ldquo;Extensions using obfuscated code are not permitted, regardless of whether they are hosted on addons.mozilla.org (AMO) or not. Extensions using obfuscated code are in violation of our Add-on Policy and are subject to being blocked.&rdquo;</p>
<p>&ldquo;Code is considered obfuscated if the logic and meaning is transformed in a way intended to make it difficult for a human to understand or reverse-engineer. A commonly used tool is <a href="https://obfuscator.io/"  target="_blank" rel="noreferrer">JavaScript Obfuscator</a>, and there are a number of other tools that can conceal code functionality.&rdquo;</p>
<p>&ldquo;Not all code that is difficult to read is obfuscated, and we specifically allow minified code to be submitted along with [source code]. With minification, the intent is to reduce the file size of the code. Techniques used here include reducing the length of variable and function names or removing whitespaces, comments and other redundant syntax elements.&rdquo;</p>
</blockquote><p>There&rsquo;s a few different concepts packed in there, so let&rsquo;s break it down. Keep in mind though that it mostly comes down to intent - one is for the user&rsquo;s benefit; the other is for the developer&rsquo;s.</p>
<hr>

<h2 class="relative group">What&rsquo;s minified code?
    <div id="whats-minified-code" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whats-minified-code" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Minifying your code means reducing its size to make it faster - download faster, parse faster, maybe even run faster. Let&rsquo;s just consider JavaScript, though these concepts could apply to any language.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// let the world know you exist
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">name</span> <span class="o">=</span> <span class="s1">&#39;Grant&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">alert</span><span class="p">(</span><span class="sb">`Hello World, this is </span><span class="si">${</span><span class="nx">name</span><span class="si">}</span><span class="sb">!`</span><span class="p">);</span></span></span></code></pre></div></div>
<p>I could &ldquo;minify&rdquo; this by hand, just by removing anything that&rsquo;s unnecessary - blank lines, comments, long variable names, etc. This does exactly the same thing while cutting the file size in half (97 bytes to 50).</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">n</span><span class="o">=</span><span class="s1">&#39;Grant&#39;</span><span class="p">;</span><span class="nx">alert</span><span class="p">(</span><span class="sb">`Hello World, this is </span><span class="si">${</span><span class="nx">n</span><span class="si">}</span><span class="sb">!`</span><span class="p">);</span></span></span></code></pre></div></div>
<p>While it doesn&rsquo;t matter so much for compiled languages like C#, it can make a large difference for JavaScript and other interpreted languages. Every space, extra line, or long variable name causes longer transmission times from some server to your computer, more data usage on a user&rsquo;s phone, and more time to load code into memory and run it.</p>
<p><a href="https://stackoverflow.com/q/1181447/301857"  target="_blank" rel="noreferrer">Minifying code has real benefits</a>, and there are plenty of <a href="https://www.hongkiat.com/blog/javascript-minifying-tools/"  target="_blank" rel="noreferrer">online tools to help you do it</a>.</p>
<hr>

<h2 class="relative group">What&rsquo;s obfuscated code?
    <div id="whats-obfuscated-code" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whats-obfuscated-code" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Obfuscating your code means making it as <em>unreadable</em> as possible, to thwart others from understanding it. Best case scenario, it might be to hide intellectual property. Worst case, it&rsquo;s to hide some malicious intent. Here&rsquo;s the same <em>&ldquo;hello world&rdquo;</em> script, run through the <a href="https://obfuscator.io/"  target="_blank" rel="noreferrer">JavaScript Obfuscator Tool</a>:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">_0x4c59</span><span class="o">=</span><span class="p">[</span><span class="s1">&#39;Grant&#39;</span><span class="p">,</span><span class="s1">&#39;Hello\x20World,\x20this\x20is\x20&#39;</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">_0x283f45</span><span class="p">,</span><span class="nx">_0x4aad64</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">_0x4bcaa6</span><span class="o">=</span><span class="kd">function</span><span class="p">(</span><span class="nx">_0x226500</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">        <span class="k">while</span><span class="p">(</span><span class="o">--</span><span class="nx">_0x226500</span><span class="p">){</span><span class="nx">_0x283f45</span><span class="p">[</span><span class="s1">&#39;push&#39;</span><span class="p">](</span><span class="nx">_0x283f45</span><span class="p">[</span><span class="s1">&#39;shift&#39;</span><span class="p">]());}</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="nx">_0x4bcaa6</span><span class="p">(</span><span class="o">++</span><span class="nx">_0x4aad64</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}(</span><span class="nx">_0x4c59</span><span class="p">,</span><span class="mh">0x14c</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">_0x46ec</span><span class="o">=</span><span class="kd">function</span><span class="p">(</span><span class="nx">_0x3f6a4c</span><span class="p">,</span><span class="nx">_0x5a9af4</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">    <span class="nx">_0x3f6a4c</span><span class="o">=</span><span class="nx">_0x3f6a4c</span><span class="o">-</span><span class="mh">0x0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">_0x24debb</span><span class="o">=</span><span class="nx">_0x4c59</span><span class="p">[</span><span class="nx">_0x3f6a4c</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">_0x24debb</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">name</span><span class="o">=</span><span class="nx">_0x46ec</span><span class="p">(</span><span class="s1">&#39;0x0&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">alert</span><span class="p">(</span><span class="nx">_0x46ec</span><span class="p">(</span><span class="s1">&#39;0x1&#39;</span><span class="p">)</span><span class="o">+</span><span class="nx">name</span><span class="o">+</span><span class="s1">&#39;!&#39;</span><span class="p">);</span></span></span></code></pre></div></div>
<p>Copy it and run it from the browser console - it does the same thing, but it&rsquo;s 5x the size of the original unminified version. This definitely doesn&rsquo;t help the end-user, and it could be doing anything in addition to displaying an alert.</p>
<hr>

<h2 class="relative group">Blurring the lines
    <div id="blurring-the-lines" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#blurring-the-lines" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>A minified file might be so difficult to understand that it might as well be obfuscated. Compare jQuery <a href="https://code.jquery.com/jquery-3.4.1.js"  target="_blank" rel="noreferrer">before</a> and <a href="https://code.jquery.com/jquery-3.4.1.min.js"  target="_blank" rel="noreferrer">after</a> minification. Good luck understanding the minified version. It still looks vaguely like English (and it&rsquo;s 30% of the original size!), but it&rsquo;d take a lot of work to figure out what it&rsquo;s doing.</p>
<p>On the other hand, obfuscators can usually minimize code too. Even with longer names and crazy looking code, the generated file will still be smaller than the original size&hellip; but nowhere near as small as the plain minified file.</p>
<hr>

<h2 class="relative group">Why bother at all?
    <div id="why-bother-at-all" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#why-bother-at-all" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you&rsquo;re an optimist, you might argue that someone wrote some JavaScript code (such as a browser extension) for profit, and is just trying to protect their intellectual property. Or a company may mandate that their programmers obfuscate all code, for the same reason. You can&rsquo;t hide your code forever by obfuscating it, but you can certainly slow down all but the most determined. A better option might be to have a thin UI that makes API calls to the proprietary stuff running on a server somewhere.</p>
<p>If you&rsquo;re a pessimist, then someone using an obfuscator is trying to hide something malicious. It might not be, but it&rsquo;s nearly impossible to know for sure (although watching the <a href="https://grantwinney.com/how-do-i-view-the-dev-console-in-my-browser/"  target="_blank" rel="noreferrer">browser console</a> might provide some clues). So Mozilla drew a line in the sand - if you&rsquo;re making your code nearly impossible to read, it raises bright red flags and they won&rsquo;t allow it.</p>
<p>Again, it mostly comes down to intent - one is for the user&rsquo;s benefit; the other is for the developer&rsquo;s. If you trust the author, that&rsquo;s one thing. But if you don&rsquo;t 100% trust them, then the obfuscated code could be doing just about anything.</p>
<hr>

<h2 class="relative group">What does this mean for developers?
    <div id="what-does-this-mean-for-developers" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-does-this-mean-for-developers" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>It depends on what you might be trying to do with your browser extension. If you&rsquo;re writing something you find useful and sharing it with the world for free, then you&rsquo;re unlikely to care about obfuscating your code. Likewise, if you&rsquo;re writing something as an employee that supports a larger service, it&rsquo;s likely everything really important is tucked away server side, and you&rsquo;re making API calls to it&hellip; so again, obfuscation isn&rsquo;t really necessary.</p>
<p>If you were planning on retiring on a fully client-side Firefox extension, and you protected your intellectual property with obfuscation, well&hellip;. you&rsquo;ll need to come up with another plan.</p>
<p>One of the more interesting parts is where they state, &ldquo;<em>regardless of whether they are hosted on addons.mozilla.org (AMO) or not&rdquo;.</em> It sounds as if not only are they going to block extensions in the Addons store, but they&rsquo;re going to block obfuscated addons from other sources too - which suggests they&rsquo;re running some heuristics to determine whether loaded extensions have obfuscated code and they&rsquo;ll just disable them. I&rsquo;m not sure what to make of that&hellip;</p>
<hr>

<h2 class="relative group">Further reading&hellip;
    <div id="further-reading" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#further-reading" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you want to learn more about different forms of obfuscation, check out:<br>
<a href="https://www.preemptive.com/obfuscation"  target="_blank" rel="noreferrer">What is Obfuscation and how does it apply to Java, Android, .NET and iOS applications?</a></p>
<p>And I highly, <em>highly</em> recommend (seriously, can&rsquo;t recommend it enough) checking out the <a href="https://github.com/Rob--W/crxviewer"  target="_blank" rel="noreferrer">Chrome Extension Source Viewer</a> (available for Firefox, Chrome and Opera, as well as any site that can load Chrome extensions like Brave). If you doubt an extension at all, you can easily check out its source code before adding it. I&rsquo;ve used it quite a few times, and discovered some very interesting and shady things in the process.</p>
]]></content:encoded><media:content url="https://grantwinney.com/minification-vs-obfuscation/feature.webp" medium="image" type="image/webp"/></item><item><title>Protect your GitHub account to keep your code secure</title><link>https://grantwinney.com/keeping-your-github-code-secure/</link><pubDate>Wed, 22 May 2019 02:23:58 +0000</pubDate><guid>https://grantwinney.com/keeping-your-github-code-secure/</guid><description>GitHub, GitLab, and Bitbucket just released a joint statement on a widespread ransomware attack that resulted in compromised accounts. That got me thinking, what can a person do to protect his or her code on GitHub? As it turns out, a lot&amp;hellip;</description><content:encoded><![CDATA[<p>A few days ago, <a href="https://github.blog/2019-05-14-git-ransom-campaign-incident-report/"  target="_blank" rel="noreferrer">GitHub</a> (along with <a href="https://bitbucket.org/blog/git-ransom-campaign-incident-report-atlassian-bitbucket-github-gitlab"  target="_blank" rel="noreferrer">Bitbucket</a> and <a href="https://about.gitlab.com/2019/05/14/git-ransom-campaign-incident-report-atlassian-bitbucket-github-gitlab/"  target="_blank" rel="noreferrer">GitLab</a>) reported that numerous users across their platforms had repos hacked, their code forcibly overwritten and held for ransom. You can read the whole thing in any of their blog posts (they&rsquo;re all the same), but here&rsquo;s a few takeaways:</p>
<p><strong>No one hacked GitHub et al directly.</strong><br>
<em>&ldquo;All account compromises were the result of credential leakage by users or other third-parties &hellip; using legitimate credentials including passwords, app passwords, API keys, and personal access tokens.&rdquo;</em></p>
<p><strong>One or more sites with access to repos either leaked data or were hacked.</strong><br>
<em>&quot;[W]e identified a third-party credential dump &hellip; where the account compromise activity had originated. That credential dump comprised roughly one third of the accounts affected by the ransom campaign.&quot;</em></p>
<p><strong>There are more ways than a large-scale hack to become a victim too.</strong><br>
<em>&quot;[C]ontinuous scanning for publicly exposed</em> <code>.git/config</code> <em>and other environment files has been and continues to be conducted by the same IP address that conducted the account compromises. These files can contain sensitive credentials and personal access tokens&hellip;&quot;</em></p>
<p>As a developer, there are few accounts I&rsquo;d hate to lose access to more than my GitHub account. Writing code is such an intangible thing - it can be tough enough to show results without having it deleted from under you (<a href="https://help.github.com/en/articles/removing-sensitive-data-from-a-repository"  target="_blank" rel="noreferrer">sometimes necessary</a> but in this case completely malicious). And for a technology company, it could be a death knell - their bread and butter suddenly gone from their control, their intellectual property exposed to the world.</p>
<hr>

<h1 class="relative group">Securing Your Code
    <div id="securing-your-code" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#securing-your-code" aria-label="Anchor">#</a>
    </span>
    
</h1>
<p>So on that cheery note, what can we do to avoid this? Well, as it turns out, <em>a lot.</em></p>

<h2 class="relative group">Choose a good password
    <div id="choose-a-good-password" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#choose-a-good-password" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The first and easiest thing we can do is to choose a good password. Ironically, we tend to be much worse at it than we think, selecting a shorter (thus easier to hack) password that&rsquo;s tough to remember, rather than a longer yet more memorable one.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="https://imgs.xkcd.com/comics/password_strength.png"
    ></figure>
<p><em>source:</em> <a href="https://xkcd.com/936"  target="_blank" rel="noreferrer"><em>xkcd.com</em></a> <em>(</em><a href="https://security.stackexchange.com/questions/6095/xkcd-936-short-complex-password-or-long-dictionary-passphrase"  target="_blank" rel="noreferrer"><em>good explanation</em></a><em>)</em></p>
<p>For the curious, try out a tool like Tyler Akins&rsquo;s <a href="http://rumkin.com/tools/password/passchk.php"  target="_blank" rel="noreferrer">password strength tester</a> to see how length can increase security even without adding special characters. For instance, an apparently random one presents 18.5 billion billion trillion possibilities, but a random string of words presents for more (7 trillion trillion trillion trillion).</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/keeping-your-github-code-secure/pass1-1.png"
    width="833"
      height="194"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/keeping-your-github-code-secure/pass2-1.png"
    width="830"
      height="193"></figure>
<p>If you save your passwords in a password manager like <a href="https://1password.com/"  target="_blank" rel="noreferrer">1Password</a> <em>(my personal favorite),</em> <a href="https://lastpass.com/"  target="_blank" rel="noreferrer">Lastpass</a>, <a href="https://keepersecurity.com/"  target="_blank" rel="noreferrer">Keeper</a>, <a href="https://clipperz.is/"  target="_blank" rel="noreferrer">Clipperz</a>, or another one you trust, then you can <em>really</em> go completely random. 1Password has a built-in random password generator. The default settings produce a password with 1200 trillion trillion trillion possibilities, but you can dial it to 64 and produce something with <em>118 quadrillion quadrillion quadrillion quadrillion quadrillion quadrillion quadrillion quintillion possibilities!</em></p>
<p>At that level, it&rsquo;s highly unlikely anyone that someone will manage to crack your password (before the heat death of the universe anyway).</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/keeping-your-github-code-secure/pass3-1.png"
    width="836"
      height="195"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/keeping-your-github-code-secure/pass4-1.png"
    width="832"
      height="192"></figure>
<p>Tyler has a post on passwords that I recommend reading, especially the section on <a href="https://web.archive.org/web/20220123131343/http://www.fidian.com/programming/passwordsecurity#TOC-Precautions-You-Need-To-Take"  target="_blank" rel="noreferrer">password security precautions</a>. It&rsquo;s full of great suggestions for choosing a password wisely.</p>
<hr>

<h2 class="relative group">Don&rsquo;t reuse passwords
    <div id="dont-reuse-passwords" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#dont-reuse-passwords" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>You&rsquo;ll hear this advice a lot, but why does it matter? Well, no matter how good your password is, some site is going to store it incorrectly.. and get hacked&hellip; and your password is going to end up out in the open.</p>
<p>Most people use the same email address as their username everywhere. So once a hacker has your password it&rsquo;s easy to try it out on different sites. And if they&rsquo;ve figured out what your GitHub password is, they can try it on other sites developers might frequent like Microsoft and Apple, cloud services like Amazon and DigitalOcean, code repos like GitLab and Bitbucket, and on and on.</p>
<p>The only reasonable way to have a hundred different complex passwords is to use a password manager like <a href="https://1password.com/"  target="_blank" rel="noreferrer">1Password</a>, as I mentioned before. Let them generate a long and unique password, and store it for you.</p>
<hr>

<h2 class="relative group">Setup 2FA for your account
    <div id="setup-2fa-for-your-account" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#setup-2fa-for-your-account" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Imagine you have a safety box, and in order to gain access you have to whisper a secret phrase to the teller. Now imagine he verifies your phrase but leaves his computer unlocked, and everyone who walks by for the rest of the day can see it. The teller also has a horrible memory <em>(did I forget to mention that?)</em> and a hundred different people repeat your secret phrase, and he just shows everyone the contents of your box. 😒</p>
<p>But what if you needed to insert a physical key into the box too? Anyone with the secret phrase would still be locked out, unable to see the contents. That&rsquo;s two-factor authentication (aka authorization, verification, etc). In addition to something you <em><strong>know</strong></em> (your password), you also need something you <em><strong>own</strong></em> (your phone, to generate a secret code) or that you <em><strong>are</strong></em> (your fingerprint). The more factors (multi-factor auth) that you can add to an account, the less likely anyone has everything they need to access it.</p>
<p>Setup 2FA for <a href="https://github.com/settings/security"  target="_blank" rel="noreferrer">GitHub</a> <em>(</em><a href="https://help.github.com/en/articles/about-two-factor-authentication"  target="_blank" rel="noreferrer"><em>help</em></a><em>),</em> and then set it up everywhere else that offers it too - <a href="https://www.amazon.com/a/settings/approval"  target="_blank" rel="noreferrer">Amazon</a>, <a href="https://myaccount.google.com/signinoptions/two-step-verification"  target="_blank" rel="noreferrer">Google</a><em>,</em> <a href="https://www.linkedin.com/psettings/two-step-verification"  target="_blank" rel="noreferrer">LinkedIn</a>, etc. Here&rsquo;s a great site that documents how (or if) you can enable 2FA for various sites: <a href="https://twofactorauth.org/"  target="_blank" rel="noreferrer">twofactorauth.org</a></p>
<p>Briefly, how it works is a site generates a unique QR code, which you&rsquo;ll scan with an app like <a href="https://github.com/andOTP/andOTP"  target="_blank" rel="noreferrer">andOTP</a> (which can do encrypted backups). If you scan the code with multiple devices, they&rsquo;ll generate the same (one-time use) code every 30 seconds or so, which is a great way to avoid disaster if one device fails. Print and store any recovery codes they give you too.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/keeping-your-github-code-secure/qr-code-namecheap.png"
    width="1200"
      height="1004"></figure>
<p>Using an <a href="https://zxing.org/w/decode.jspx"  target="_blank" rel="noreferrer">online decoder</a> on the above QR code, the contents include the site and a unique &ldquo;secret&rdquo;. The only way this works is if the service stores the secret too and (by combining it with the current time) generates the same code your app does, in order to verify it. This keeps you safe in the event a third party leaks your credentials (like happened with GitHub et al), or a different site gets hacked and you reused the same password, or someone discovers your password scrawled under your keyboard. It&rsquo;s unlikely to help if the site itself gets hacked and the secret is discovered along with your password.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/keeping-your-github-code-secure/qr-code-decrypted.png"
    width="1004"
      height="489"></figure>
<p><a href="https://authy.com/what-is-2fa/"  target="_blank" rel="noreferrer">Learn more about what 2FA is</a>, the <a href="https://web.archive.org/web/20181121184020/https://auth0.com/learn/two-factor-authentication/"  target="_blank" rel="noreferrer">pros and cons of various 2FA methods</a>, or just see <a href="https://www.namecheap.com/support/knowledgebase/article.aspx/10073/45/how-can-i-use-the-totp-method-for-twofactor-authentication"  target="_blank" rel="noreferrer">what a typical setup process for enabling 2FA looks like</a>. 1Password can <a href="https://support.1password.com/one-time-passwords/"  target="_blank" rel="noreferrer">store and track your 2FA codes for you</a> too, which seemed a little odd when I first heard about it&hellip; but then, if someone hacks them and gets anything useful we&rsquo;re screwed anyway.</p>
<hr>

<h2 class="relative group">Require 2FA for the whole organization
    <div id="require-2fa-for-the-whole-organization" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#require-2fa-for-the-whole-organization" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Even if you enable 2FA on your device, what if you&rsquo;re part of a GitHub organization and dozens of other developers <em>don&rsquo;t</em> have 2FA enabled?</p>
<p>If they have access to push changes to the organization&rsquo;s repos, any one of them could fall prey to the issue GitHub just notified everyone about, and their account could be used to wipe the organization&rsquo;s code. Granted, they probably have a local copy, and all the other members have cloned copies too, and GitHub (I would hope) has backups, but the other threats include releasing code to the public that was meant to remain private, or slipping malicious commits in and having them go unnoticed.</p>
<p>GitHub allows <a href="https://help.github.com/en/articles/requiring-two-factor-authentication-in-your-organization"  target="_blank" rel="noreferrer">organization owners to force members to enable 2FA</a>. Um, warn them first though, because as soon as you enable this option it&rsquo;ll boot everyone who doesn&rsquo;t have 2FA from the organization, lol. Don&rsquo;t worry, <a href="https://help.github.com/en/articles/reinstating-a-former-member-of-your-organization"  target="_blank" rel="noreferrer">you can reinstate them</a>.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/keeping-your-github-code-secure/github-require-2fa-org.png"
    width="949"
      height="271"></figure>
<p>If you didn&rsquo;t want to enforce 2FA on everyone_,_ I can think of another way that <em>might</em> prevent a problem like this as long as the admins have 2FA enabled - <a href="https://help.github.com/en/articles/enabling-branch-restrictions"  target="_blank" rel="noreferrer">branch restrictions</a>. Setup a branch protection rule for the pattern &ldquo;master&rdquo;. That alone disables force-pushes, which is what the hacker used to ransom these accounts. You can also make it so PRs can&rsquo;t be merged without other eyes on the changes, and require signed commits (more on that next).</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/keeping-your-github-code-secure/master-branch-protected.png"
    width="1120"
      height="848"></figure>
<hr>

<h2 class="relative group">Sign your git commits
    <div id="sign-your-git-commits" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#sign-your-git-commits" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>You&rsquo;ve got a good unique password, it&rsquo;s stored securely, and you&rsquo;ve enabled another method of auth so even if your credentials are compromised you&rsquo;re likely to still be okay. What&rsquo;s next?</p>
<p>You can digitally &ldquo;sign&rdquo; your commits so everyone knows they came from you, and not someone <a href="https://softwareengineering.stackexchange.com/a/212216"  target="_blank" rel="noreferrer">pretending to be you</a>&hellip; which is a <a href="https://help.github.com/en/articles/why-are-my-commits-linked-to-the-wrong-user"  target="_blank" rel="noreferrer">possibility</a> when multiple people have access to a repo. GitHub provides comprehensive docs on <a href="https://help.github.com/en/articles/managing-commit-signature-verification"  target="_blank" rel="noreferrer">managing commit signature verification</a>.</p>

<h3 class="relative group">Create a signing key
    <div id="create-a-signing-key" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#create-a-signing-key" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>If you&rsquo;ve never setup a key before (I hadn&rsquo;t before writing this), start by <a href="https://help.github.com/en/articles/generating-a-new-gpg-key"  target="_blank" rel="noreferrer">generating a new GPG key</a>. Fill in your name and email, etc. If you want, go into <a href="https://github.com/settings/emails"  target="_blank" rel="noreferrer">email settings</a> and select the <em>&ldquo;Keep my email address private&rdquo;</em> checkbox first, and use whatever GitHub assigns to you.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">gpg --full-generate-key</span></span></code></pre></div></div>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/keeping-your-github-code-secure/gpg-setup-1.png"
    width="606"
      height="693"></figure>
<p>In a minute, you&rsquo;ll need the key id listed next to <em>&ldquo;gpg: key&rdquo;</em>. If you clear the screen or whatever, you can display it again with this; it&rsquo;s on the first line next to <em>&ldquo;rsa4096&rdquo;.</em></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">gpg --list-secret-keys --keyid-format LONG</span></span></code></pre></div></div>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/keeping-your-github-code-secure/gpg-setup-2.png"
    width="643"
      height="229"></figure>

<h3 class="relative group">Add the key id to your local git config
    <div id="add-the-key-id-to-your-local-git-config" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#add-the-key-id-to-your-local-git-config" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Next, <a href="https://help.github.com/en/articles/telling-git-about-your-signing-key"  target="_blank" rel="noreferrer">tell Git about your signing key</a>. Copy the key id and add it to your git config file:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">git config --global user.signingkey 12341234ABCDEF12</span></span></code></pre></div></div>
<p>Open the <code>~/.gitconfig</code> file you just edited to make sure it&rsquo;s in there. You should see something like this:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">[user]
name = Grant Winney
email = your_email_address@domain.com
signingkey = 12341234ABCDEF12</code></pre></div>
<p>Add one more section underneath that:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">[commit]
gpgsign = true</code></pre></div>

<h3 class="relative group">Add the GPG key to GitHub
    <div id="add-the-gpg-key-to-github" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#add-the-gpg-key-to-github" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Finally, you&rsquo;ll need to <a href="https://docs.github.com/en/authentication/managing-commit-signature-verification/adding-a-gpg-key-to-your-github-account"  target="_blank" rel="noreferrer">add the GPG key to your account</a>.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">gpg --armor --export 12341234ABCDEF12</span></span></code></pre></div></div>
<p>Open the <a href="https://github.com/settings/gpg/new"  target="_blank" rel="noreferrer">SSH and GPG keys</a> settings and enter the ginormous value it generates for you.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/keeping-your-github-code-secure/gpg-setup-3.png"
    width="1125"
      height="466"></figure>
<p>Now when you push to a repo, you&rsquo;ll have to enter the passphrase you chose (you can <a href="https://stackoverflow.com/a/38422272/301857"  target="_blank" rel="noreferrer">save the passphrase</a> so you don&rsquo;t have to enter it with each commit, but I&rsquo;m not&hellip; for now), and you&rsquo;ll see a special badge next to your commits on GitHub.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/keeping-your-github-code-secure/gpg-setup-4.png"
    width="1106"
      height="326"></figure>
<hr>

<h2 class="relative group">Require signing all commits for a repo
    <div id="require-signing-all-commits-for-a-repo" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#require-signing-all-commits-for-a-repo" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Okay, you&rsquo;re signing your commits&hellip; but <a href="https://mikegerwitz.com/2012/05/a-git-horror-story-repository-integrity-with-signed-commits"  target="_blank" rel="noreferrer">what about everyone else with access</a> to your repository? It&rsquo;d be great if every commit was proven to be from exactly who they seem to be from. You can <a href="https://help.github.com/en/articles/about-required-commit-signing"  target="_blank" rel="noreferrer">require verification for a branch</a>, even for admins.</p>
<p>If you wanted to require all commits to master be signed, for instance, you could do that on the &ldquo;Branches&rdquo; settings page. This would&rsquo;ve (I believe) prevented the hackers from being able to force-push their ransom note and overwrite repo history.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/keeping-your-github-code-secure/master-branch-protected-1.png"
    width="1121"
      height="600"></figure>
<hr>

<h2 class="relative group">Be wary who you grant access to
    <div id="be-wary-who-you-grant-access-to" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#be-wary-who-you-grant-access-to" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>This whole thing happened, according to GitHub, because some unnamed third party leaked credentials. So now&rsquo;s a great time to <a href="https://github.com/settings/applications"  target="_blank" rel="noreferrer">review those third-party applications</a> to which you&rsquo;ve granted some level of access in the past, and decide whether you need it anymore.</p>
<p>Click on an application to see what it has access to.</p>
<ul>
<li>If the only permission is <em>&ldquo;Access user email addresses (read-only)&rdquo;,</em> then most likely it&rsquo;s being used as a login to a site.</li>
<li>But if the permissions include <em>&ldquo;Full control of private repositories&rdquo;,</em> you&rsquo;ll want to think hard about how much you trust that service - they have access to everything!</li>
</ul>
<p>If you&rsquo;re an admin of an organization, consider <a href="https://help.github.com/en/articles/about-oauth-app-access-restrictions"  target="_blank" rel="noreferrer">restricting app access</a> to the org. And while you&rsquo;re there, <a href="https://github.com/settings/security"  target="_blank" rel="noreferrer">revoke any login sessions</a> you don&rsquo;t recognize too (scroll to the bottom of the page).</p>
<hr>

<h2 class="relative group">Don&rsquo;t expose your .git directory
    <div id="dont-expose-your-git-directory" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#dont-expose-your-git-directory" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I don&rsquo;t have experience with an environment where this would be a problem, but I could imagine one pretty easily.</p>
<p>An educational / government / whatever institution stores their entire website in GitHub, then configures some server to clone it and make the whole thing publicly available. Yay versioning! Yay automation and convenience! Except there are sensitive folders like <code>.git</code> that might contain logs and other stuff you wouldn&rsquo;t actually want available to the public, so without a little care it could definitely be a security issue.</p>
<p>From the released report, this seems to be one of the things hackers are looking for:</p>
<blockquote><p>Continuous scanning for publicly exposed <code>.git/config</code> and other environment files has been and continues to be conducted &hellip; as recently as May 10. These files can contain sensitive credentials and personal access tokens &hellip; and they should not be publicly accessible in repositories or on web servers. This <a href="https://en.internetwache.org/dont-publicly-expose-git-or-how-we-downloaded-your-websites-sourcecode-an-analysis-of-alexas-1m-28-07-2015/"  target="_blank" rel="noreferrer">problem</a> is <a href="https://laravel-news.com/psa-hide-your-gitconfig-directory"  target="_blank" rel="noreferrer">not</a> a new <a href="https://web.archive.org/web/20190616081313/https://sweetness.hmmz.org/2013-06-10-devs-please-stop-serving-git-to-the-outside.html"  target="_blank" rel="noreferrer">one</a>. More information on the <code>.git</code> directory and the <code>.git/config</code> file is available <a href="https://git-scm.com/docs/gitrepository-layout"  target="_blank" rel="noreferrer">here</a> and <a href="https://git-scm.com/docs/git-config#_configuration_file"  target="_blank" rel="noreferrer">here</a>.</p>
</blockquote><hr>

<h2 class="relative group">Anything else?
    <div id="anything-else" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#anything-else" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>That&rsquo;s all I can think of for now. Here&rsquo;s a couple other resources to check out too:</p>
<ul>
<li><a href="https://help.github.com/en/categories/authenticating-to-github"  target="_blank" rel="noreferrer">Authenticating to GitHub</a></li>
<li><a href="https://snyk.io/blog/ten-git-hub-security-best-practices/"  target="_blank" rel="noreferrer">10 GitHub Security Best Practices | Snyk</a></li>
</ul>
<p>Was this helpful? Do you have any other tips for securing your GitHub code?</p>
]]></content:encoded><media:content url="https://grantwinney.com/keeping-your-github-code-secure/feature.webp" medium="image" type="image/webp"/></item><item><title>Modify a config file in Erlang</title><link>https://grantwinney.com/how-to-modify-a-config-file-in-erlang/</link><pubDate>Thu, 16 May 2019 11:22:49 +0000</pubDate><guid>https://grantwinney.com/how-to-modify-a-config-file-in-erlang/</guid><description>Modifying an Erlang config file at runtime wasn&amp;rsquo;t as easy (or obvious) as I&amp;rsquo;d thought it&amp;rsquo;d be. So I wrote a script to hopefully make it easier.</description><content:encoded><![CDATA[<p>I found myself recently needing to write an <a href="http://erlang.org/doc/man/escript.html"  target="_blank" rel="noreferrer">escript</a> to modify a <a href="https://www.erlang.org/docs/19/man/config"  target="_blank" rel="noreferrer">config file</a>. All I needed was to read it in, make a couple updates, and write it back out. Should be easy, right? Please make it easy Erlang. No? Okay&hellip; 😢</p>
<blockquote><p>The code in this post is available on <a href="https://github.com/grantwinney/BlogCodeSamples/tree/master/Languages/Erlang/ConfigFileModifier"  target="_blank" rel="noreferrer">GitHub</a>, for you to use, expand upon, or just follow along while you read&hellip; and hopefully discover something new!</p>
</blockquote><p>Here&rsquo;s a sample of what the config file looks like, without resembling actual production code of course. The point is, it&rsquo;s nothing special - just a list of configuration parameters for a system, laid out in a nested <code>proplist</code> format.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">[{</span><span class="n">application_1</span><span class="p">,[{</span><span class="n">log_options</span><span class="p">,[{</span><span class="n">log_path</span><span class="p">,</span><span class="s">&#34;C:/Program Files/Acme/Logs&#34;</span><span class="p">}]}]},</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="n">application_2</span><span class="p">,[{</span><span class="n">log_options</span><span class="p">,[{</span><span class="n">log_path</span><span class="p">,</span><span class="s">&#34;C:/Program Files/Acme/Logs&#34;</span><span class="p">}]},</span>
</span></span><span class="line"><span class="cl">                 <span class="p">{</span><span class="n">app_options</span><span class="p">,[{</span><span class="n">max_attempts</span><span class="p">,</span><span class="mi">4</span><span class="p">},{</span><span class="n">attempt_delay_ms</span><span class="p">,</span><span class="mi">5000</span><span class="p">}]},</span>
</span></span><span class="line"><span class="cl">                 <span class="p">{</span><span class="n">dependencies</span><span class="p">,[[{</span><span class="n">name</span><span class="p">,</span><span class="n">writer</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">                                 <span class="p">{</span><span class="n">exe</span><span class="p">,</span><span class="s">&#34;C:/Program Files/Acme/Writer.exe&#34;</span><span class="p">}],</span>
</span></span><span class="line"><span class="cl">                                <span class="p">[{</span><span class="n">name</span><span class="p">,</span><span class="n">logger</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">                                 <span class="p">{</span><span class="n">exe</span><span class="p">,</span><span class="s">&#34;C:/Program Files/Acme/Logger.exe&#34;</span><span class="p">}],</span>
</span></span><span class="line"><span class="cl">                                <span class="p">[{</span><span class="n">name</span><span class="p">,</span><span class="n">server</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">                                 <span class="p">{</span><span class="n">exe</span><span class="p">,</span><span class="s">&#34;C:/Program Files/Acme/Server.exe&#34;</span><span class="p">}]]}]},</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="n">application_3</span><span class="p">,[{</span><span class="n">app_options</span><span class="p">,[{</span><span class="n">allowed_groups</span><span class="p">,[</span><span class="n">admin</span><span class="p">,</span><span class="n">manager</span><span class="p">]}]}]}].</span></span></span></code></pre></div></div>

<h2 class="relative group">Reading in terms from a file
    <div id="reading-in-terms-from-a-file" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#reading-in-terms-from-a-file" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>My first thought was to just open the file and use the <a href="http://erlang.org/doc/man/proplists.html"  target="_blank" rel="noreferrer">proplists</a> module to parse it, but whenever I opened it I got a binary string with the contents of the file. Was I reading it wrong? I started looking at the <a href="http://erlang.org/doc/man/file.html"  target="_blank" rel="noreferrer">file</a> module for different ways to read a file, aaaand&hellip;.. I had skipped right over the function I needed - <a href="http://erlang.org/doc/man/file.html#consult-1"  target="_blank" rel="noreferrer">file:consult/1</a>. If your file has nothing but legit Erlang code in it, then <code>file:consult()</code> can read it into memory.</p>
<p>In my defense, the name, description, and example are all awful&hellip; <em>&ldquo;Reads Erlang terms, separated by &lsquo;.&rsquo;&rdquo;</em> That&rsquo;s all we get, but then the Erlang documentation leaves <em>much</em> to be desired. And the name!! What does consulting a file have to do with reading in Erlang terms? And of course, there&rsquo;s no opposite unconsult or deconsult. Why can&rsquo;t we have a module that makes parsing and modifying these config files easier? 😖</p>

<h2 class="relative group">Modifying a config file
    <div id="modifying-a-config-file" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#modifying-a-config-file" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>And so, I present my own, more appropriately-named module called <code>config_parser</code>. You can grab it below or <a href="https://github.com/grantwinney/BlogCodeSamples/tree/master/Languages/Erlang/ConfigFileModifier"  target="_blank" rel="noreferrer">find it on GitHub</a>, and modify it to your heart&rsquo;s content. It reads and writes <em>(<a href="https://zxq9.com/archives/1021"  target="_blank" rel="noreferrer">thank you</a>)</em> config files, and can also get and set nested terms so you can more easily modify them.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="c">% Author: Grant Winney
</span></span></span><span class="line"><span class="cl"><span class="c">% License: MIT
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">module</span><span class="p">(</span><span class="n">config_parser</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">export</span><span class="p">([</span><span class="n">read_terms</span><span class="o">/</span><span class="mi">1</span><span class="p">,</span> <span class="n">get_nested_terms</span><span class="o">/</span><span class="mi">2</span><span class="p">,</span> <span class="n">set_nested_terms</span><span class="o">/</span><span class="mi">3</span><span class="p">,</span> <span class="n">write_terms</span><span class="o">/</span><span class="mi">2</span><span class="p">]).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">read_terms</span><span class="p">(</span><span class="nv">FileName</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nn">file</span><span class="p">:</span><span class="nf">consult</span><span class="p">(</span><span class="nv">FileName</span><span class="p">)</span> <span class="k">of</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="n">ok</span><span class="p">,</span> <span class="p">[</span><span class="nv">Terms</span><span class="p">]}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span><span class="n">ok</span><span class="p">,</span> <span class="nv">Terms</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="n">error</span><span class="p">,</span> <span class="p">{_</span><span class="nv">Line</span><span class="p">,</span> <span class="p">_</span><span class="nv">Mod</span><span class="p">,</span> <span class="p">_</span><span class="nv">Term</span><span class="p">}</span> <span class="o">=</span> <span class="nv">Reason</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span><span class="n">error</span><span class="p">,</span> <span class="nn">file</span><span class="p">:</span><span class="nf">format_error</span><span class="p">(</span><span class="nv">Reason</span><span class="p">)};</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="n">error</span><span class="p">,</span> <span class="nv">Reason</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span><span class="n">error</span><span class="p">,</span> <span class="n">error_message</span><span class="p">(</span><span class="nv">Reason</span><span class="p">,</span> <span class="nv">FileName</span><span class="p">)}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">write_terms</span><span class="p">(</span><span class="nv">FileName</span><span class="p">,</span> <span class="nv">Terms</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Format</span> <span class="o">=</span> <span class="k">fun</span><span class="p">(</span><span class="nv">Term</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nn">io_lib</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34;~tp.</span><span class="si">~n</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[</span><span class="nv">Term</span><span class="p">])</span> <span class="k">end</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nn">file</span><span class="p">:</span><span class="nf">write_file</span><span class="p">(</span><span class="nv">FileName</span><span class="p">,</span> <span class="nn">lists</span><span class="p">:</span><span class="nf">map</span><span class="p">(</span><span class="nv">Format</span><span class="p">,</span> <span class="p">[</span><span class="nv">Terms</span><span class="p">])).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">get_nested_terms</span><span class="p">(</span><span class="nv">Keys</span><span class="p">,</span> <span class="nv">Terms</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nn">lists</span><span class="p">:</span><span class="nf">foldl</span><span class="p">(</span><span class="k">fun</span><span class="p">(</span><span class="nv">Key</span><span class="p">,</span> <span class="nv">InnerTerms</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nn">proplists</span><span class="p">:</span><span class="nf">get_value</span><span class="p">(</span><span class="nv">Key</span><span class="p">,</span> <span class="nv">InnerTerms</span><span class="p">)</span> <span class="k">end</span><span class="p">,</span> <span class="nv">Terms</span><span class="p">,</span> <span class="nv">Keys</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">set_nested_terms</span><span class="p">([</span><span class="nv">Key</span><span class="p">],</span> <span class="nv">ReplacementTerms</span><span class="p">,</span> <span class="nv">Terms</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nn">lists</span><span class="p">:</span><span class="nf">keyreplace</span><span class="p">(</span><span class="nv">Key</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="nv">Terms</span><span class="p">,</span> <span class="p">{</span><span class="nv">Key</span><span class="p">,</span> <span class="nv">ReplacementTerms</span><span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="nf">set_nested_terms</span><span class="p">([</span><span class="nv">Key</span><span class="p">|</span><span class="nv">NestedKeys</span><span class="p">],</span> <span class="nv">ReplacementTerms</span><span class="p">,</span> <span class="nv">Terms</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">InnerValue</span> <span class="o">=</span> <span class="n">set_nested_terms</span><span class="p">(</span><span class="nv">NestedKeys</span><span class="p">,</span> <span class="nv">ReplacementTerms</span><span class="p">,</span> <span class="nn">proplists</span><span class="p">:</span><span class="nf">get_value</span><span class="p">(</span><span class="nv">Key</span><span class="p">,</span> <span class="nv">Terms</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">    <span class="nn">lists</span><span class="p">:</span><span class="nf">keyreplace</span><span class="p">(</span><span class="nv">Key</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="nv">Terms</span><span class="p">,</span> <span class="p">{</span><span class="nv">Key</span><span class="p">,</span> <span class="nv">InnerValue</span><span class="p">}).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">error_message</span><span class="p">(</span><span class="n">enoent</span><span class="p">,</span> <span class="nv">FileName</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nn">io_lib</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34;The file does not exist: </span><span class="si">~p</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[</span><span class="nv">FileName</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"><span class="nf">error_message</span><span class="p">(</span><span class="n">eaccess</span><span class="p">,</span> <span class="nv">FileName</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nn">io_lib</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34;Missing permission for reading the file, or for searching one of the parent directories: </span><span class="si">~p</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[</span><span class="nv">FileName</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"><span class="nf">error_message</span><span class="p">(</span><span class="n">eisdir</span><span class="p">,</span> <span class="nv">FileName</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nn">io_lib</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34;The named file is a directory: </span><span class="si">~p</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[</span><span class="nv">FileName</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"><span class="nf">error_message</span><span class="p">(</span><span class="n">enotdir</span><span class="p">,</span> <span class="nv">FileName</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nn">io_lib</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34;A component of the filename is not a directory: </span><span class="si">~p</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[</span><span class="nv">FileName</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"><span class="nf">error_message</span><span class="p">(</span><span class="n">enomem</span><span class="p">,</span> <span class="p">_</span><span class="nv">FileName</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nn">io_lib</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34;There is not enough memory for the contents of the file.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">error_message</span><span class="p">(</span><span class="nv">Error</span><span class="p">,</span> <span class="nv">FileName</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nn">io_lib</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34;</span><span class="si">~p</span><span class="s"> error: </span><span class="si">~p</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[</span><span class="nv">Error</span><span class="p">,</span> <span class="nv">FileName</span><span class="p">]).</span></span></span></code></pre></div></div>

<h2 class="relative group">Usage
    <div id="usage" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#usage" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There&rsquo;s a couple other files in the <a href="https://github.com/grantwinney/BlogCodeSamples/tree/master/Languages/Erlang/ConfigFileModifier"  target="_blank" rel="noreferrer">repo</a> so you can try it out. Just leave them in the same directory, compile the Erlang module, and run the two functions to see how it updates the config file. You should see a new dependency added to application_2, and a new group added to application_3.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">[{</span><span class="n">application_1</span><span class="p">,[{</span><span class="n">log_options</span><span class="p">,[{</span><span class="n">log_path</span><span class="p">,</span><span class="s">&#34;C:/Program Files/Acme/Logs&#34;</span><span class="p">}]}]},</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="n">application_2</span><span class="p">,[{</span><span class="n">log_options</span><span class="p">,[{</span><span class="n">log_path</span><span class="p">,</span><span class="s">&#34;C:/Program Files/Acme/Logs&#34;</span><span class="p">}]},</span>
</span></span><span class="line"><span class="cl">                 <span class="p">{</span><span class="n">app_options</span><span class="p">,[{</span><span class="n">max_attempts</span><span class="p">,</span><span class="mi">4</span><span class="p">},{</span><span class="n">attempt_delay_ms</span><span class="p">,</span><span class="mi">5000</span><span class="p">}]},</span>
</span></span><span class="line"><span class="cl">                 <span class="p">{</span><span class="n">dependencies</span><span class="p">,[[{</span><span class="n">name</span><span class="p">,</span><span class="n">writer</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">                                 <span class="p">{</span><span class="n">exe</span><span class="p">,</span><span class="s">&#34;C:/Program Files/Acme/Writer.exe&#34;</span><span class="p">}],</span>
</span></span><span class="line"><span class="cl">                                <span class="p">[{</span><span class="n">name</span><span class="p">,</span><span class="n">logger</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">                                 <span class="p">{</span><span class="n">exe</span><span class="p">,</span><span class="s">&#34;C:/Program Files/Acme/Logger.exe&#34;</span><span class="p">}],</span>
</span></span><span class="line"><span class="cl">                                <span class="p">[{</span><span class="n">name</span><span class="p">,</span><span class="n">server</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">                                 <span class="p">{</span><span class="n">exe</span><span class="p">,</span><span class="s">&#34;C:/Program Files/Acme/Server.exe&#34;</span><span class="p">}],</span>
</span></span><span class="line"><span class="cl">                                <span class="p">[{</span><span class="n">name</span><span class="p">,</span><span class="n">consumer</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">                                 <span class="p">{</span><span class="n">exe</span><span class="p">,</span><span class="s">&#34;C:/Program Files/Acme/Consumer.exe&#34;</span><span class="p">}]]}]},</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="n">application_3</span><span class="p">,[{</span><span class="n">app_options</span><span class="p">,[{</span><span class="n">allowed_groups</span><span class="p">,[</span><span class="n">admin</span><span class="p">,</span><span class="n">manager</span><span class="p">,</span><span class="n">owner</span><span class="p">]}]}]}].</span></span></span></code></pre></div></div>

<h2 class="relative group">Issues
    <div id="issues" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#issues" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you have a fix or problem, feel free to <a href="https://github.com/grantwinney/BlogCodeSamples/pulls"  target="_blank" rel="noreferrer">submit a PR</a> or <a href="https://github.com/grantwinney/BlogCodeSamples/issues/new?title=Issue%20regarding%20Erlang%20config%20file%20script"  target="_blank" rel="noreferrer">open an issue</a>. Also, I haven&rsquo;t added any specs or EUnit tests around this, but if you do and you&rsquo;d like to share them, I&rsquo;d like to include them!</p>
]]></content:encoded><media:content url="https://grantwinney.com/how-to-modify-a-config-file-in-erlang/feature.webp" medium="image" type="image/webp"/></item><item><title>Running Windows XP in VirtualBox</title><link>https://grantwinney.com/running-windows-xp-in-virtualbox/</link><pubDate>Sat, 04 May 2019 14:35:40 +0000</pubDate><guid>https://grantwinney.com/running-windows-xp-in-virtualbox/</guid><description>Just got an MSDN account, which always comes with some old treasures (hey, beauty&amp;rsquo;s in the eye of the beholder). Take a trip back with me, to the days of Windows XP, the beginning of the .NET Framework, and even further&amp;hellip; ;)</description><content:encoded><![CDATA[<p>I just got access to an MSDN account with keys for various versions of Windows and Visual Studio, so&hellip;. it&rsquo;s retro time! Isn&rsquo;t it funny how something brand new comes out and we get excited, then we get annoyed with it&rsquo;s deficiencies, then it&rsquo;s forgotten when something better comes out, and finally after enough time we get all nostalgic and pull it out of mothballs?</p>
<p>With that in mind, let&rsquo;s check out the best of Windows yesteryear. But first&hellip;</p>
<p><strong>Note:</strong> I won&rsquo;t share any keys or recommend where to find them, but you were allowed to use XP for 60 days without activating, so you could probably use any key you find and you&rsquo;ll be good for a couple months. Sure, that&rsquo;s annoying, but you&rsquo;re not seriously using this for anything serious are you?</p>
<p><a href="http://web.archive.org/web/20080430165302/http://www.microsoft.com/windowsxp/sp2/sysreqs.mspx"  target="_blank" rel="noreferrer">You can&rsquo;t install service packs on 64-bit systems</a>, so you may want to choose the 32-bit option. If you&rsquo;re doing this from MSDN, select the <em>&ldquo;with Service Pack 3&rdquo;</em> option to make life easier.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/winxp86.png"
    width="1243"
      height="145"></figure>
<p>Give it plenty of hard disk space if you plan on installing other apps. It&rsquo;s a pita to <a href="https://thegenomefactory.blogspot.com/2013/06/extending-windows-xp-partition-in.html"  target="_blank" rel="noreferrer">resize the partition</a> afterwards, so give it 30 or 40 GB at least. XP had really minimal requirements, so a couple gigs of memory should be more than enough.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/vmsetup1.png"
    width="609"
      height="550"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/vmsetup2.png"
    width="600"
      height="544"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/vmsetup3.png"
    width="525"
      height="488"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/vmsetup4.png"
    width="722"
      height="486"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/vmsetup5.png"
    width="722"
      height="486"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/vmsetup6.png"
    width="642"
      height="570"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/vmsetup7.png"
    width="799"
      height="598"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/vmsetup8.png"
    width="798"
      height="598"></figure>

<h2 class="relative group">Map a Host Machine Folder
    <div id="map-a-host-machine-folder" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#map-a-host-machine-folder" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>After installing XP, the first thing you&rsquo;ll notice is that Internet Explorer is awful and horrible and you probably can&rsquo;t even connect to the Internet (at least until you have the service packs installed). Let&rsquo;s do the very first thing anyone does when they install any version of Windows&hellip; install a real browser.</p>
<p>Follow the suggestions in <a href="https://askubuntu.com/q/52773/927673"  target="_blank" rel="noreferrer">this thread</a> to share a folder with your host machine, or run <em>&ldquo;Devices / Insert Guest Additions CD image&hellip;&rdquo;</em> in VirtualBox <em>(</em><a href="https://www.virtualbox.org/manual/ch04.html"  target="_blank" rel="noreferrer"><em>read more here</em></a><em>)</em> and then go to <em>&ldquo;Devices / Shared Folders / Shared Folders Settings&rdquo;</em> and map a folder on your host machine. You might have to select <em>&ldquo;Devices / Optical Drives / Remove disk&rdquo;</em> first, but maybe not. I mapped the &ldquo;Downloads&rdquo; folder, so it&rsquo;ll show up as a drive in the VM under <em>My Computer</em>.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/sharedfolders.png"
    width="880"
      height="518"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/winexplorer.png"
    width="822"
      height="401"></figure>
<p>Now you can download <a href="https://www.google.com/chrome/"  target="_blank" rel="noreferrer">Chrome</a> (it&rsquo;ll install v49, the last supported version for XP), <a href="https://ftp.mozilla.org/pub/firefox/releases/52.9.0esr/win32/en-US/"  target="_blank" rel="noreferrer">Firefox 52.9.0esr</a> (Mozilla&rsquo;s <a href="https://support.mozilla.org/en-US/kb/end-support-windows-xp-and-vista"  target="_blank" rel="noreferrer">last supported version</a>, but you&rsquo;ll likely have to install SP2 first), or <a href="https://ftp.opera.com/pub/opera-winxpvista/36.0.2130.80/win/"  target="_blank" rel="noreferrer">Opera 36</a> (you get the picture) on the host machine, drop them in the shared folder, and install them from the VM.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/xpbrowsers.png"
    width="1152"
      height="864"></figure>
<hr>

<h2 class="relative group">Installing Service Packs
    <div id="installing-service-packs" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#installing-service-packs" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>XP is ancient but we might as well update it where we can. If you selected the <em>&ldquo;with Service Pack 3&rdquo;</em> option like I mentioned, then skip this section.</p>
<p>Otherwise, there&rsquo;s a couple places you can get the SPs from (at least). The easiest is from your MSDN account. Save &ldquo;Service Pack 1a&rdquo;, &ldquo;Service Pack 2 (English)&rdquo; and &ldquo;Service Pack 3 (x86)&rdquo; to the shared folder on your host machine.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/msdn-xp-sps.png"
    width="1690"
      height="510"></figure>
<p>The files are ISOs, so download <a href="https://www.elby.ch/en/products/vcd.html"  target="_blank" rel="noreferrer">Virtual CloneDrive</a> in the VM <em>(you might have to right-click the exe, open properties, and press &ldquo;unblock&rdquo;)</em> and mount each file to install it. All you have to do is choose Settings and create a half-dozen virtual drives, then mount each one to an ISO on the host machine.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/clonedrive1.png"
    width="800"
      height="495"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/clonedrive2.png"
    width="800"
      height="659"></figure>
<p>Alternatively, you can grab them from the <a href="http://www.catalog.update.microsoft.com"  target="_blank" rel="noreferrer">Microsoft Update Catalog</a>:</p>
<ul>
<li><a href="http://www.catalog.update.microsoft.com/ScopedViewInline.aspx?updateid=4fcf5feb-70cf-419f-99c3-d75269e76ce7"  target="_blank" rel="noreferrer">Windows XP Service Pack 1, Express</a> (<a href="http://www.download.windowsupdate.com/msdownload/update/v3-19990518/cabpool/sp1aexpress_5d7ed5146e86a5e10e309048d02744efe5aba1d8.exe"  target="_blank" rel="noreferrer">download</a>) - 5/15/2004</li>
<li><a href="http://www.catalog.update.microsoft.com/ScopedViewInline.aspx?updateid=7477af62-8f9b-4f32-9daf-7ab452e52396"  target="_blank" rel="noreferrer">Windows XP Service Pack 2</a> (<a href="http://www.download.windowsupdate.com/msdownload/update/v3-19990518/cabpool/xpsp2_33a8fef60d48ae1f2c4feea27111af5ceca3c4f6.exe"  target="_blank" rel="noreferrer">download</a>) - 9/13/2005</li>
<li><a href="http://www.catalog.update.microsoft.com/ScopedViewInline.aspx?updateid=e23c92ac-448c-45c2-8bd2-aa8021456e00"  target="_blank" rel="noreferrer">Windows XP Pro Service Pack 2, x64 Edition</a> (<a href="http://www.download.windowsupdate.com/msdownload/update/v3-19990518/cabpool/windowsserver2003.windowsxp-kb914961-sp2-x64-enu_7f8e909c52d23ac8b5dbfd73f1f12d3ee0fe794c.exe"  target="_blank" rel="noreferrer">download</a>) - 5/23/2008</li>
<li><a href="http://www.catalog.update.microsoft.com/ScopedViewInline.aspx?updateid=60b990a0-6efa-47be-8f5a-7df2c402583e"  target="_blank" rel="noreferrer">Windows XP Service Pack 3</a> (<a href="http://www.download.windowsupdate.com/msdownload/update/software/dflt/2008/04/windowsxp-kb936929-sp3-x86-enu_c81472f7eeea2eca421e116cd4c03e2300ebfde4.exe"  target="_blank" rel="noreferrer">download</a>) - 5/19/2009</li>
</ul>

<h2 class="relative group">Install Updates
    <div id="install-updates" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#install-updates" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Although <a href="https://web.archive.org/web/20170217202701/https://www.microsoft.com/en-us/windowsforbusiness/end-of-xp-support"  target="_blank" rel="noreferrer">support for XP ended 5 years ago</a>, you can still grab the updates that were available. Click on <em>Start / All Programs / Windows Update</em> and select all the updates. Install, reboot, select more updates, rinse, repeat. Good ol&rsquo; XP.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/winupdate1.png"
    width="1022"
      height="734"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/winupdate2.png"
    width="1022"
      height="748"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/winupdate3.png"
    width="1022"
      height="699"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/winupdate4.png"
    width="1022"
      height="699"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/winupdate5.png"
    width="1022"
      height="699"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/winupdate6.png"
    width="1036"
      height="748"></figure>

<h2 class="relative group">Setup a Development Environment
    <div id="setup-a-development-environment" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#setup-a-development-environment" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Now that XP has the latest updates and security patches of yesteryear, we can setup a development environment. Gotta have git. The <a href="https://github.com/git-for-windows/git/releases/tag/v2.10.0.windows.1"  target="_blank" rel="noreferrer">last version of Git for Windows supported on XP was 2.10.0</a>, so download the exe and install it so you can clone some repos!!</p>

<h3 class="relative group">Visual Studio 2012
    <div id="visual-studio-2012" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#visual-studio-2012" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Thought this would be a good one to start with. I was wrong. <a href="https://www.techulator.com/resources/7422-Visual-Studio-2012-System-requirements-hardware-requirements.aspx"  target="_blank" rel="noreferrer">VS2012 no likey XP</a>.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-02-16_42_24-InstallVS2012-1.png"
    width="460"
      height="644"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-02-16_42_24-InstallVS2012-2.png"
    width="460"
      height="644"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-02-16_42_24-InstallVS2012-3.png"
    width="460"
      height="644"></figure>

<h3 class="relative group">Visual Studio 2010
    <div id="visual-studio-2010" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#visual-studio-2010" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>You can <a href="https://visualstudio.microsoft.com/vs/older-downloads/"  target="_blank" rel="noreferrer">download the free Visual Studio 2010 Express</a>, or the pro version from MSDN. Surprisingly, the look and feel were pretty similar to what we&rsquo;ve got a decade later. The team hadn&rsquo;t yet decided to <a href="https://stackoverflow.com/q/10859173/301857"  target="_blank" rel="noreferrer">CAPITALIZE ALL THE MENUS</a>. NuGet integration wasn&rsquo;t built-in, but I&rsquo;m sure a quick search would turn up someone figuring out how to do it.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-02-16_52_16-Microsoft-Visual-Studio-2010-Setup-1.png"
    width="498"
      height="379"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-02-16_52_16-Microsoft-Visual-Studio-2010-Setup-2.png"
    width="756"
      height="574"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-02-16_52_16-Microsoft-Visual-Studio-2010-Setup-3.png"
    width="756"
      height="574"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-02-16_52_16-Microsoft-Visual-Studio-2010-Setup-4.png"
    width="756"
      height="574"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-02-16_52_16-Microsoft-Visual-Studio-2010-Setup-5.png"
    width="756"
      height="575"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-02-16_52_16-Microsoft-Visual-Studio-2010-Setup-6.png"
    width="1152"
      height="836"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-02-16_52_16-Microsoft-Visual-Studio-2010-Setup-7.png"
    width="1152"
      height="836"></figure>

<h3 class="relative group">Visual Studio .NET 2003
    <div id="visual-studio-net-2003" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#visual-studio-net-2003" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Not retro enough for you? Let&rsquo;s go back another few years and try out <a href="https://en.wikipedia.org/wiki/Microsoft_Visual_Studio#.NET_2003"  target="_blank" rel="noreferrer">VS .NET 2003</a>. It introduced .NET 1.1, so sadly no generics and <em>definitely</em> no LINQ yet. Boooo. If it complains about prereq&rsquo;s but you don&rsquo;t have a prereq CD, try installing the Windows Components it&rsquo;s complaining about.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-03-09_50_37-Visual-Basic-.NET-Setup-1.png"
    width="498"
      height="544"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-03-09_50_37-Visual-Basic-.NET-Setup-2.png"
    width="1087"
      height="759"></figure>
<p>After that, I ran Windows Update (yet again), tried to install <a href="https://www.microsoft.com/en-us/download/details.aspx?id=33"  target="_blank" rel="noreferrer">.NET 1.1. SP1</a> (although it said it was already installed), rebooted, and finally ran the <code>setup.exe</code> file with a special flag that you can see in the command window.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">F:\&gt;./setup/setup.exe /NO_BSLN_CHECK</code></pre></div>
<p>Not sure which part of all that fixed it <em>(or was it a combination of everything?)</em> but I got to a normal installation screen eventually. And here&rsquo;s the best WinForms had to offer:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-03-09_50_37-Visual-Basic-.NET-Setup-4.png"
    width="838"
      height="651"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-03-09_50_37-Visual-Basic-.NET-Setup-5.png"
    width="756"
      height="568"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-03-09_50_37-Visual-Basic-.NET-Setup-6.png"
    width="756"
      height="568"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-03-09_50_37-Visual-Basic-.NET-Setup-8.png"
    width="1024"
      height="736"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-03-09_50_37-Visual-Basic-.NET-Setup-9.png"
    width="1036"
      height="752"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-03-09_50_37-Visual-Basic-.NET-Setup-10.png"
    width="1152"
      height="836"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-03-09_50_37-Visual-Basic-.NET-Setup-11.png"
    width="1152"
      height="836"></figure>

<h3 class="relative group">Visual Basic 6.0
    <div id="visual-basic-60" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#visual-basic-60" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p><em>Still</em> not retro enough?? Let&rsquo;s go back a couple decades to <a href="https://en.wikipedia.org/wiki/Microsoft_Visual_Studio#6.0_.281998.29"  target="_blank" rel="noreferrer">VB 6.0</a>. Forget everything you thought you knew&hellip; this is before the .NET Framework altogether.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-01-09_15_54-Visual-Basic-6.0-Enterprise-Setup.png"
    width="966"
      height="741"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-01-09_17_12-Visual-Basic-6.0-Enterprise-Setup.png"
    width="966"
      height="741"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-02-15_20_21-Installation-Wizard-for-Visual-Basic-6.0-Enterprise-Edition.png"
    width="564"
      height="453"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-02-15_22_21-Installation-Wizard-for-Visual-Basic-6.0-Enterprise-Edition.png"
    width="564"
      height="453"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-02-15_44_13-Microsoft-Visual-Basic.png"
    width="914"
      height="696"></figure>
<p>Wow, when did we lose this?! There was a wizard to create an ultra modern app, complete with splash screens, toolbars, &ldquo;about&rdquo; screens&hellip;.. <em>built-in interweb browsers!!</em> I&rsquo;m not sure why we ever progressed beyond this.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-04-08_20_01-Microsoft-Visual-Basic--design-.png"
    width="1001"
      height="743"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-04-08_20_33-Application-Wizard---Menus.png"
    width="486"
      height="357"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-04-08_22_03-Microsoft-Visual-Basic--design-.png"
    width="1001"
      height="743"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-04-08_22_34-Application-Wizard---Standard-Forms.png"
    width="486"
      height="357"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-04-08_29_45-Project1---Microsoft-Visual-Basic--design-.png"
    width="1152"
      height="836"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-04-08_33_24-Project1---Microsoft-Visual-Basic--design-.png"
    width="1152"
      height="836"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-04-08_38_03-Project1.png"
    width="1152"
      height="836"></figure>

<h3 class="relative group">Visual Basic 4.0
    <div id="visual-basic-40" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#visual-basic-40" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Older! More olderer!! Okay, here&rsquo;s VB 4.0 from 25 years ago. Look at that GUI.. every component is a separate floating toolbar.. thing. And the humble beginning of <em>(gag)</em> Crystal Reports. 🤢 🤮</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-04-20_35_07-Visual-Basic-4.0-Master-Setup-01.png"
    width="437"
      height="313"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-04-20_35_07-Visual-Basic-4.0-Master-Setup-02.png"
    width="856"
      height="588"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-04-20_35_07-Visual-Basic-4.0-Master-Setup-03.png"
    width="856"
      height="588"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-04-20_35_07-Visual-Basic-4.0-Master-Setup-04.png"
    width="856"
      height="588"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-04-20_35_07-Visual-Basic-4.0-Master-Setup-05.png"
    width="800"
      height="480"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-04-20_35_07-Visual-Basic-4.0-Master-Setup-06.png"
    width="800"
      height="480"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-05-00_24_48-.png"
    width="1037"
      height="862"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-05-00_26_22-Crystal-Reports-for-Visual-Basic.png"
    width="797"
      height="571"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/running-windows-xp-in-virtualbox/2019-05-05-00_27_46-Crystal-Reports-for-Visual-Basic----Untitled-Report--1-.png"
    width="969"
      height="764"></figure>

<h3 class="relative group">Must. Go. OLDER!!
    <div id="must-go-older" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#must-go-older" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>25 years isn&rsquo;t retro enough?!? You&rsquo;re relentless.</p>
<p>Check out my post from a few years back on <a href="https://grantwinney.com/installing-windows-3-1-in-vmware-player/"  target="_blank" rel="noreferrer">installing Windows 3.1</a>. Get a sneak peek at Visual Basic 2.0, QBasic, and all manners of 16-bit awesomeness. ;p</p>
]]></content:encoded><media:content url="https://grantwinney.com/running-windows-xp-in-virtualbox/feature.webp" medium="image" type="image/webp"/></item><item><title>Using an application config file with a .NET Standard app and NUnit 3</title><link>https://grantwinney.com/how-to-use-an-app-config-file-with-a-net-standard-app-and-nunit-3/</link><pubDate>Mon, 15 Apr 2019 14:37:19 +0000</pubDate><guid>https://grantwinney.com/how-to-use-an-app-config-file-with-a-net-standard-app-and-nunit-3/</guid><description>Porting .NET Framework code to .NET Standard has been a learning experience, with some challenges too. This time I had a .NET Standard library that expected an application config file, but loading one from an NUnit test suite proved to be more difficult than it sounded at first.</description><content:encoded><![CDATA[<p>I&rsquo;ve been busy porting some .NET Framework 4.x code to individual <a href="https://docs.microsoft.com/en-us/dotnet/standard/net-standard"  target="_blank" rel="noreferrer">.NET Standard</a> libraries at work, in the hopes of modularizing some of our codebase and making it possible to build and run on different platforms. Since quite a few of our devs use OSX and there&rsquo;s a <a href="https://visualstudio.microsoft.com/vs/mac/"  target="_blank" rel="noreferrer">Visual Studio for Mac</a>, this could be a nice win&hellip; but it&rsquo;s led to some frustrating issues too. The whole idea of a .NET Standard app is that it&rsquo;s sort of a &ldquo;lowest common denominator&rdquo; of the .NET family, containing a minimum of API calls in order to work on more platforms than just Windows.</p>
<p>What&rsquo;s annoying though is that some API calls throw a <code>PlatformNotSupportedException</code>. For example, locking/unlocking a FileStream is not supported on OSX. To run code on only one platform or another, there&rsquo;s the <a href="https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.runtimeinformation.isosplatform?view=netstandard-2.0"  target="_blank" rel="noreferrer">IsOSPlatform</a> method, but that seems odd to me.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">RuntimeInformation</span><span class="p">.</span><span class="n">IsOSPlatform</span><span class="p">(</span><span class="n">OSPlatform</span><span class="p">.</span><span class="n">Windows</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// do something on Windows only</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(!</span><span class="n">RuntimeInformation</span><span class="p">.</span><span class="n">IsOSPlatform</span><span class="p">(</span><span class="n">OSPlatform</span><span class="p">.</span><span class="n">OSX</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// do something as long as we&#39;re not running in OSX</span></span></span></code></pre></div></div>
<p>In a framework meant to target multiple platforms, it seems like a call not supported on <em>all</em> platforms shouldn&rsquo;t be present at all. But I digress&hellip;</p>
<hr>

<h2 class="relative group">Trying to load a config file from NUnit
    <div id="trying-to-load-a-config-file-from-nunit" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#trying-to-load-a-config-file-from-nunit" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>One of the .NET Standard libraries I created assumes the app using it will provide an application configuration file, and so I pulled in <a href="https://www.nuget.org/packages/System.Configuration.ConfigurationManager"  target="_blank" rel="noreferrer">System.Configuration.ConfigurationManager</a> from NuGet <em>(Microsoft&rsquo;s been splitting functionality out of the gargantuan .NET Framework into smaller components too)</em> to read the config file. I had no problem testing the library out by creating a .NET Core app that consumed it and provided a file called <code>app.config</code>. I had a problem when I tried to test the library with <a href="https://nunit.org/"  target="_blank" rel="noreferrer">NUnit</a> though. I wanted my test suite to provide a config file too, but for the life of me couldn&rsquo;t figure out what to name it or how to load it.</p>
<p>It <em>seems</em> like <a href="https://github.com/nunit/docs/wiki/Configuration-Files"  target="_blank" rel="noreferrer">NUnit supports config files</a>, but I&rsquo;m not sure if the docs are referring to some special NUnit config file, something with a specific name, or if the docs are just outdated. What exactly did I try?</p>
<ul>
<li>Dropping in an app.config file</li>
<li>Renaming the app.config file to my_test_project_name.dll.config</li>
<li>Setting the file&rsquo;s &ldquo;Copy to output directory&rdquo; setting to &ldquo;Copy always&rdquo;</li>
<li>Creating copies with every name I could think of, hoping <em>one</em> would load&hellip; app.config, App.config, my_test_project_name.config, my_test_project_name.dll.config, etc, etc..</li>
<li>Loading the config file using <code>AppDomain.CurrentDomain.SetData()</code> (didn&rsquo;t work, possibly because NUnit3 doesn&rsquo;t support AppDomain)</li>
</ul>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">AppDomain.CurrentDomain.SetData(&#34;APP_CONFIG_FILE&#34;, @&#34;C:\Path\To\My\Tests\my_test_project_name.dll.config&#34;);</span></span></code></pre></div></div>
<p>There are tests in the NUnit repo that suggest <a href="https://github.com/nunit/nunit3-vs-adapter-demo/blob/master/src/csharp/ConfigFileTests.cs"  target="_blank" rel="noreferrer">using a configuration file in NUnit3 is possible</a>, but that particular test file is only referenced in the <a href="https://github.com/nunit/nunit3-vs-adapter-demo/blob/master/solutions/vs2017/CSharpTestDemo/CSharpTestDemo.csproj#L49"  target="_blank" rel="noreferrer">.NET 4.5 demo project</a>, not the <a href="https://github.com/nunit/nunit3-vs-adapter-demo/blob/master/solutions/vs2017/NUnit3CoreTestDemo/NUnit3CoreTestDemo.csproj#L10"  target="_blank" rel="noreferrer">.NET Core demo project</a>. <em>Sigh&hellip;</em></p>
<hr>

<h2 class="relative group">Naming things is hard&hellip;
    <div id="naming-things-is-hard" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#naming-things-is-hard" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>After doing research and messing with it for most of a day, I threw in the towel and <a href="https://stackoverflow.com/q/55541912/301857"  target="_blank" rel="noreferrer">turned to StackOverflow</a>. After a couple days I had gotten about 10 views, but I&rsquo;ve got enough magical points to buy more attention, after which I got a couple hundred views and <a href="https://stackoverflow.com/a/55592119/301857"  target="_blank" rel="noreferrer">the answer I was after</a>:</p>
<blockquote><p>When you execute the following line within a unit test and inspect its result, you may notice that the NUnit project looks for a configuration file called <code>testhost.dll.config</code>.</p>
<p><code>ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).FilePath;</code></p>
<p>Also, make sure that the <em>Copy to Output Directory</em> setting for the configuration file is set to <code>Copy always</code>.</p>
</blockquote><p>That name was the secret sauce! It seems that NUnit looks for that specific file, and sure enough after I renamed the config file, it loaded just fine.</p>
<p>Now we can argue all day about whether I should be doing this in a unit test, or an integration test, or whether the .NET Standard library should be expecting a config file at all (it&rsquo;s supported and I&rsquo;m cool with it), but at the end of the day it&rsquo;s possible and this is how.</p>
<hr>

<h2 class="relative group">What I ended up with
    <div id="what-i-ended-up-with" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-i-ended-up-with" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>As usual, I posted to GitHub the code I used to test this - you can <a href="https://github.com/grantwinney/BlogCodeSamples/tree/master/Languages/CSharp/ReadingConfigFile"  target="_blank" rel="noreferrer">view it here</a>. The Demo project is the .NET Core console app that implements the library, while the Tests project is the NUnit project.</p>

<h3 class="relative group">Parsing the config file in .NET Standard
    <div id="parsing-the-config-file-in-net-standard" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#parsing-the-config-file-in-net-standard" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>I created a class that matches the config file, in order to make using it easier. If you&rsquo;d like to learn more about how that works, I wrote about it and <a href="https://grantwinney.com/csharp-attributes/#parsing-config-files"  target="_blank" rel="noreferrer">the many other uses of attributes</a>.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">LogSettings</span> <span class="p">:</span> <span class="n">ConfigurationSection</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="n">Settings</span> <span class="n">Instance</span> <span class="p">=&gt;</span> <span class="p">(</span><span class="n">ConfigurationManager</span><span class="p">.</span><span class="n">GetSection</span><span class="p">(</span><span class="s">&#34;appConfiguration/logging&#34;</span><span class="p">)</span> <span class="k">as</span> <span class="n">LogSettings</span><span class="p">).</span><span class="n">Settings</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [ConfigurationProperty(&#34;application&#34;)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Settings</span> <span class="n">Settings</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="p">(</span><span class="n">Settings</span><span class="p">)</span><span class="k">this</span><span class="p">[</span><span class="s">&#34;application&#34;</span><span class="p">];</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Settings</span> <span class="p">:</span> <span class="n">ConfigurationElement</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">LogLevel</span> <span class="n">Level</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="p">(</span><span class="n">LogLevel</span><span class="p">)</span><span class="n">Enum</span><span class="p">.</span><span class="n">Parse</span><span class="p">(</span><span class="k">typeof</span><span class="p">(</span><span class="n">LogLevel</span><span class="p">),</span> <span class="n">Convert</span><span class="p">.</span><span class="n">ToString</span><span class="p">(</span><span class="n">LevelInternal</span><span class="p">));</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [ConfigurationProperty(&#34;level&#34;, DefaultValue = &#34;1&#34;, IsRequired = false)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kt">int</span> <span class="n">LevelInternal</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">Convert</span><span class="p">.</span><span class="n">ToInt32</span><span class="p">(</span><span class="k">this</span><span class="p">[</span><span class="s">&#34;level&#34;</span><span class="p">]);</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [ConfigurationProperty(&#34;name&#34;, IsRequired = true)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">ApplicationName</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">Convert</span><span class="p">.</span><span class="n">ToString</span><span class="p">(</span><span class="k">this</span><span class="p">[</span><span class="s">&#34;name&#34;</span><span class="p">]);</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [ConfigurationProperty(&#34;logFilePath&#34;, IsRequired = true)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">LogFilePath</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">Convert</span><span class="p">.</span><span class="n">ToString</span><span class="p">(</span><span class="k">this</span><span class="p">[</span><span class="s">&#34;logFilePath&#34;</span><span class="p">]);</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [ConfigurationProperty(&#34;dateFormat&#34;, DefaultValue = &#34;MM/dd/yyyy&#34;, IsRequired = false)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">DateFormat</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">Convert</span><span class="p">.</span><span class="n">ToString</span><span class="p">(</span><span class="k">this</span><span class="p">[</span><span class="s">&#34;dateFormat&#34;</span><span class="p">]);</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [ConfigurationProperty(&#34;includeDate&#34;, DefaultValue = &#34;true&#34;, IsRequired = false)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">bool</span> <span class="n">IncludeDate</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">Convert</span><span class="p">.</span><span class="n">ToBoolean</span><span class="p">(</span><span class="k">this</span><span class="p">[</span><span class="s">&#34;includeDate&#34;</span><span class="p">]);</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h3 class="relative group">Providing a config file from NUnit
    <div id="providing-a-config-file-from-nunit" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#providing-a-config-file-from-nunit" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>In my NUnit project, I created a file called <code>testhost.dll.config</code> and set the &ldquo;Copy to Output Directory&rdquo; setting to &ldquo;Copy always&rdquo;.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="cp">&lt;?xml version=&#34;1.0&#34; encoding=&#34;utf-8&#34; ?&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;configuration&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;configSections&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;sectionGroup</span> <span class="na">name=</span><span class="s">&#34;appConfiguration&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;section</span> <span class="na">name=</span><span class="s">&#34;logging&#34;</span>
</span></span><span class="line"><span class="cl">               <span class="na">type=</span><span class="s">&#34;SampleLibrary.LogSettings, SampleLibrary&#34;</span>
</span></span><span class="line"><span class="cl">               <span class="na">allowLocation=</span><span class="s">&#34;true&#34;</span>
</span></span><span class="line"><span class="cl">               <span class="na">allowDefinition=</span><span class="s">&#34;Everywhere&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/sectionGroup&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;/configSections&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;appConfiguration&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;logging&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;application</span> <span class="na">level=</span><span class="s">&#34;2&#34;</span>
</span></span><span class="line"><span class="cl">                   <span class="na">name=</span><span class="s">&#34;test_logger&#34;</span>
</span></span><span class="line"><span class="cl">                   <span class="na">logFilePath=</span><span class="s">&#34;C:\ProgramData\MyApp\TestLogs\&#34;</span>
</span></span><span class="line"><span class="cl">                   <span class="na">includeDate=</span><span class="s">&#34;false&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/logging&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;/appConfiguration&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/configuration&gt;</span></span></span></code></pre></div></div>

<h3 class="relative group">Testing that the config file loaded correctly
    <div id="testing-that-the-config-file-loaded-correctly" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#testing-that-the-config-file-loaded-correctly" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Finally, I created a set of tests to make sure the config file was consumed by the .NET Standard library correctly.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="na">[TestFixture]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Tests</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="na">   [Test]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">ConfigFile_Loads_ApplicationName</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">AreEqual</span><span class="p">(</span><span class="s">&#34;test_logger&#34;</span><span class="p">,</span> <span class="n">LogSettings</span><span class="p">.</span><span class="n">Instance</span><span class="p">.</span><span class="n">ApplicationName</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [Test]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">ConfigFile_Loads_Level</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">AreEqual</span><span class="p">(</span><span class="n">LogLevel</span><span class="p">.</span><span class="n">Warn</span><span class="p">,</span> <span class="n">LogSettings</span><span class="p">.</span><span class="n">Instance</span><span class="p">.</span><span class="n">Level</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [Test]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">ConfigFile_Loads_LogFilePath</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">AreEqual</span><span class="p">(</span><span class="s">@&#34;C:\ProgramData\MyApp\TestLogs\&#34;</span><span class="p">,</span> <span class="n">LogSettings</span><span class="p">.</span><span class="n">Instance</span><span class="p">.</span><span class="n">LogFilePath</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [Test]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">ConfigFile_Loads_DateFormat</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">AreEqual</span><span class="p">(</span><span class="s">&#34;MM/dd/yyyy&#34;</span><span class="p">,</span> <span class="n">LogSettings</span><span class="p">.</span><span class="n">Instance</span><span class="p">.</span><span class="n">DateFormat</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">    [Test]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">ConfigFile_Loads_IncludeDate</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Assert</span><span class="p">.</span><span class="n">IsFalse</span><span class="p">(</span><span class="n">LogSettings</span><span class="p">.</span><span class="n">Instance</span><span class="p">.</span><span class="n">IncludeDate</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h3 class="relative group">Results&hellip; Success!
    <div id="results-success" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#results-success" aria-label="Anchor">#</a>
    </span>
    
</h3>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-to-use-an-app-config-file-with-a-net-standard-app-and-nunit-3/testresults.PNG"
    width="882"
      height="308"></figure>
]]></content:encoded><media:content url="https://grantwinney.com/how-to-use-an-app-config-file-with-a-net-standard-app-and-nunit-3/feature.webp" medium="image" type="image/webp"/></item><item><title>Using a constant as a DateTime format inside string interpolation</title><link>https://grantwinney.com/csharp-constant-datetime-format-inside-string-interpolation/</link><pubDate>Thu, 04 Apr 2019 20:06:50 +0000</pubDate><guid>https://grantwinney.com/csharp-constant-datetime-format-inside-string-interpolation/</guid><description>I was upgrading some code to use string interpolation, a feature introduced in C# 6, when I ran into a small snag with DateTimes and a format string stored as a constant.</description><content:encoded><![CDATA[<p>I was upgrading some code to use string interpolation, a feature <a href="https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-6#string-interpolation"  target="_blank" rel="noreferrer">introduced in C# 6</a>, when I ran into a small snag with DateTimes and format strings.</p>
<blockquote><p>The code in this post is available on <a href="https://github.com/grantwinney/CSharpDotNetExamples/tree/master/C%23%2006/ConstantDateTimeFormatInStringInterpolation"  target="_blank" rel="noreferrer">GitHub</a>, for you to use, expand upon, or just follow along while you read&hellip; and hopefully discover something new!</p>
</blockquote><p>The original code looked something like this:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">name</span> <span class="p">=</span> <span class="s">&#34;Grant&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">message</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Format</span><span class="p">(</span><span class="s">&#34;Hello {0}, the date is {1:MM/dd/yyyy}.&#34;</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">message</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// output: Hello Grant, the date is 04/04/2019.</span></span></span></code></pre></div></div>
<p>Updating the <code>string.Format</code> to use string interpolation was straight-forward:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">name</span> <span class="p">=</span> <span class="s">&#34;Grant&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">message</span> <span class="p">=</span> <span class="s">$&#34;Hello {name}, the date is {DateTime.Now:MM/dd/yyyy}.&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">message</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// output: Hello Grant, the date is 04/04/2019.</span></span></span></code></pre></div></div>
<p>But then I tried to move the format string into a constant so I could use it in several places, which didn&rsquo;t work:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">const</span> <span class="kt">string</span> <span class="n">DATE_FORMAT</span> <span class="p">=</span> <span class="s">&#34;MM/dd/yyyy&#34;</span><span class="p">;</span>  <span class="c1">// ignored</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">name</span> <span class="p">=</span> <span class="s">&#34;Grant&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">message</span> <span class="p">=</span> <span class="s">$&#34;Hello {name}, the date is {DateTime.Now:DATE_FORMAT}.&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">message</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// output: Hello Grant, the date is DATE_9OR4AT.</span></span></span></code></pre></div></div>
<p>The output looks funky because the <code>F</code> and <code>M</code> in &ldquo;DATE_FORMAT&rdquo; are valid formats (for tenths of a second and month, respectively). It took a few minutes to realize it, but I just had to use the overloaded <code>ToString()</code> method that accepts a format string:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">const</span> <span class="kt">string</span> <span class="n">DATE_FORMAT</span> <span class="p">=</span> <span class="s">&#34;MM/dd/yyyy&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">name</span> <span class="p">=</span> <span class="s">&#34;Grant&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">message</span> <span class="p">=</span> <span class="s">$&#34;Hello {name}, the date is {DateTime.Now.ToString(DATE_FORMAT)}.&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">message</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// output: Hello Grant, the date is 04/04/2019.</span></span></span></code></pre></div></div>
<p>And what is string interpolation? According to <a href="https://docs.microsoft.com/en-us/dotnet/csharp/tutorials/string-interpolation"  target="_blank" rel="noreferrer">MSDN</a>, it&rsquo;s just syntactic sugar around the traditional <code>String.Format</code> method:</p>
<blockquote><p>At compile time, an interpolated string is typically transformed into a <a href="https://docs.microsoft.com/en-us/dotnet/api/system.string.format"  target="_blank" rel="noreferrer">String.Format</a> method call. That makes all the capabilities of the <a href="https://docs.microsoft.com/en-us/dotnet/standard/base-types/composite-formatting"  target="_blank" rel="noreferrer">string composite formatting</a> feature available to you to use with interpolated strings as well.</p>
</blockquote><p>In general, it looks better, is clearer to read, and is more convenient too.</p>

<h2 class="relative group">Other Resources
    <div id="other-resources" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#other-resources" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Want to learn more? Start here:</p>
<ul>
<li><a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated"  target="_blank" rel="noreferrer">$ - string interpolation (C# Reference)</a></li>
<li><a href="https://docs.microsoft.com/en-us/dotnet/csharp/tutorials/string-interpolation"  target="_blank" rel="noreferrer">String interpolation in C#</a></li>
<li><a href="https://weblog.west-wind.com/posts/2016/Dec/27/Back-to-Basics-String-Interpolation-in-C"  target="_blank" rel="noreferrer">Back to Basics: String Interpolation in C#</a></li>
</ul>
]]></content:encoded><media:content url="https://grantwinney.com/csharp-constant-datetime-format-inside-string-interpolation/feature.webp" medium="image" type="image/webp"/></item><item><title>Using Attributes in C#</title><link>https://grantwinney.com/csharp-attributes/</link><pubDate>Thu, 04 Apr 2019 15:57:19 +0000</pubDate><guid>https://grantwinney.com/csharp-attributes/</guid><description>Ever thought it&amp;rsquo;d be convenient to attach metadata to your code at design time, then read it at runtime? Attributes let you do just that - to methods, classes, tests, enumerations, and more. Use reflection to read them at runtime and take some action. Here&amp;rsquo;s a few examples for the uninitiated&amp;hellip;</description><content:encoded><![CDATA[<p>Ever thought it&rsquo;d be convenient to attach some extra info to your code? Not just documentation to read at design time, but something that can actually be consumed at runtime and change how your program runs?</p>
<p><a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/attributes/"  target="_blank" rel="noreferrer">Attributes</a> let you attach metadata to methods, classes, tests, enumerations&hellip; pretty much anything. Then you can use reflection to read them at runtime and take some action. If you haven&rsquo;t used them much before, or if it&rsquo;s just been awhile, let&rsquo;s look at a few practical examples.</p>

<h2 class="relative group">Parsing Config Files
    <div id="parsing-config-files" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#parsing-config-files" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>It&rsquo;s common for a .NET app to include a config file of some sorts (app.config, web.config, etc). These are great because you can simply change values, without having to recompile the app.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="cp">&lt;?xml version=&#34;1.0&#34; encoding=&#34;utf-8&#34; ?&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;configuration&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;configSections&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;section</span> <span class="na">name=</span><span class="s">&#34;application&#34;</span>
</span></span><span class="line"><span class="cl">             <span class="na">type=</span><span class="s">&#34;AttributesExamples.Application, AttributesExamples&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;/configSections&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;application&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;logging</span> <span class="na">name=</span><span class="s">&#34;log.txt&#34;</span>
</span></span><span class="line"><span class="cl">             <span class="na">location=</span><span class="s">&#34;c:\logs&#34;</span>
</span></span><span class="line"><span class="cl">             <span class="na">level=</span><span class="s">&#34;Warn&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;/application&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/configuration&gt;</span></span></span></code></pre></div></div>
<p>They&rsquo;re slightly less great because there&rsquo;s several ways to read those values from your code, and some of them are a pain. By using the <a href="https://docs.microsoft.com/en-us/dotnet/api/system.configuration.configurationproperty?view=netframework-4.7.2"  target="_blank" rel="noreferrer">ConfigurationProperty</a> attribute, and any of the various classes that extend the <a href="https://docs.microsoft.com/en-us/dotnet/api/system.configuration.configurationvalidatorattribute?view=netframework-4.7.2"  target="_blank" rel="noreferrer">ConfigurationValidator</a> attribute, you can actually set up a class to represent the config file at runtime.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">System.Configuration</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">namespace</span> <span class="nn">AttributesExamples</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">enum</span> <span class="n">Severity</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Info</span> <span class="p">=</span> <span class="m">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">Warn</span> <span class="p">=</span> <span class="m">2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">Error</span> <span class="p">=</span> <span class="m">3</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">class</span> <span class="nc">Application</span> <span class="p">:</span> <span class="n">ConfigurationSection</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kd">static</span> <span class="n">LogSettings</span> <span class="n">LoggingConfig</span> <span class="p">=&gt;</span> <span class="p">(</span><span class="n">ConfigurationManager</span><span class="p">.</span><span class="n">GetSection</span><span class="p">(</span><span class="s">&#34;application&#34;</span><span class="p">)</span> <span class="k">as</span> <span class="n">Application</span><span class="p">).</span><span class="n">LogSettings</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">        [ConfigurationProperty(&#34;logging&#34;, Options = ConfigurationPropertyOptions.IsRequired)]</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="n">LogSettings</span> <span class="n">LogSettings</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="p">(</span><span class="n">LogSettings</span><span class="p">)</span><span class="k">this</span><span class="p">[</span><span class="s">&#34;logging&#34;</span><span class="p">];</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">class</span> <span class="nc">LogSettings</span> <span class="p">:</span> <span class="n">ConfigurationElement</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="na">        [ConfigurationProperty(&#34;name&#34;, IsRequired = false, DefaultValue = @&#34;log.txt&#34;)]</span>
</span></span><span class="line"><span class="cl"><span class="na">        [StringValidator(InvalidCharacters = @&#34;:;()!@3$%^&amp;*&#39;&#34;&#34;&lt;&gt;&#34;, MinLength = 1, MaxLength = 32)]</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kt">string</span> <span class="n">LogName</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">Convert</span><span class="p">.</span><span class="n">ToString</span><span class="p">(</span><span class="k">this</span><span class="p">[</span><span class="s">&#34;name&#34;</span><span class="p">]);</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">        [ConfigurationProperty(&#34;location&#34;, IsRequired = true)]</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kt">string</span> <span class="n">LogPath</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">Convert</span><span class="p">.</span><span class="n">ToString</span><span class="p">(</span><span class="k">this</span><span class="p">[</span><span class="s">&#34;location&#34;</span><span class="p">]);</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">        [ConfigurationProperty(&#34;level&#34;, IsRequired = false, DefaultValue = Severity.Info)]</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="n">Severity</span> <span class="n">SeverityLevel</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="p">(</span><span class="n">Severity</span><span class="p">)</span><span class="n">Enum</span><span class="p">.</span><span class="n">Parse</span><span class="p">(</span><span class="k">typeof</span><span class="p">(</span><span class="n">Severity</span><span class="p">),</span> <span class="n">Convert</span><span class="p">.</span><span class="n">ToString</span><span class="p">(</span><span class="k">this</span><span class="p">[</span><span class="s">&#34;level&#34;</span><span class="p">]));</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">        [ConfigurationProperty(&#34;enabled&#34;, IsRequired = false, DefaultValue = true)]</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kt">bool</span> <span class="n">LoggingEnabled</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">Convert</span><span class="p">.</span><span class="n">ToBoolean</span><span class="p">(</span><span class="k">this</span><span class="p">[</span><span class="s">&#34;enabled&#34;</span><span class="p">]);</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Setting up the class is a little time-consuming, but once it&rsquo;s done you have a nice way of accessing and displaying your config settings.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">settings</span> <span class="p">=</span> <span class="n">Application</span><span class="p">.</span><span class="n">LoggingConfig</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Log Name: {settings.LogName}&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Log Location: {settings.LogPath}&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Severity Level: {settings.SeverityLevel}&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Logging {(settings.LoggingEnabled ? @&#34;</span><span class="k">is</span><span class="s">&#34; : @&#34;</span><span class="k">is</span> <span class="n">not</span><span class="s">&#34;)} enabled.&#34;</span><span class="p">);</span></span></span></code></pre></div></div>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/csharp-attributes/parsingdemo.PNG"
    width="923"
      height="215"></figure>
<p>If you want to learn more:</p>
<ul>
<li><a href="https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/"  target="_blank" rel="noreferrer">Configuring Apps by using Configuration Files</a></li>
<li><a href="https://stackoverflow.com/questions/13043530/what-is-app-config-in-c-net-how-to-use-it"  target="_blank" rel="noreferrer">What is App.config in C#.NET? How to use it?</a></li>
</ul>

<h2 class="relative group">Unit Testing
    <div id="unit-testing" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#unit-testing" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>All of the major testing frameworks make use of attributes, whether it&rsquo;s <a href="https://nunit.org/"  target="_blank" rel="noreferrer">NUnit</a>, <a href="https://github.com/xunit/xunit"  target="_blank" rel="noreferrer">xUnit</a>, or <a href="https://docs.microsoft.com/en-us/dotnet/core/testing/unit-testing-with-mstest"  target="_blank" rel="noreferrer">MSTest</a>.</p>
<p>Here&rsquo;s a ridiculous <code>Employee</code> class, thoroughly vetted by some NUnit tests. You can decorate any method you want with <code>SetUp</code> and <code>TearDown</code> attributes, which act like a constructor and destructor run before and after <em>every</em> test. Tests have a <code>Test</code> attribute, and you can even reuse the same test for multiple values with the <code>TestCase</code> attribute. NUnit makes use of all these attributes to know what, when, and how to run things.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">NUnit.Framework</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">namespace</span> <span class="nn">AttributesExamples</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="na">    [TestFixture]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">class</span> <span class="nc">UnitTesting</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Employee</span> <span class="n">emp</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">        [SetUp]</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="k">void</span> <span class="n">AnyNameWeWant</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">emp</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Employee</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">        [TearDown]</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="k">void</span> <span class="n">SomeOtherName</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">emp</span> <span class="p">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">        [Test]</span>
</span></span><span class="line"><span class="cl"><span class="na">        [TestCase(&#34;Lucky&#34;, &#34;Day&#34;, &#34;Lucky Day&#34;)]</span>
</span></span><span class="line"><span class="cl"><span class="na">        [TestCase(&#34;Dusty&#34;, &#34;Bottoms&#34;, &#34;Dusty Bottoms&#34;)]</span>
</span></span><span class="line"><span class="cl"><span class="na">        [TestCase(&#34;Ned&#34;, &#34;Nederlander&#34;, &#34;Ned Nederlander&#34;)]</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="k">void</span> <span class="n">GetFullName_ReturnsFullName</span><span class="p">(</span><span class="kt">string</span> <span class="n">firstName</span><span class="p">,</span> <span class="kt">string</span> <span class="n">lastName</span><span class="p">,</span> <span class="kt">string</span> <span class="n">expectedFullName</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">emp</span><span class="p">.</span><span class="n">SetName</span><span class="p">(</span><span class="n">firstName</span><span class="p">,</span> <span class="n">lastName</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="n">Assert</span><span class="p">.</span><span class="n">AreEqual</span><span class="p">(</span><span class="n">expectedFullName</span><span class="p">,</span> <span class="n">emp</span><span class="p">.</span><span class="n">GetFullName</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">class</span> <span class="nc">Employee</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">private</span> <span class="kt">string</span> <span class="n">firstName</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="kd">private</span> <span class="kt">string</span> <span class="n">lastName</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="k">void</span> <span class="n">SetName</span><span class="p">(</span><span class="kt">string</span> <span class="n">firstName</span><span class="p">,</span> <span class="kt">string</span> <span class="n">lastName</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">this</span><span class="p">.</span><span class="n">firstName</span> <span class="p">=</span> <span class="n">firstName</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">this</span><span class="p">.</span><span class="n">lastName</span> <span class="p">=</span> <span class="n">lastName</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kt">string</span> <span class="n">GetFullName</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="s">$&#34;{firstName} {lastName}&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/csharp-attributes/unittest.PNG"
    width="886"
      height="262"></figure>

<h2 class="relative group">Planned Code Obsolescence
    <div id="planned-code-obsolescence" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#planned-code-obsolescence" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Another practical use for attributes is warning users of your library that you plan on <a href="https://docs.microsoft.com/en-us/dotnet/api/system.obsoleteattribute?view=netframework-4.7.2"  target="_blank" rel="noreferrer">deprecating some piece of code</a>. Even Microsoft, known for its unprecedented backwards-compatibility in the .NET Framework, marks their code as obsolete from <a href="https://github.com/dotnet/coreclr/blob/baa4f19a7158e31b7012ff2dafebfb5f1b1edee4/src/System.Private.CoreLib/shared/System/Reflection/AssemblyFlagsAttribute.cs"  target="_blank" rel="noreferrer">time</a> to <a href="https://github.com/dotnet/coreclr/blob/3b807944d8822b44eb5085d6b95b130b4a91808f/src/System.Private.CoreLib/shared/System/ExecutionEngineException.cs"  target="_blank" rel="noreferrer">time</a>.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">namespace</span> <span class="nn">AttributesExamples</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">class</span> <span class="nc">PlannedCodeObsolescence</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">string</span> <span class="n">firstName</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="kt">string</span> <span class="n">lastName</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="kt">int</span> <span class="n">age</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">        [Obsolete(&#34;SetFirstName and SetLastName are more reliable and replace this method.&#34;)]</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="k">void</span> <span class="n">SetName</span><span class="p">(</span><span class="kt">string</span> <span class="n">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">names</span> <span class="p">=</span> <span class="n">name</span><span class="p">.</span><span class="n">Split</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">            <span class="n">firstName</span> <span class="p">=</span> <span class="n">names</span><span class="p">[</span><span class="m">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">            <span class="n">lastName</span> <span class="p">=</span> <span class="n">names</span><span class="p">[</span><span class="m">1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="k">void</span> <span class="n">SetFirstName</span><span class="p">(</span><span class="kt">string</span> <span class="n">first</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">firstName</span> <span class="p">=</span> <span class="n">first</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="k">void</span> <span class="n">SetLastName</span><span class="p">(</span><span class="kt">string</span> <span class="n">last</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">lastName</span> <span class="p">=</span> <span class="n">last</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="na">
</span></span></span><span class="line"><span class="cl"><span class="na">        [Obsolete(&#34;Function renamed to SetAge in v2.4 for consistency&#34;, true)]</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="k">void</span> <span class="n">AgeSet</span><span class="p">(</span><span class="kt">int</span> <span class="n">age</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">SetAge</span><span class="p">(</span><span class="n">age</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="k">void</span> <span class="n">SetAge</span><span class="p">(</span><span class="kt">int</span> <span class="n">age</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">this</span><span class="p">.</span><span class="n">age</span> <span class="p">=</span> <span class="n">age</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>When you mark something as obsolete, any attempts to use it are underlined in green as a warning. You might leave things like this for several releases, and when you&rsquo;re ready you set the error parameter to <code>true</code>, which tells the compiler to treat usage as an error instead of a warning.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/csharp-attributes/obsolescence.png"
    width="789"
      height="284"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/csharp-attributes/errorlist.PNG"
    width="1174"
      height="196"></figure>

<h2 class="relative group">Bit Field Enums
    <div id="bit-field-enums" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#bit-field-enums" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you&rsquo;ve ever needed to store a bunch of related flags, you <em>could</em> do something like this. It&rsquo;s tedious - and unnecessary.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">PreferredContactMethods</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">IsLandPhoneAllowed</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">IsCellPhoneAllowed</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">IsEmailAllowed</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Instead, create an enumeration and use the <a href="https://docs.microsoft.com/en-us/dotnet/api/system.flagsattribute?view=netframework-4.7.2"  target="_blank" rel="noreferrer">Flags</a> attribute so the system knows to treat it like a bit field. What is that, you ask? Great question.</p>
<p>Imagine you had a bunch of switches or flags, to signify &ldquo;on&rdquo; and &ldquo;off&rdquo; for a variety of settings. You could use 1 for &ldquo;on&rdquo; and 0 for &ldquo;off&rdquo;. So if you had three such flags, and the second was &ldquo;off&rdquo; while the first and third were &ldquo;on&rdquo;, you might represent them like this: <code>1 0 1</code></p>
<p>As it turns out, using bits to represent your flags is a perfect fit, but then how do you code that? Well, 101 in binary is the same as 22 + 21 + 20 = 7 in decimal. If you only use the power of 2 for each flag, you can combine values and know exactly which flags are &ldquo;on&rdquo;. The decimal value of 7 means the first and third flag are set, and nothing else. It&rsquo;s completely unambiguous&hellip; and efficient.</p>
<p>Note the numbering for the enum values in the following example.. all powers of 2. By applying the <code>Flags</code> attribute, you can make use of other .NET code that allows you to quickly select multiple values at once, as well as quickly test which values are selected.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">namespace</span> <span class="nn">AttributesExamples</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="na">    [Flags]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">enum</span> <span class="n">PreferredContactMethods</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">None</span> <span class="p">=</span> <span class="m">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">LandPhone</span> <span class="p">=</span> <span class="m">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">CellPhone</span> <span class="p">=</span> <span class="m">2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">Email</span> <span class="p">=</span> <span class="m">4</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">SnailMail</span> <span class="p">=</span> <span class="m">8</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">Owl</span> <span class="p">=</span> <span class="m">16</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">FlooPowder</span> <span class="p">=</span> <span class="m">32</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">Text</span> <span class="p">=</span> <span class="m">64</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">Muggle</span> <span class="p">=</span> <span class="n">Email</span> <span class="p">|</span> <span class="n">Text</span> <span class="p">|</span> <span class="n">CellPhone</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">Wizard</span> <span class="p">=</span> <span class="n">Owl</span> <span class="p">|</span> <span class="n">FlooPowder</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kd">static</span> <span class="k">void</span> <span class="n">DisplayPreferences</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">prefContactMethods</span> <span class="p">=</span> <span class="n">PreferredContactMethods</span><span class="p">.</span><span class="n">Email</span> <span class="p">|</span> <span class="n">PreferredContactMethods</span><span class="p">.</span><span class="n">FlooPowder</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">prefContactMethods</span> <span class="p">==</span> <span class="n">PreferredContactMethods</span><span class="p">.</span><span class="n">None</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;User hates people. :(&#34;</span><span class="p">);</span>    <span class="c1">// won&#39;t print</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">prefContactMethods</span><span class="p">.</span><span class="n">HasFlag</span><span class="p">(</span><span class="n">PreferredContactMethods</span><span class="p">.</span><span class="n">LandPhone</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">            <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;What&#39;s a landph one? :/&#34;</span><span class="p">);</span>  <span class="c1">// also won&#39;t print</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">prefContactMethods</span><span class="p">.</span><span class="n">HasFlag</span><span class="p">(</span><span class="n">PreferredContactMethods</span><span class="p">.</span><span class="n">CellPhone</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="p">||</span> <span class="n">prefContactMethods</span><span class="p">.</span><span class="n">HasFlag</span><span class="p">(</span><span class="n">PreferredContactMethods</span><span class="p">.</span><span class="n">Email</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">            <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;Aren&#39;t we modern? :p&#34;</span><span class="p">);</span>     <span class="c1">// should print</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">prefContactMethods</span> <span class="p">|=</span> <span class="n">PreferredContactMethods</span><span class="p">.</span><span class="n">Owl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">prefContactMethods</span><span class="p">.</span><span class="n">HasFlag</span><span class="p">(</span><span class="n">PreferredContactMethods</span><span class="p">.</span><span class="n">Wizard</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">            <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;You&#39;re a wizard Harry!! ~:›&#34;</span><span class="p">);</span>  <span class="c1">// this too</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/csharp-attributes/bitflags.PNG"
    width="737"
      height="200"></figure>

<h2 class="relative group">Your Own Implementation
    <div id="your-own-implementation" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#your-own-implementation" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Okay, I tricked you. Number 5 is whatever you come up with!</p>
<p>For me, in creating a library called <a href="https://github.com/grantwinney/GhostSharp"  target="_blank" rel="noreferrer">GhostSharp</a> <em>(a C# wrapper around an API that connects to Ghost blogs),</em> I had a single class representing an object that could be POSTed to an API endpoint. The problem was that a PUT (update) to the same endpoint could only be a subset of those fields&hellip; trying to pass the same object failed. I could&rsquo;ve created two objects - one for POST and one for PUT - but I really wanted to use a single object. The solution?</p>
<p>I created a simple attribute to dress up those fields that were acceptable for an update. I mean, <em>really</em> simple. There&rsquo;s nothing else but a name, but that&rsquo;s all I needed.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="cs">/// &lt;summary&gt;</span>
</span></span><span class="line"><span class="cl"><span class="cs">/// Represents a field that can be updated in a PUT request.</span>
</span></span><span class="line"><span class="cl"><span class="cs">/// &lt;/summary&gt;</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">UpdatableFieldAttribute</span> <span class="p">:</span> <span class="n">Attribute</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>And here&rsquo;s a portion of the class representing a post (as in a blog post, not the REST action POST 😅). The uuid and comment_id fields cannot be updated, but the title and feature_image can. Adding an attribute is only half the work though.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="cs">/// &lt;summary&gt;</span>
</span></span><span class="line"><span class="cl"><span class="cs">/// Request sent to Ghost</span>
</span></span><span class="line"><span class="cl"><span class="cs">/// &lt;/summary&gt;</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">PostRequest</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;summary&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// Collection of posts.</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;/summary&gt;</span>
</span></span><span class="line"><span class="cl"><span class="na">    [JsonProperty(&#34;posts&#34;)]</span>
</span></span><span class="line"><span class="cl"><span class="na">    [UpdatableField]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Post</span><span class="p">&gt;</span> <span class="n">Posts</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cs">/// &lt;summary&gt;</span>
</span></span><span class="line"><span class="cl"><span class="cs">/// Represents a Post.</span>
</span></span><span class="line"><span class="cl"><span class="cs">/// &lt;/summary&gt;</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Post</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;summary&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// ID (Update)</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;/summary&gt;</span>
</span></span><span class="line"><span class="cl"><span class="na">    [JsonProperty(&#34;id&#34;)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Id</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;summary&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// UUID</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;/summary&gt;</span>
</span></span><span class="line"><span class="cl"><span class="na">    [JsonProperty(&#34;uuid&#34;)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Uuid</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;summary&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// Title</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;/summary&gt;</span>
</span></span><span class="line"><span class="cl"><span class="na">    [JsonProperty(&#34;title&#34;)]</span>
</span></span><span class="line"><span class="cl"><span class="na">    [UpdatableField]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Title</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;summary&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// Post, formatted in Mobile Doc</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;/summary&gt;</span>
</span></span><span class="line"><span class="cl"><span class="na">    [JsonProperty(&#34;mobiledoc&#34;)]</span>
</span></span><span class="line"><span class="cl"><span class="na">    [UpdatableField]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">MobileDoc</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;summary&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// Comment ID</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;/summary&gt;</span>
</span></span><span class="line"><span class="cl"><span class="na">    [JsonProperty(&#34;comment_id&#34;)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">CommentId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;summary&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// Feature Image</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;/summary&gt;</span>
</span></span><span class="line"><span class="cl"><span class="na">    [JsonProperty(&#34;feature_image&#34;)]</span>
</span></span><span class="line"><span class="cl"><span class="na">    [UpdatableField]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">FeatureImage</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>I&rsquo;m not going to get into the details of serializing a C# object to JSON, but with the updatable fields marked appropriately I had to write a short piece of code to tell RestSharp when a property should be included in the serialization. Using a little reflection, I could test to see whether the <code>UpdatableField</code> attribute was on a given property - if it is (<code>!= null</code>) then send it with the rest of the PUT.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">protected</span> <span class="kd">override</span> <span class="n">JsonProperty</span> <span class="n">CreateProperty</span><span class="p">(</span><span class="n">MemberInfo</span> <span class="n">member</span><span class="p">,</span> <span class="n">MemberSerialization</span> <span class="n">memberSerialization</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">JsonProperty</span> <span class="n">property</span> <span class="p">=</span> <span class="k">base</span><span class="p">.</span><span class="n">CreateProperty</span><span class="p">(</span><span class="n">member</span><span class="p">,</span> <span class="n">memberSerialization</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">property</span><span class="p">.</span><span class="n">ShouldSerialize</span> <span class="p">=</span>
</span></span><span class="line"><span class="cl">        <span class="n">instance</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">property</span><span class="p">.</span><span class="n">DeclaringType</span><span class="p">.</span><span class="n">GetProperty</span><span class="p">(</span><span class="n">property</span><span class="p">.</span><span class="n">UnderlyingName</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                           <span class="p">.</span><span class="n">GetCustomAttribute</span><span class="p">&lt;</span><span class="n">UpdatableFieldAttribute</span><span class="p">&gt;()</span> <span class="p">!=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">property</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Attributes are a handy way to add metadata to your code, which can be read back using reflection at runtime. In addition, they offer a bit of documentation too - I can look at my <code>post</code> class and quickly confirm which fields are included in an update.</p>
<p>If you&rsquo;re interested in learning about a newer feature called generic attributes, introduced in C# 11, check out <em><a href="https://grantwinney.com/what-are-generic-attributes/"  target="_blank" rel="noreferrer">What are generic attributes in C# 11?</a></em>. And if you want to learn more about a variety of C# features, <a href="https://github.com/grantwinney/CSharpDotNetExamples"  target="_blank" rel="noreferrer">check out my GitHub repo</a>, where you&rsquo;ll find links to plenty more blog posts and practical examples.</p>
]]></content:encoded><media:content url="https://grantwinney.com/csharp-attributes/feature.webp" medium="image" type="image/webp"/></item><item><title>Opening the developer console in every major browser</title><link>https://grantwinney.com/how-do-i-view-the-dev-console-in-my-browser/</link><pubDate>Tue, 26 Mar 2019 14:47:54 +0000</pubDate><guid>https://grantwinney.com/how-do-i-view-the-dev-console-in-my-browser/</guid><description>Most people will never even know their browser hides a great set of tools, mostly used by web developers, but which can be useful for anyone trying to figure out why their browser is misbehaving.</description><content:encoded><![CDATA[<p>Most people will never know their browser hides a great set of tools, mostly used by web developers, but which can be useful for anyone trying to figure out why their browser seems to be acting up.</p>
<p>Finding out why a page is slow, when an addon is throwing errors, what&rsquo;s being requested or sent out - all possible in the dev console. I had to use them just the other day to figure out why a single post on this blog wasn&rsquo;t displaying the summary on the main page - an unexpected character was messing up some custom JavaScript code.</p>
<p>Without further ado&hellip;</p>

<h2 class="relative group">Chrome
    <div id="chrome" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#chrome" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Author: Google<br>
Toolset: <a href="https://developers.google.com/web/tools/chrome-devtools/"  target="_blank" rel="noreferrer">Chrome DevTools</a></p>
<ul>
<li>To view elements of the page (DOM/CSS), either right-click and select &ldquo;Inspect&rdquo; <strong>or</strong> press <code>Command+Option+C</code> (Mac) or <code>Control+Shift+C</code> (Windows, Linux, Chrome OS).</li>
<li>To view the console (logged messages, run JavaScript), press <code>Command+Option+J</code> (Mac) or <code>Control+Shift+J</code> (Windows, Linux, Chrome OS).</li>
</ul>

<h2 class="relative group">Firefox
    <div id="firefox" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#firefox" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Author: Mozilla<br>
Toolset: <a href="https://developer.mozilla.org/en-US/docs/Tools/Web_Console"  target="_blank" rel="noreferrer">Web Console</a></p>
<ul>
<li>Select &ldquo;Web Console&rdquo; from the Web Developer submenu in the Firefox menu (or Tools menu if you display the menu bar or are on Mac).</li>
<li>Or press <code>Ctrl+Shift+K</code> or <code>Ctrl+Shift+C</code> or <code>Ctrl+Shift+I</code> (Windows).</li>
<li>Or press <code>Command+Option+K</code> (Mac).</li>
</ul>

<h2 class="relative group">Opera
    <div id="opera" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#opera" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Author: Opera Software<br>
Toolset: Developer Tools Console</p>
<p>The shortcuts seem to be the same as Chrome, at least by default.</p>
<ul>
<li>To view elements of the page (DOM/CSS), either right-click and select &ldquo;Inspect&rdquo; <strong>or</strong> press <code>Command+Option+C</code> (Mac) or <code>Control+Shift+C</code> (Windows, Linux, Chrome OS).</li>
<li>To view the console (logged messages, run JavaScript), press <code>Command+Option+J</code> (Mac) or <code>Control+Shift+J</code> (Windows, Linux, Chrome OS).</li>
</ul>
<p>You can also change the shortcuts if you&rsquo;d like:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-do-i-view-the-dev-console-in-my-browser/opera.PNG"
    width="1213"
      height="554"></figure>

<h2 class="relative group">Brave
    <div id="brave" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#brave" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://brave.com/gra339"  target="_blank" rel="noreferrer">If you&rsquo;re not using Brave yet, do</a>. This is my preferred browser now that they have all of the annoying kinks worked out&hellip; it&rsquo;s more secure by default and they have this novel idea for rewarding good content.</p>
<p>Author: Brave Software Inc<br>
Toolset: Developer Tools</p>
<p>The shortcuts seem to be the same as Chrome and Opera.</p>
<ul>
<li>To view elements of the page (DOM/CSS), either right-click and select &ldquo;Inspect&rdquo; <strong>or</strong> press <code>Command+Option+C</code> (Mac) or <code>Control+Shift+C</code> (Windows, Linux, Chrome OS).</li>
<li>To view the console (logged messages, run JavaScript), press <code>Command+Option+J</code> (Mac) or <code>Control+Shift+J</code> (Windows, Linux, Chrome OS).</li>
</ul>

<h2 class="relative group">Internet Explorer
    <div id="internet-explorer" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#internet-explorer" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Author: Microsoft<br>
Toolset: <a href="https://msdn.microsoft.com/library/hh968260%5C%28v=vs.85"  target="_blank" rel="noreferrer">Developer Tools</a></p>
<ul>
<li>Press <code>F12</code>.</li>
<li>Or right-click and choose &ldquo;View source&rdquo; or &ldquo;Inspect element&rdquo;.</li>
</ul>

<h2 class="relative group">Edge
    <div id="edge" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#edge" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Author: Microsoft<br>
Toolset: <a href="https://docs.microsoft.com/en-us/microsoft-edge/devtools-guide"  target="_blank" rel="noreferrer">Microsoft Edge DevTools</a></p>
<ul>
<li>Press <code>F12</code> or <code>Control+Shift+J</code> or <code>Control+Shift+I</code>.</li>
<li>Or right-click and choose &ldquo;View source&rdquo; or &ldquo;Inspect element&rdquo; <em>(possibly only after opening the DevTools at least once)</em>.</li>
</ul>

<h2 class="relative group">Safari
    <div id="safari" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#safari" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Author: Apple<br>
Toolset: <a href="https://developer.apple.com/safari/tools/"  target="_blank" rel="noreferrer">Web Inspector</a></p>
<p>That link is a pretty basic (nearly useless) overview - there&rsquo;s more detail in the <a href="https://support.apple.com/en-gb/guide/safari-developer/safari-developer-tools-overview-dev073038698/mac"  target="_blank" rel="noreferrer">Safari Developer Help docs</a>. I don&rsquo;t have a Mac or Safari, so the best I could do to test this was <a href="https://apple.stackexchange.com/a/68837/156872"  target="_blank" rel="noreferrer">download Safari 5 for Windows</a> from 2012.</p>
<ul>
<li>Enable the Develop menu in Advanced preferences: <code>Safari &gt; Preferences &gt; Advanced &gt; Show Develop menu</code>, then choose <code>Develop &gt; Show Error Console</code></li>
<li>Select Show Web Inspector Option-Command-I in the Develop Menu. Or control-click anywhere in the Safari tab and choose &ldquo;Inspect Element&rdquo; from the menu.</li>
<li>You can also add a Web Inspector button to your toolbar by customizing your Safari Window.</li>
<li>If you&rsquo;re using the last available Windows version from 2012: <code>Edit &gt; Preferences &gt; Advanced &gt; Show Develop menu in menu bar</code></li>
<li>After showing the new menu, you can view page elements with <code>Control+Alt+I</code> or the console with <code>Control+Alt+C</code>. Beware, it&rsquo;s a pretty busted experience. 🙄</li>
</ul>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/how-do-i-view-the-dev-console-in-my-browser/safari.png"
    width="758"
      height="429"></figure>
]]></content:encoded><media:content url="https://grantwinney.com/how-do-i-view-the-dev-console-in-my-browser/feature.webp" medium="image" type="image/webp"/></item><item><title>Keep your EUnit teardown logic as simple as possible!</title><link>https://grantwinney.com/keep-your-eunit-teardown-logic-as-simple/</link><pubDate>Thu, 14 Mar 2019 11:03:00 +0000</pubDate><guid>https://grantwinney.com/keep-your-eunit-teardown-logic-as-simple/</guid><description>Ever had an EUnit test fixture fail with meck reporting it was &amp;ldquo;already_started&amp;rdquo;? Well I did, and here&amp;rsquo;s why&amp;hellip;</description><content:encoded><![CDATA[<p>When you use <a href="https://learnyousomeerlang.com/eunit#fixtures"  target="_blank" rel="noreferrer">test fixtures</a> in EUnit, you&rsquo;ll likely define a <code>setup</code> and a <code>teardown</code> function, for doing initialization and cleanup work before and after each test. If you&rsquo;re familiar with <code>try/catch/finally</code> blocks in other languages, the teardown function is similar to a <code>finally</code> block; that is, it should always run even when a test throws an exception. But like a <code>finally</code> block, you want to be careful about what you&rsquo;re doing in your cleanup.</p>
<p>I ran into an issue recently where EUnit tests that were part of a <a href="https://learnyousomeerlang.com/eunit#test-generators"  target="_blank" rel="noreferrer">test fixture</a> were failing with an error I hadn&rsquo;t seen before. The error seemed to be coming from the <a href="https://github.com/eproxus/meck"  target="_blank" rel="noreferrer">meck</a> mocking suite itself, and was reporting that it was &ldquo;already_started&rdquo;&hellip; and the tests would fail to run.</p>
<p>Here&rsquo;s a small program we can use to see the problem. All it does is accept a name, and print out a short greeting with the current time. <em>(The code below is trimmed down, but the</em> <a href="https://github.com/grantwinney/BlogCodeSamples/tree/master/Languages/Erlang/MeckTeardownTest"  target="_blank" rel="noreferrer"><em>full code is available on GitHub</em></a> <em>if you&rsquo;d like to run it. You&rsquo;ll want to have</em> <a href="https://www.rebar3.org/docs/getting-started"  target="_blank" rel="noreferrer"><em>Rebar3</em></a> <em>installed, and it&rsquo;d help to be familiar with</em> <a href="https://github.com/eproxus/meck"  target="_blank" rel="noreferrer"><em>Meck</em></a><em>.)</em></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">module</span><span class="p">(</span><span class="n">salutations_app</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">export</span><span class="p">([</span><span class="n">greeting_time</span><span class="o">/</span><span class="mi">1</span><span class="p">]).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">greeting_time</span><span class="p">(</span><span class="nv">Name</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="n">format</span><span class="p">(</span><span class="s">&#34;Hi </span><span class="si">~s</span><span class="s">, it&#39;s </span><span class="si">~s</span><span class="s">!&#34;</span><span class="p">,</span> <span class="p">[</span><span class="nv">Name</span><span class="p">,</span> <span class="n">current_time</span><span class="p">()]).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">%% INTERNAL
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">current_time</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">binary_to_list</span><span class="p">(</span><span class="nn">iso8601</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="nn">calendar</span><span class="p">:</span><span class="nf">universal_time</span><span class="p">())).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">format</span><span class="p">(</span><span class="nv">Template</span><span class="p">,</span> <span class="nv">Params</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nn">lists</span><span class="p">:</span><span class="nf">flatten</span><span class="p">(</span><span class="nn">io_lib</span><span class="p">:</span><span class="nf">fwrite</span><span class="p">(</span><span class="nv">Template</span><span class="p">,</span> <span class="nv">Params</span><span class="p">)).</span></span></span></code></pre></div></div>

<h2 class="relative group">Teardown succeeds, even when a test throws an exception
    <div id="teardown-succeeds-even-when-a-test-throws-an-exception" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#teardown-succeeds-even-when-a-test-throws-an-exception" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Here&rsquo;s the first example. Two of these tests intentionally throw exceptions - dividing by zero and sorting a non-list - but the <code>teardown</code> function should run regardless of whether individual tests throw an exception.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">module</span><span class="p">(</span><span class="n">exceptions_in_tests</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">ifdef</span><span class="p">(</span><span class="nv">EUNIT</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">include_lib</span><span class="p">(</span><span class="s">&#34;eunit/include/eunit.hrl&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">import</span><span class="p">(</span><span class="n">salutations_app</span><span class="p">,</span> <span class="p">[</span><span class="n">greeting_time</span><span class="o">/</span><span class="mi">1</span><span class="p">]).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">setup</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Modules</span> <span class="o">=</span> <span class="p">[</span><span class="n">iso8601</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nn">meck</span><span class="p">:</span><span class="nf">new</span><span class="p">(</span><span class="nv">Modules</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nn">meck</span><span class="p">:</span><span class="nf">expect</span><span class="p">(</span><span class="n">iso8601</span><span class="p">,</span> <span class="n">format</span><span class="p">,</span> <span class="k">fun</span><span class="p">(_)</span> <span class="o">-&gt;</span> <span class="o">&lt;&lt;</span><span class="s">&#34;2019-02-16T01:06:48Z&#34;</span><span class="o">&gt;&gt;</span> <span class="k">end</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Modules</span><span class="p">.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">teardown</span><span class="p">(</span><span class="nv">Modules</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">?</span><span class="n">debugFmt</span><span class="p">(</span><span class="s">&#34;Do we ALWAYS get into teardown? (yes)&#34;</span><span class="p">,</span> <span class="p">[]),</span>
</span></span><span class="line"><span class="cl">    <span class="nn">meck</span><span class="p">:</span><span class="nf">unload</span><span class="p">(</span><span class="nv">Modules</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">greeting_time_test_</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="n">foreach</span><span class="p">,</span> <span class="k">fun</span> <span class="n">setup</span><span class="o">/</span><span class="mi">0</span><span class="p">,</span> <span class="k">fun</span> <span class="n">teardown</span><span class="o">/</span><span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">     <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="s">&#34;greet bob&#34;</span><span class="p">,</span> <span class="k">fun</span> <span class="n">bob_gets_expected_greeting</span><span class="o">/</span><span class="mi">0</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="s">&#34;greet tim&#34;</span><span class="p">,</span> <span class="k">fun</span> <span class="n">tim_gets_expected_greeting</span><span class="o">/</span><span class="mi">0</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="s">&#34;greet sue&#34;</span><span class="p">,</span> <span class="k">fun</span> <span class="n">sue_gets_expected_greeting</span><span class="o">/</span><span class="mi">0</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">     <span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="p">}.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">bob_gets_expected_greeting</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="mi">1</span><span class="o">/</span><span class="mi">0</span><span class="p">,</span>  <span class="c">% &lt;- no good can come of this!
</span></span></span><span class="line"><span class="cl">    <span class="o">?</span><span class="n">assertEqual</span><span class="p">(</span><span class="s">&#34;Hi Bob, it&#39;s 2019-02-16T01:06:48Z!&#34;</span><span class="p">,</span> <span class="nn">salutations_app</span><span class="p">:</span><span class="nf">greeting_time</span><span class="p">(</span><span class="s">&#34;Bob&#34;</span><span class="p">)).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">tim_gets_expected_greeting</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">?</span><span class="n">assertEqual</span><span class="p">(</span><span class="s">&#34;Hi Tim, it&#39;s 2019-02-16T01:06:48Z!&#34;</span><span class="p">,</span> <span class="nn">salutations_app</span><span class="p">:</span><span class="nf">greeting_time</span><span class="p">(</span><span class="s">&#34;Tim&#34;</span><span class="p">)).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">sue_gets_expected_greeting</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">?</span><span class="n">debugFmt</span><span class="p">(</span><span class="s">&#34;Do we start the test? (yes)&#34;</span><span class="p">,</span> <span class="p">[]),</span>
</span></span><span class="line"><span class="cl">    <span class="nn">lists</span><span class="p">:</span><span class="nf">sort</span><span class="p">(</span><span class="n">this_aint_no_list</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="o">?</span><span class="n">debugFmt</span><span class="p">(</span><span class="s">&#34;Do we finish the test? (no way)&#34;</span><span class="p">,</span> <span class="p">[]),</span>
</span></span><span class="line"><span class="cl">    <span class="o">?</span><span class="n">assertEqual</span><span class="p">(</span><span class="s">&#34;Hi Sue, it&#39;s 2019-02-16T01:06:48Z!&#34;</span><span class="p">,</span> <span class="nn">salutations_app</span><span class="p">:</span><span class="nf">greeting_time</span><span class="p">(</span><span class="s">&#34;Sue&#34;</span><span class="p">)).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">endif</span><span class="p">.</span></span></span></code></pre></div></div>
<p>From the output below, we can see where <code>sue_gets_expected_greeting</code> printed the first debug statement, but not the second after the exception is thrown. Both exceptions are printed to the console. But the <code>teardown</code> function ran all three times, even for the tests that fail. 👍</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">&gt; rebar3 eunit --module exceptions_in_tests
===&gt; Verifying dependencies...
===&gt; Compiling salutations
===&gt; Performing EUnit tests...

&lt;0.107.0&gt;: Do we ALWAYS get into teardown? (yes)
&lt;0.107.0&gt;: Do we ALWAYS get into teardown? (yes)
&lt;0.131.0&gt;: Do we start the test? (yes)
&lt;0.107.0&gt;: Do we ALWAYS get into teardown? (yes)

Failures:

  1) exceptions_in_tests:greeting_time_test_/0: greet bob
     Failure/Error: {error,badarith,
                        [{exceptions_in_tests,bob_gets_expected_greeting,0,
                             [{file,
                                  &#34;c:/.../exceptions_in_tests.erl&#34;},
                              {line,29}]}]}

  2) exceptions_in_tests:greeting_time_test_/0: greet sue
     Failure/Error: {error,function_clause,
                        [{lists,sort,
                             [this_aint_no_list],
                             [{file,&#34;lists.erl&#34;},{line,478}]},
                         {exceptions_in_tests,sue_gets_expected_greeting,0,
                             [{file,
                                  &#34;c:/.../exceptions_in_tests.erl&#34;},
                              {line,37}]}]}

Finished in 0.343 seconds
3 tests, 2 failures
===&gt; Error running tests</code></pre></div>

<h2 class="relative group">Teardown fails, when the teardown itself throws an exception
    <div id="teardown-fails-when-the-teardown-itself-throws-an-exception" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#teardown-fails-when-the-teardown-itself-throws-an-exception" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Here&rsquo;s the second example. Now the tests should pass, but the <code>teardown</code> function itself will throw an exception. The question is, what happens when it throws before the <code>meck:unload</code> runs?</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">module</span><span class="p">(</span><span class="n">exceptions_in_teardown</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">ifdef</span><span class="p">(</span><span class="nv">EUNIT</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">include_lib</span><span class="p">(</span><span class="s">&#34;eunit/include/eunit.hrl&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">import</span><span class="p">(</span><span class="n">salutations_app</span><span class="p">,</span> <span class="p">[</span><span class="n">greeting_time</span><span class="o">/</span><span class="mi">1</span><span class="p">]).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">setup</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Modules</span> <span class="o">=</span> <span class="p">[</span><span class="n">iso8601</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nn">meck</span><span class="p">:</span><span class="nf">new</span><span class="p">(</span><span class="nv">Modules</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nn">meck</span><span class="p">:</span><span class="nf">expect</span><span class="p">(</span><span class="n">iso8601</span><span class="p">,</span> <span class="n">format</span><span class="p">,</span> <span class="k">fun</span><span class="p">(_)</span> <span class="o">-&gt;</span> <span class="o">&lt;&lt;</span><span class="s">&#34;2019-02-16T01:06:48Z&#34;</span><span class="o">&gt;&gt;</span> <span class="k">end</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Modules</span><span class="p">.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">teardown</span><span class="p">(</span><span class="nv">Modules</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">?</span><span class="n">debugFmt</span><span class="p">(</span><span class="s">&#34;Do we ALWAYS get into teardown? (well, the first time...)&#34;</span><span class="p">,</span> <span class="p">[]),</span>
</span></span><span class="line"><span class="cl">    <span class="p">_</span> <span class="o">=</span> <span class="mi">1</span><span class="o">/</span><span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nn">meck</span><span class="p">:</span><span class="nf">unload</span><span class="p">(</span><span class="nv">Modules</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">greeting_time_test_</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="n">foreach</span><span class="p">,</span> <span class="k">fun</span> <span class="n">setup</span><span class="o">/</span><span class="mi">0</span><span class="p">,</span> <span class="k">fun</span> <span class="n">teardown</span><span class="o">/</span><span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">     <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="s">&#34;greet bob&#34;</span><span class="p">,</span> <span class="k">fun</span> <span class="n">bob_gets_expected_greeting</span><span class="o">/</span><span class="mi">0</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="s">&#34;greet tim&#34;</span><span class="p">,</span> <span class="k">fun</span> <span class="n">tim_gets_expected_greeting</span><span class="o">/</span><span class="mi">0</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="s">&#34;greet sue&#34;</span><span class="p">,</span> <span class="k">fun</span> <span class="n">sue_gets_expected_greeting</span><span class="o">/</span><span class="mi">0</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">     <span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="p">}.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">bob_gets_expected_greeting</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">?</span><span class="n">assertEqual</span><span class="p">(</span><span class="s">&#34;Hi Bob, it&#39;s 2019-02-16T01:06:48Z!&#34;</span><span class="p">,</span> <span class="nn">salutations_app</span><span class="p">:</span><span class="nf">greeting_time</span><span class="p">(</span><span class="s">&#34;Bob&#34;</span><span class="p">)).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">tim_gets_expected_greeting</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">?</span><span class="n">assertEqual</span><span class="p">(</span><span class="s">&#34;Hi Tim, it&#39;s 2019-02-16T01:06:48Z!&#34;</span><span class="p">,</span> <span class="nn">salutations_app</span><span class="p">:</span><span class="nf">greeting_time</span><span class="p">(</span><span class="s">&#34;Tim&#34;</span><span class="p">)).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">sue_gets_expected_greeting</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">?</span><span class="n">assertEqual</span><span class="p">(</span><span class="s">&#34;Hi Sue, it&#39;s 2019-02-16T01:06:48Z!&#34;</span><span class="p">,</span> <span class="nn">salutations_app</span><span class="p">:</span><span class="nf">greeting_time</span><span class="p">(</span><span class="s">&#34;Sue&#34;</span><span class="p">)).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">endif</span><span class="p">.</span></span></span></code></pre></div></div>
<p>Nothing good, as it turns out! <em>This</em> is why I was seeing the &ldquo;already_started&rdquo; error - a previous <code>meck:unload</code> fails to run and the next test causes <code>meck:new</code> to run again. This example is silly, but what if you had some tests creating a file (yeah, yeah, against unit test philosophy but whatever) and wanted to delete it each time? What if one of those deletes failed and threw an exception? Every test after it fails too. 😭</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">&gt; rebar3 eunit --module exceptions_in_teardown
===&gt; Verifying dependencies...
===&gt; Compiling salutations
===&gt; Performing EUnit tests...

&lt;0.218.0&gt;: Do we ALWAYS get into teardown? (well, the first time...)

Pending:
  undefined
    %% Unknown error: {abort,
                   {cleanup_failed,
                       {error,badarith,
                           [{exceptions_in_teardown,teardown,1,
                                [{file,
                                     &#34;c:/.../exceptions_in_teardown.erl&#34;},
                                 {line,17}]}]}}}
  undefined
    %% Unknown error: {abort,
                   {setup_failed,
                       {error,
                           {already_started,&lt;0.219.0&gt;},
                           [{meck_proc,start,
                                [iso8601,[]],
                                [{file,
                                     &#34;c:/.../_build/test/lib/meck/src/meck_proc.erl&#34;},
                                 {line,93}]},
                            {lists,foreach,2,[{file,&#34;lists.erl&#34;},{line,1336}]},
                            {meck,new,1,
                                [{file,
                                     &#34;c:/.../_build/test/lib/meck/src/meck.erl&#34;},
                                 {line,141}]},
                            {exceptions_in_teardown,setup,0,
                                [{file,
                                     &#34;c:/.../exceptions_in_teardown.erl&#34;},
                                 {line,11}]}]}}}
  undefined
    %% Unknown error: {abort,
                   {setup_failed,
                       {error,
                           {already_started,&lt;0.219.0&gt;},
                           [{meck_proc,start,
                                [iso8601,[]],
                                [{file,
                                     &#34;c:/.../_build/test/lib/meck/src/meck_proc.erl&#34;},
                                 {line,93}]},
                            {lists,foreach,2,[{file,&#34;lists.erl&#34;},{line,1336}]},
                            {meck,new,1,
                                [{file,
                                     &#34;c:/.../_build/test/lib/meck/src/meck.erl&#34;},
                                 {line,141}]},
                            {exceptions_in_teardown,setup,0,
                                [{file,
                                     &#34;c:/.../exceptions_in_teardown.erl&#34;},
                                 {line,11}]}]}}}

Finished in 0.235 seconds
3 tests, 0 failures, 3 cancelled
===&gt; Error running tests</code></pre></div>

<h2 class="relative group">Teardown succeeds, as long as it handles exceptions
    <div id="teardown-succeeds-as-long-as-it-handles-exceptions" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#teardown-succeeds-as-long-as-it-handles-exceptions" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Oooookay, first let me say you should really refactor your <code>teardown</code> function to do as little as possible and make it simple. But if that&rsquo;s not possible, then at the very least surround anything that could potentially fail in a <a href="https://learnyousomeerlang.com/errors-and-exceptions#dealing-with-exceptions"  target="_blank" rel="noreferrer">try/catch/after block</a>. Here&rsquo;s one final example that catches exceptions and <em>guarantees</em> that the <code>meck:unload</code> will run.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">module</span><span class="p">(</span><span class="n">exceptions_in_teardown_handled</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">ifdef</span><span class="p">(</span><span class="nv">EUNIT</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">include_lib</span><span class="p">(</span><span class="s">&#34;eunit/include/eunit.hrl&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">import</span><span class="p">(</span><span class="n">salutations_app</span><span class="p">,</span> <span class="p">[</span><span class="n">greeting_time</span><span class="o">/</span><span class="mi">1</span><span class="p">]).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">setup</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Modules</span> <span class="o">=</span> <span class="p">[</span><span class="n">iso8601</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nn">meck</span><span class="p">:</span><span class="nf">new</span><span class="p">(</span><span class="nv">Modules</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nn">meck</span><span class="p">:</span><span class="nf">expect</span><span class="p">(</span><span class="n">iso8601</span><span class="p">,</span> <span class="n">format</span><span class="p">,</span> <span class="k">fun</span><span class="p">(_)</span> <span class="o">-&gt;</span> <span class="o">&lt;&lt;</span><span class="s">&#34;2019-02-16T01:06:48Z&#34;</span><span class="o">&gt;&gt;</span> <span class="k">end</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Modules</span><span class="p">.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">teardown</span><span class="p">(</span><span class="nv">Modules</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span>
</span></span><span class="line"><span class="cl">        <span class="o">?</span><span class="n">debugFmt</span><span class="p">(</span><span class="s">&#34;Do we ALWAYS get into teardown? (hopefully!)&#34;</span><span class="p">,</span> <span class="p">[]),</span>
</span></span><span class="line"><span class="cl">        <span class="p">_</span> <span class="o">=</span> <span class="mi">1</span><span class="o">/</span><span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="k">of</span> <span class="p">_</span> <span class="o">-&gt;</span> <span class="n">ok</span>
</span></span><span class="line"><span class="cl">    <span class="k">catch</span>
</span></span><span class="line"><span class="cl">        <span class="nv">C</span><span class="p">:</span><span class="nv">R</span> <span class="o">-&gt;</span> <span class="o">?</span><span class="n">debugFmt</span><span class="p">(</span><span class="s">&#34;Teardown failed!!! </span><span class="si">~p</span><span class="s"> : </span><span class="si">~p</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[</span><span class="nv">C</span><span class="p">,</span><span class="nv">R</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">    <span class="k">after</span>
</span></span><span class="line"><span class="cl">        <span class="o">?</span><span class="n">debugFmt</span><span class="p">(</span><span class="s">&#34;Do we ALWAYS get into after block?&#34;</span><span class="p">,</span> <span class="p">[]),</span>
</span></span><span class="line"><span class="cl">        <span class="nn">meck</span><span class="p">:</span><span class="nf">unload</span><span class="p">(</span><span class="nv">Modules</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">greeting_time_test_</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="n">foreach</span><span class="p">,</span> <span class="k">fun</span> <span class="n">setup</span><span class="o">/</span><span class="mi">0</span><span class="p">,</span> <span class="k">fun</span> <span class="n">teardown</span><span class="o">/</span><span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">     <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="s">&#34;greet bob&#34;</span><span class="p">,</span> <span class="k">fun</span> <span class="n">bob_gets_expected_greeting</span><span class="o">/</span><span class="mi">0</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="s">&#34;greet tim&#34;</span><span class="p">,</span> <span class="k">fun</span> <span class="n">tim_gets_expected_greeting</span><span class="o">/</span><span class="mi">0</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="s">&#34;greet sue&#34;</span><span class="p">,</span> <span class="k">fun</span> <span class="n">sue_gets_expected_greeting</span><span class="o">/</span><span class="mi">0</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">     <span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="p">}.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">bob_gets_expected_greeting</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">?</span><span class="n">assertEqual</span><span class="p">(</span><span class="s">&#34;Hi Bob, it&#39;s 2019-02-16T01:06:48Z!&#34;</span><span class="p">,</span> <span class="nn">salutations_app</span><span class="p">:</span><span class="nf">greeting_time</span><span class="p">(</span><span class="s">&#34;Bob&#34;</span><span class="p">)).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">tim_gets_expected_greeting</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">?</span><span class="n">assertEqual</span><span class="p">(</span><span class="s">&#34;Hi Tim, it&#39;s 2019-02-16T01:06:48Z!&#34;</span><span class="p">,</span> <span class="nn">salutations_app</span><span class="p">:</span><span class="nf">greeting_time</span><span class="p">(</span><span class="s">&#34;Tim&#34;</span><span class="p">)).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">sue_gets_expected_greeting</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">?</span><span class="n">assertEqual</span><span class="p">(</span><span class="s">&#34;Hi Sue, it&#39;s 2019-02-16T01:06:48Z!&#34;</span><span class="p">,</span> <span class="nn">salutations_app</span><span class="p">:</span><span class="nf">greeting_time</span><span class="p">(</span><span class="s">&#34;Sue&#34;</span><span class="p">)).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">endif</span><span class="p">.</span></span></span></code></pre></div></div>
<p>So now we have a <code>teardown</code> that should always run <em>and</em> finish, thanks to an <code>after</code> block that runs <code>meck:unload</code> if all hell breaks loose. Granted, if it <em>did</em> throw when it failed to delete a file, you might run into other issues&hellip; but one disaster at a time. 😎</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">&gt; rebar3 eunit --module exceptions_in_teardown_handled
===&gt; Verifying dependencies...
===&gt; Compiling salutations
===&gt; Performing EUnit tests...

&lt;0.107.0&gt;: Do we ALWAYS get into teardown? (hopefully!)
&lt;0.107.0&gt;: Teardown failed!!! error : badarith
&lt;0.107.0&gt;: Do we ALWAYS get into after block?
    
&lt;0.107.0&gt;: Do we ALWAYS get into teardown? (hopefully!)
&lt;0.107.0&gt;: Teardown failed!!! error : badarith
&lt;0.107.0&gt;: Do we ALWAYS get into after block?
    
&lt;0.107.0&gt;: Do we ALWAYS get into teardown? (hopefully!)
&lt;0.107.0&gt;: Teardown failed!!! error : badarith
&lt;0.107.0&gt;: Do we ALWAYS get into after block?

Finished in 0.438 seconds
3 tests, 0 failures</code></pre></div>
]]></content:encoded><media:content url="https://grantwinney.com/keep-your-eunit-teardown-logic-as-simple/feature.webp" medium="image" type="image/webp"/></item><item><title>Regaining access to a Mozilla Addons account</title><link>https://grantwinney.com/lost-access-to-your-mozilla-addons-account/</link><pubDate>Wed, 13 Mar 2019 03:29:29 +0000</pubDate><guid>https://grantwinney.com/lost-access-to-your-mozilla-addons-account/</guid><description>I recently realized that somehow, in the 6 months since I last logged into my Mozilla developer account, none of my short list of emails would let me back in. Here&amp;rsquo;s how I regained access.</description><content:encoded><![CDATA[<p>I recently realized that somehow, in the 6 months since I last logged into my Mozilla developer account, none of my short list of emails would let me back in.</p>
<p>As with any company who offers free software to the masses, it was a little tough trying to find just who to ask for help. I stumbled on <a href="https://discourse.mozilla.org/t/i-cant-remember-the-email-address-i-used-to-upload-my-addons-so-i-cant-access-the-account/36785"  target="_blank" rel="noreferrer">Mozilla&rsquo;s Discourse board</a>, and posted my situation there. Caitlin, a Community Manager at Mozilla and a moderator on the board, was kind enough to point me in the right direction.</p>
<blockquote><p>Hey **@**grantwinney, can you email amo-admins [at] mozilla [dot] org with this information and include the email addresses you think you used with your account? The admins might be able to help you out.</p>
</blockquote><p>According to their <a href="https://wiki.mozilla.org/AMO"  target="_blank" rel="noreferrer">wiki</a>, that email is their support mailing list. So I did as she said and included my usual email addresses, and in short order heard back from Philipp, who also works for Mozilla. <em>(You can find the</em> <a href="https://wiki.mozilla.org/Add-ons"  target="_blank" rel="noreferrer"><em>whole team</em></a> <em>along with a bunch of other related info too.)</em></p>
<blockquote><p>Hi Grant, we&rsquo;ve received this request, apparently from you. The email address to use is **<em><strong>@</strong></em>. If it says there is no account, please create a new account and you will regain access to your add-ons. Thanks, Philipp</p>
</blockquote><p>There you have it. If you lose access, try emailing the amo-admins list. Or if you only have one email address, try creating a new account with it. I had to create a new account using the email address they said was attached to my addons, and sure enough I regained access and was able to update it again! Weird.</p>
<p><strong>Kudos to the Mozilla staff. I got very prompt and personal help&hellip; thanks!!</strong></p>
<p>I&rsquo;m glad I didn&rsquo;t dead-end in a public forum where some other user had the same problem 10 years ago and no one ever answered. 😅</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/lost-access-to-your-mozilla-addons-account/xkcd-wisdom-of-the-ancients.png"
    width="485"
      height="270"></figure>
<p><a href="https://xkcd.com/979/"  target="_blank" rel="noreferrer">xkcd: Wisdom of the Ancients</a></p>
]]></content:encoded><media:content url="https://grantwinney.com/lost-access-to-your-mozilla-addons-account/feature.webp" medium="image" type="image/webp"/></item><item><title>Find info about an IP address with the IP Geolocation API</title><link>https://grantwinney.com/using-the-ip-geolocation-api-to-find-info-about-an-ip-address/</link><pubDate>Tue, 12 Feb 2019 11:47:00 +0000</pubDate><guid>https://grantwinney.com/using-the-ip-geolocation-api-to-find-info-about-an-ip-address/</guid><description>Last year I caught an article about a simple, free service called ipify that returns your IP address. It became so popular the author soon found himself dealing with billions of requests per month! Here&amp;rsquo;s a look at that API and the IP Geolocation API that it spawned.</description><content:encoded><![CDATA[<p>I read an article awhile back, called <a href="https://dev.to/rdegges/to-30-billion-and-beyond-3f94"  target="_blank" rel="noreferrer">To 30 Billion and Beyond</a>, about a simple to use and completely free service called <a href="https://www.ipify.org/"  target="_blank" rel="noreferrer">ipify</a> <em>(</em><a href="https://github.com/rdegges/ipify-api"  target="_blank" rel="noreferrer"><em>source code</em></a><em>),</em> which returns your current IP address in a few different formats. Randall Degges wrote it because he needed it personally, but it became so popular he soon found himself having to deal with <em>tens of billions</em> of requests per month!</p>
<p>The spinoff from that was a separate <a href="https://geoipify.whoisxmlapi.com/"  target="_blank" rel="noreferrer">IP Geolocation API</a> that can tell you all kinds of information about an IP address once you have it. That&rsquo;s what I want to look at today, although I&rsquo;ll show an example of ipify too.</p>
<p>Before we dig deeper though, a few things to consider:</p>
<ul>
<li>If you&rsquo;re new to APIs, here&rsquo;s a brief intro: <a href="https://grantwinney.com/what-is-an-api/"  target="_blank" rel="noreferrer">What is an API?</a></li>
<li>You also may want to install <a href="https://www.getpostman.com/"  target="_blank" rel="noreferrer">Postman</a>, which lets you access endpoints without having to write an app, plus it saves/syncs everything to the cloud.</li>
<li>Afterwards, check out some <a href="https://grantwinney.com/tags/api/"  target="_blank" rel="noreferrer">other APIs I&rsquo;ve written about</a>.</li>
</ul>
<hr>

<h2 class="relative group">ipify
    <div id="ipify" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#ipify" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There&rsquo;s not much else to say about it. Randall Degges, personal project, 30 billion requests, the road to hell and all that. ;) It&rsquo;s amazing he was (and presumably still is) paying for it out of pocket every month. Quite generous.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">GET https://api.ipify.org
  &gt; 123.456.123.456

GET https://api.ipify.org?format=json
  &gt; { &#34;ip&#34;: &#34;123.456.123.456&#34; }

GET https://api.ipify.org?format=jsonp&amp;callback=process_reply  
  &gt; process_reply({&#34;ip&#34;:&#34;71.74.96.6&#34;});</code></pre></div>
<p>He provides tons of examples in different languages, and links to libraries written for different languages and frameworks too. If you wanted to try it out in C#, for example, it&rsquo;s as simple as a half-dozen lines of code.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">httpClient</span> <span class="p">=</span> <span class="k">new</span> <span class="n">HttpClient</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">ip_task</span> <span class="p">=</span> <span class="n">httpClient</span><span class="p">.</span><span class="n">GetStringAsync</span><span class="p">(</span><span class="s">&#34;https://api.ipify.org&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">ip_task</span><span class="p">.</span><span class="n">Wait</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;My public IP address is: {ip_task.Result}&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">ReadLine</span><span class="p">();</span></span></span></code></pre></div></div>
<p>I could see one obvious application, where you only have a dynamic IP at your house, but you want to run a web server that&rsquo;s available externally. Maybe something for personal use, like security cameras setup around your property. Normally you&rsquo;d need a static IP, but if you ran a small service that checked for your IP once a minute and then notified you&hellip; hmm&hellip;</p>
<hr>

<h2 class="relative group">Getting Started
    <div id="getting-started" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#getting-started" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Whois API provides a free tier that provides 1000 requests per month <em>(to this particular API - they have</em> <a href="https://user.whoisxmlapi.com/products"  target="_blank" rel="noreferrer"><em>other APIs</em></a> <em>with their own limits),</em> so just <a href="https://geoipify.whoisxmlapi.com/signup"  target="_blank" rel="noreferrer">sign up</a>. Right away, they provide you with a sample query using Google&rsquo;s well-known name server and your personal API key. It&rsquo;s a great way to get started quickly.</p>

<h3 class="relative group">What&rsquo;s Google&rsquo;s geolocation?
    <div id="whats-googles-geolocation" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whats-googles-geolocation" aria-label="Anchor">#</a>
    </span>
    
</h3>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">https://geoipify.whoisxmlapi.com/api/v1?ipAddress=8.8.8.8&amp;apiKey=&lt;your_key&gt;</span></span></code></pre></div></div>
<p>Unless something&rsquo;s gone drastically wrong, running this in Postman should produce a result like this. If that&rsquo;s the case, your network is up, Google is up, and your API key works. :)</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;ip&#34;</span><span class="p">:</span> <span class="s2">&#34;8.8.8.8&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;country&#34;</span><span class="p">:</span> <span class="s2">&#34;US&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;region&#34;</span><span class="p">:</span> <span class="s2">&#34;California&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;city&#34;</span><span class="p">:</span> <span class="s2">&#34;Mountain View&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">37.40599</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-122.078514</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;postalCode&#34;</span><span class="p">:</span> <span class="s2">&#34;94043&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;timezone&#34;</span><span class="p">:</span> <span class="s2">&#34;-08:00&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;as&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;asn&#34;</span><span class="p">:</span> <span class="mi">15169</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;GOOGLE&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;route&#34;</span><span class="p">:</span> <span class="s2">&#34;8.8.8.0/24&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;domain&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h3 class="relative group">What&rsquo;s <em>your</em> geolocation?
    <div id="whats-your-geolocation" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whats-your-geolocation" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Just leave the IP address off the request, and it uses yours! That could be useful, say if you want to run a server from your house that&rsquo;s available externally, but you have a</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">https://geoipify.whoisxmlapi.com/api/v1?apiKey=&lt;your_key&gt;</code></pre></div>

<h3 class="relative group">What&rsquo;s your blog&rsquo;s geolocation?
    <div id="whats-your-blogs-geolocation" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whats-your-blogs-geolocation" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>You can search for a website&rsquo;s geolocation info too, just by specifying the domain name.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">https://geoipify.whoisxmlapi.com/api/v1?domain=grantwinney.com&amp;apiKey=&lt;your_key&gt;</code></pre></div>
<p>The response is most likely wherever my server is running.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;ip&#34;</span><span class="p">:</span> <span class="s2">&#34;45.55.81.77&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;country&#34;</span><span class="p">:</span> <span class="s2">&#34;US&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;region&#34;</span><span class="p">:</span> <span class="s2">&#34;New Jersey&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;city&#34;</span><span class="p">:</span> <span class="s2">&#34;Clifton&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">40.8344</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-74.1377</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;postalCode&#34;</span><span class="p">:</span> <span class="s2">&#34;07014&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;timezone&#34;</span><span class="p">:</span> <span class="s2">&#34;America/New_York&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;domains&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;grantwinney.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;www.grantwinney.com&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<hr>

<h2 class="relative group">What next?
    <div id="what-next" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-next" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>So, this was a short one. Their API is ridiculously easy to use in comparison to some other APIs I&rsquo;ve tried out. Signup was easy, the &ldquo;free&rdquo; tier of usage is obvious and well documented, and the site is clean and easy to read. Awesome!</p>
<p>They have a bunch of other APIs to check out too if you&rsquo;re interested. Maybe I&rsquo;ll cover one or more of them in the future&hellip;</p>
<ul>
<li>WHOIS API: <a href="https://whoisapi.whoisxmlapi.com"  target="_blank" rel="noreferrer">https://whoisapi.whoisxmlapi.com</a></li>
<li>Email Verification API: <a href="https://emailverification.whoisxmlapi.com"  target="_blank" rel="noreferrer">https://emailverification.whoisxmlapi.com</a></li>
<li>IP Geolocation API: <a href="https://geoipify.whoisxmlapi.com"  target="_blank" rel="noreferrer">https://geoipify.whoisxmlapi.com</a></li>
<li>Reverse IP API: <a href="https://reverse-ip-api.whoisxmlapi.com"  target="_blank" rel="noreferrer">https://reverse-ip-api.whoisxmlapi.com</a></li>
<li>Reverse MX API: <a href="https://reverse-mx-api.whoisxmlapi.com"  target="_blank" rel="noreferrer">https://reverse-mx-api.whoisxmlapi.com</a></li>
<li>Reverse NS API: <a href="https://reverse-ns-api.whoisxmlapi.com"  target="_blank" rel="noreferrer">https://reverse-ns-api.whoisxmlapi.com</a></li>
<li>Other APIs: <a href="https://whoisxmlapi.com"  target="_blank" rel="noreferrer">https://whoisxmlapi.com</a></li>
</ul>
]]></content:encoded><media:content url="https://grantwinney.com/using-the-ip-geolocation-api-to-find-info-about-an-ip-address/feature.webp" medium="image" type="image/webp"/></item><item><title>Fun web comics for developers</title><link>https://grantwinney.com/web-comics-for-devs/</link><pubDate>Sun, 10 Feb 2019 18:58:54 +0000</pubDate><guid>https://grantwinney.com/web-comics-for-devs/</guid><description>Need a comic break? Here&amp;rsquo;s some web comics I&amp;rsquo;ve stumbled upon over the years - the funny, sarcastic, informative, and just plain weird.</description><content:encoded><![CDATA[<p>Who couldn&rsquo;t use a little light-heartedness in their day? Or dry humor if that&rsquo;s what you&rsquo;re looking for? Or something educational in an easy to digest format?</p>
<p>Enjoy! <em>(I know I will&hellip;)</em></p>
<hr>

<h2 class="relative group">CommitStrip
    <div id="commitstrip" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#commitstrip" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://commitstrip.tumblr.com/"  target="_blank" rel="noreferrer">CommitStrip</a> is a fun comic about life as a developer, especially web development. The stories are the shared results of a group of devs from Europe and Asia, and if you&rsquo;ve been a dev for any amount of time they&rsquo;ll hit close to home. 😃</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/web-comics-for-devs/Strip-VIM-appla-650-finalenglish2-1.jpg"
    width="650"
      height="979"></figure>
<p><a href="http://www.commitstrip.com/en/2017/05/29/trapped/"  target="_blank" rel="noreferrer">Trapped - CommitStrip</a></p>
<hr>

<h2 class="relative group">XKCD
    <div id="xkcd" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#xkcd" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://xkcd.com/"  target="_blank" rel="noreferrer">XKCD</a> by Randall Munroe is one of those comics it&rsquo;s tough to avoid, assuming you wanted to try. Like Seinfeld, they&rsquo;re so numerous and relatable they&rsquo;re quoted everywhere. He also writes <a href="https://what-if.xkcd.com/"  target="_blank" rel="noreferrer">What If?</a>&hellip; serious scientific answers to absurd questions.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="|297x335"
    src="https://imgs.xkcd.com/comics/estimation.png"
    ></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="https://imgs.xkcd.com/comics/compiling.png"
    ></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/web-comics-for-devs/xkcd-nerd-sniping.png"
    width="740"
      height="371"></figure>
<p><a href="https://xkcd.com/612/"  target="_blank" rel="noreferrer">Estimation</a> / <a href="https://xkcd.com/303/"  target="_blank" rel="noreferrer">Compiling</a> / <a href="https://xkcd.com/356/"  target="_blank" rel="noreferrer">Nerd Sniping</a> (XKCD)</p>
<hr>

<h2 class="relative group">O RLY? Book Covers
    <div id="o-rly-book-covers" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#o-rly-book-covers" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Once in awhile an O RLY? <em>(parody of O&rsquo;Reilly)</em> book cover pops up somewhere online. Who writes them? Where are they posted? I have no clue. They hit close to home too, but usually in a <em>&ldquo;too true.. too damn true&rdquo;</em> kinda way. 😏</p>
<p>Here&rsquo;s <a href="https://boyter.org/2016/04/collection-orly-book-covers/"  target="_blank" rel="noreferrer">a collection of book covers</a>, and you can even <a href="https://dev.to/rly"  target="_blank" rel="noreferrer">generate your own</a>.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/web-comics-for-devs/orly-resolving-broken-deps.png"
    width="914"
      height="1200"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/web-comics-for-devs/orly-six-git-commands.png"
    width="914"
      height="1200"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/web-comics-for-devs/orly-change-stuff-what-happens.png"
    width="600"
      height="787"></figure>
<hr>

<h2 class="relative group">PHD Comics
    <div id="phd-comics" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#phd-comics" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="http://phdcomics.com/comics/most_popular.php"  target="_blank" rel="noreferrer">PHD Comics</a> by <a href="http://jorgecham.com/"  target="_blank" rel="noreferrer">Jorge Cham</a> has been around a <em>long</em> time, for Interweb standards anyway. It&rsquo;s a collection of hundreds (thousands?) of comics about university life&hellip; and probably a fair amount of other stuff too. Oh, and <a href="https://www.amazon.com/gp/product/0735211515"  target="_blank" rel="noreferrer">he has a book</a> I&rsquo;m thinking about getting.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="http://phdcomics.com/comics/archive/phd031714s.gif"
    ></figure>
<p><a href="http://phdcomics.com/comics.php?f=1690"  target="_blank" rel="noreferrer">Programming for Non-Programmers - PHD Comics</a></p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="|600x1762"
    src="http://phdcomics.com/comics/archive/phd020116_part1_600.jpg"
    ></figure>
<p><a href="http://phdcomics.com/comics/archive.php?comicid=1853"  target="_blank" rel="noreferrer">Gravitational Waves Explained - PHD Comics</a></p>
<hr>

<h2 class="relative group">The Oatmeal
    <div id="the-oatmeal" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-oatmeal" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://theoatmeal.com/"  target="_blank" rel="noreferrer">The Oatmeal</a> by Matthew Inman is hit or miss for me personally, but the degradation of social skills of a remote worker is pretty darned funny. 🧦</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="|645x758"
    src="https://s3.amazonaws.com/theoatmeal-img/comics/working_home/6.png"
    ></figure>
<p><a href="http://theoatmeal.com/comics/working_home"  target="_blank" rel="noreferrer">Why working at home is both awesome and horrible - The Oatmeal</a></p>
<hr>

<h2 class="relative group">MonkeyUser
    <div id="monkeyuser" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#monkeyuser" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://www.monkeyuser.com/"  target="_blank" rel="noreferrer">MonkeyUser</a> by Cornel and Constantin is another funny comic about life as a dev. The comic below is completely believable with some of the test suites in old monolithic apps. 💀</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/web-comics-for-devs/110-test-optimization.webp"
    width="1500"
      height="1632"></figure>
<p><a href="https://www.monkeyuser.com/2018/test-optimization/"  target="_blank" rel="noreferrer">Tests Optimization - MonkeyUser</a></p>
<hr>

<h2 class="relative group">Zines
    <div id="zines" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#zines" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://jvns.ca/zines/"  target="_blank" rel="noreferrer">Julia Evans&rsquo; zines</a> are a really cool idea. What better way to learn about an array of topics than in graphic form? Go check them out and learn something new!</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/web-comics-for-devs/julia-evans-zines.jpg"
    width="800"
      height="1302"></figure>
<p><a href="https://jvns.ca/debugging-zine.pdf"  target="_blank" rel="noreferrer">Linux Debugging Tools You&rsquo;ll Love - Julia Evans</a></p>
<hr>

<h2 class="relative group">Abstruse Goose
    <div id="abstruse-goose" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#abstruse-goose" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Abstruse Goose <em><strong>was</strong></em> a comic about random topics&hellip; although quite a few seemed to be about development. There&rsquo;s a few archives laying around, but the most organized and accessible I&rsquo;ve seen is the <a href="https://github.com/s-macke/Abstruse-Goose-Archive"  target="_blank" rel="noreferrer">Abstruse Goose Archive</a> on GitHub.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="The Happy Programmer - Abstruse Goose"
    src="/web-comics-for-devs/abstrusegoose-joyofprogramming.png"
    width="744"
      height="299"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="How to Teach Yourself Programming - Abstruse Goose"
    src="/web-comics-for-devs/abstrusegoose-249.png"
    width="744"
      height="638"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Other People&rsquo;s Code"
    src="/web-comics-for-devs/abstrusegoose-432.png"
    width="744"
      height="612"></figure>
<hr>

<h2 class="relative group">Invisible Bread
    <div id="invisible-bread" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#invisible-bread" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="http://invisiblebread.com"  target="_blank" rel="noreferrer">Invisible Bread</a> by Justin Boyd has a lot of just plain fun stuff. He&rsquo;s a developer too, but the comics are all over the place. Oh, and go read his about page for a secret. 🤫</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="http://invisiblebread.com/comics/2013-07-25-my-wallet.png"
    ></figure>
<p><a href="http://invisiblebread.com/2013/07/my-wallet/"  target="_blank" rel="noreferrer">My Wallet - Invisible Bread</a></p>
]]></content:encoded><media:content url="https://grantwinney.com/web-comics-for-devs/feature.webp" medium="image" type="image/webp"/></item><item><title>Common dialyzer errors and solutions in Erlang</title><link>https://grantwinney.com/common-dialyzer-errors-and-solutions-in-erlang/</link><pubDate>Sat, 09 Feb 2019 21:10:27 +0000</pubDate><guid>https://grantwinney.com/common-dialyzer-errors-and-solutions-in-erlang/</guid><description>When dealing with a dynamically typed language, any effort to tame the beast can pay off. For Erlang that means Dialyzer specs, but they can be a pain. Here&amp;rsquo;s some warnings I&amp;rsquo;ve seen and how to solve them.</description><content:encoded><![CDATA[<p>When you&rsquo;re dealing with a dynamically typed language like Erlang, any effort to <a href="https://grantwinney.com/taming-the-erlang-beast/#dialyzer"  target="_blank" rel="noreferrer">tame the beast</a> can pay off in spades. I&rsquo;m currently focused on an Erlang app that has zero <a href="https://learnyousomeerlang.com/dialyzer"  target="_blank" rel="noreferrer">Dialyzer specs</a> in it, so adding them is the hill I&rsquo;m currently dying on. If you&rsquo;re new to it, check out <a href="https://learnyousomeerlang.com/dialyzer"  target="_blank" rel="noreferrer">Learn You Some Erlang</a>.</p>
<blockquote><p>Dialyzer begins each analysis optimistically assuming that all functions are good. It will see them as always succeeding, accepting anything, and possibly returning anything. No matter how an unknown is used, it&rsquo;s a good way to use it. This is why warnings about unknown functions are not a big deal when generating PLTs. It&rsquo;s all good anyway; Dialyzer is a natural optimist when it comes to type inference. As the analysis goes, Dialyzer gets to know your functions better and better.</p>
</blockquote><p>The more specs you add, the more helpful and complete the tool becomes, but getting to that point can be painful at first. Trust me, it&rsquo;s worth it. Once implemented, Dialyzer can save you runtime exceptions, make you aware of dead code, and more. It&rsquo;s prevented me from making some stupid mistakes.</p>
<p>Here are some of the errors I&rsquo;m encountering as I&rsquo;m struggling to add specs, along with what they mean and how to solve them. It&rsquo;ll never be all-inclusive, but I&rsquo;ll just keep adding to it over time and hopefully it&rsquo;ll save someone else a headache.</p>
<hr>

<h2 class="relative group">Overlapping Domains
    <div id="overlapping-domains" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#overlapping-domains" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>When you&rsquo;ve got several functions with the same arity that expect different types of parameters, it&rsquo;s normal to specify several Dialyzer specs too&hellip; but sometimes they accidentally overlap and need to be crunched down to a single definition.</p>
<blockquote><p>Overloaded contract for module:function/2 has overlapping domains; such contracts are currently unsupported and are simply ignored</p>
</blockquote>
<h3 class="relative group">Subsets / Supersets
    <div id="subsets--supersets" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#subsets--supersets" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>It&rsquo;s possible one set of specs is a direct subset of another. Here&rsquo;s two clauses with the same arity, one that accepts positive integers and the other that accepts all integers. I&rsquo;m aware this could be reworked to pass, but humor me. Assuming you couldn&rsquo;t change the code, the first spec should just be removed in favor of the second.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">add</span><span class="p">(</span><span class="n">pos_integer</span><span class="p">(),</span> <span class="n">pos_integer</span><span class="p">())</span> <span class="o">-&gt;</span> <span class="n">pos_integer</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">         <span class="p">(</span><span class="n">integer</span><span class="p">(),</span> <span class="n">integer</span><span class="p">())</span> <span class="o">-&gt;</span> <span class="n">integer</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">         <span class="p">(</span><span class="n">string</span><span class="p">(),</span> <span class="n">string</span><span class="p">())</span> <span class="o">-&gt;</span> <span class="n">string</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">add</span><span class="p">(</span><span class="nv">X</span><span class="p">,</span> <span class="nv">Y</span><span class="p">)</span> <span class="k">when</span> <span class="nb">is_integer</span><span class="p">(</span><span class="nv">X</span><span class="p">),</span> <span class="nb">is_integer</span><span class="p">(</span><span class="nv">Y</span><span class="p">),</span> <span class="nv">X</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">Y</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">X</span> <span class="o">+</span> <span class="nv">Y</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">add</span><span class="p">(</span><span class="nv">X</span><span class="p">,</span> <span class="nv">Y</span><span class="p">)</span> <span class="k">when</span> <span class="nb">is_integer</span><span class="p">(</span><span class="nv">X</span><span class="p">),</span> <span class="nb">is_integer</span><span class="p">(</span><span class="nv">Y</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">abs</span><span class="p">(</span><span class="nv">X</span> <span class="o">+</span> <span class="nv">Y</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">add</span><span class="p">(</span><span class="nv">X</span><span class="p">,</span> <span class="nv">Y</span><span class="p">)</span> <span class="k">when</span> <span class="nb">is_list</span><span class="p">(</span><span class="nv">X</span><span class="p">),</span> <span class="nb">is_list</span><span class="p">(</span><span class="nv">Y</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">X</span> <span class="o">++</span> <span class="nv">Y</span><span class="p">.</span></span></span></code></pre></div></div>

<h3 class="relative group">Redundancy
    <div id="redundancy" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#redundancy" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>It&rsquo;s also possible that two specs cover more cases than intended. Here&rsquo;s two clauses where the first accepts undefined and anything to return anything, and the second accepts anything with a string to return anything. These overlap, since ultimately the function can accept anything in either position to return anything. IMO it&rsquo;d actually be better to allow them separately to better indicate what the developer&rsquo;s intentions were, but it&rsquo;s not supported so that&rsquo;s that.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">get_value</span><span class="p">(</span><span class="n">undefined</span><span class="p">,</span> <span class="n">any</span><span class="p">())</span> <span class="o">-&gt;</span> <span class="n">any</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">               <span class="p">(</span><span class="n">any</span><span class="p">(),</span> <span class="n">string</span><span class="p">())</span> <span class="o">-&gt;</span> <span class="n">any</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_value</span><span class="p">(</span><span class="n">undefined</span><span class="p">,</span> <span class="nv">Default</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Default</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_value</span><span class="p">(</span><span class="nv">Value</span><span class="p">,</span> <span class="nv">Default</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nn">lager</span><span class="p">:</span><span class="nf">debug</span><span class="p">(</span><span class="s">&#34;Default would&#39;ve been: </span><span class="si">~s</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[</span><span class="nv">Default</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Value</span><span class="p">.</span></span></span></code></pre></div></div>
<hr>

<h2 class="relative group">Invalid Type Specification
    <div id="invalid-type-specification" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#invalid-type-specification" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>As the name suggests, this one occurs any time there&rsquo;s an invalid type spec&hellip; and there could be lots of reasons your specification is invalid.</p>
<blockquote><p>Invalid type specification for function module:function/1. The success typing is (boolean()) -&gt; atom()</p>
</blockquote>
<h3 class="relative group">Missing Parentheses
    <div id="missing-parentheses" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#missing-parentheses" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>It could be as simple as accidentally omitting the parentheses after the type name. For example, typing <code>boolean</code> instead of <code>boolean()</code>.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">get_status</span><span class="p">(</span><span class="n">boolean</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">atom</span><span class="p">().</span></span></span></code></pre></div></div>

<h3 class="relative group">Wrong Type
    <div id="wrong-type" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#wrong-type" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Or it could just be that, true to the name, the type is wrong. Here&rsquo;s a function that only ever returns an atom, but the spec claims it&rsquo;s a boolean.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">get_status</span><span class="p">(</span><span class="n">boolean</span><span class="p">())</span> <span class="o">-&gt;</span> <span class="n">boolean</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_status</span><span class="p">(</span><span class="n">true</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="n">ready</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_status</span><span class="p">(</span><span class="n">false</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="n">not_ready</span><span class="p">.</span></span></span></code></pre></div></div>

<h3 class="relative group">Inconsistent Types
    <div id="inconsistent-types" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#inconsistent-types" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Check for inconsistent specs between related functions. Here someone saw the comparison in <code>test_system_status</code> and mistook the return value for <code>boolean()</code>, when the boolean value is really being sent to <code>get_status()</code> and the ultimate return value is an <code>atom()</code>.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">test_system_status</span><span class="p">(</span><span class="nl">#a_record</span><span class="p">{})</span> <span class="o">-&gt;</span> <span class="n">boolean</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">test_system_status</span><span class="p">(</span><span class="nv">Record</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="n">get_status</span><span class="p">(</span><span class="nv">Record</span><span class="nl">#a_record.field_one</span> <span class="o">=:=</span> <span class="s">&#34;foo&#34;</span> <span class="ow">or</span>
</span></span><span class="line"><span class="cl">               <span class="nv">Record</span><span class="nl">#a_record.field_two</span> <span class="o">=:=</span> <span class="s">&#34;bar&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">get_status</span><span class="p">(</span><span class="n">boolean</span><span class="p">())</span> <span class="o">-&gt;</span> <span class="n">atom</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_status</span><span class="p">(</span><span class="n">true</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="n">ready</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_status</span><span class="p">(</span><span class="n">false</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="n">not_ready</span><span class="p">.</span></span></span></code></pre></div></div>
<p>Another possibility is an inconsistency between how a field is being treated in several places within a function. Here, Dialyzer will complain that the &ldquo;success typing&rdquo; for <code>Age</code> is actually a string. It&rsquo;s being accepted as an integer, treated as and integer, then stored in a string field. Yeah I know, it&rsquo;d be goofy to define the record like that, but it makes a point. :)</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">record</span><span class="p">(</span><span class="nl">person</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">name</span> <span class="p">::</span> <span class="n">string</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">    <span class="n">age</span> <span class="p">::</span> <span class="n">string</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">    <span class="n">comment</span> <span class="p">::</span> <span class="n">string</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">create_person</span><span class="p">(</span><span class="n">string</span><span class="p">(),</span> <span class="n">integer</span><span class="p">())</span> <span class="o">-&gt;</span> <span class="nl">#person</span><span class="p">{}.</span>
</span></span><span class="line"><span class="cl"><span class="nf">create_person</span><span class="p">(</span><span class="nv">Name</span><span class="p">,</span> <span class="nv">Age</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nv">Age</span> <span class="o">&gt;</span> <span class="mi">100</span> <span class="k">of</span>
</span></span><span class="line"><span class="cl">        <span class="n">true</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nv">Comment</span> <span class="o">=</span> <span class="s">&#34;Woah, you&#39;re old.&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">_</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nv">Comment</span> <span class="o">=</span> <span class="s">&#34;Get back to work.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nl">#person</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">name</span> <span class="o">=</span> <span class="nv">Name</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">age</span> <span class="o">=</span> <span class="nv">Age</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">comment</span> <span class="o">=</span> <span class="nv">Comment</span>
</span></span><span class="line"><span class="cl">    <span class="p">}.</span></span></span></code></pre></div></div>
<hr>

<h2 class="relative group">Function Will Never Be Called
    <div id="function-will-never-be-called" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#function-will-never-be-called" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>This one&rsquo;s nice. Dialyzer can tell you pretty easily if a function is dead code. If a function isn&rsquo;t exported or referenced in its module - in other words, it&rsquo;ll never be called - it&rsquo;ll warn you.</p>
<blockquote><p>Function function/2 will never be called</p>
</blockquote><p>Look for functions that are not exported or used anywhere in the module. You&rsquo;ll usually get a warning when compiling, even without Dialyzer. This might seem obvious, but if you&rsquo;re dealing with source files that are a couple thousand lines with a hundred functions calling one another, dead code can hide pretty well. And once you remove one dead function, it may turn out other code that <em>it</em> was calling is dead too.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">module</span><span class="p">(</span><span class="n">test</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">export</span><span class="p">([]).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">create_greeting</span><span class="p">(</span><span class="n">string</span><span class="p">(),</span> <span class="n">string</span><span class="p">())</span> <span class="o">-&gt;</span> <span class="n">string</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">create_greeting</span><span class="p">(</span><span class="nv">Name</span><span class="p">,</span> <span class="nv">Greeting</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Greeting</span> <span class="o">++</span> <span class="s">&#34;, &#34;</span> <span class="o">++</span> <span class="nv">Name</span> <span class="o">++</span> <span class="s">&#34;!&#34;</span><span class="p">.</span></span></span></code></pre></div></div>
<hr>

<h2 class="relative group">The Pattern Can Never Match
    <div id="the-pattern-can-never-match" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-pattern-can-never-match" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>As you add more Dialyzer specs, it&rsquo;s capable of analyzing your code to determine where certain clauses couldn&rsquo;t <em>possibly</em> be hit. This is great for removing unused code, including unnecessary &ldquo;catch-all&rdquo; clauses.</p>
<blockquote><p>The pattern <em>some_pattern</em> can never match since previous clauses completely covered the type <em>some_type</em></p>
</blockquote><blockquote><p>The variable some_variable can never match since previous clauses completely covered the type some_type</p>
</blockquote><p>Take the <a href="http://erlang.org/doc/apps/kernel/application.html#get_env-1"  target="_blank" rel="noreferrer">application:get_env</a> function for example. It either finds the value and returns <code>{ok, Value}</code>, or doesn&rsquo;t and returns <code>undefined</code>. That&rsquo;s it.. it won&rsquo;t return any other value. Let&rsquo;s say someone writes a catch-all (the fourth clause) to handle unexpected input&hellip; Dialyzer will complain that it could never match, and it&rsquo;s right.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">get_username</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="n">any</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_username</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="n">get_value</span><span class="p">(</span><span class="nn">application</span><span class="p">:</span><span class="nf">get_env</span><span class="p">(</span><span class="n">username</span><span class="p">)).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">get_value</span><span class="p">(</span><span class="n">tuple</span><span class="p">()</span> <span class="p">|</span> <span class="n">undefined</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="p">{</span><span class="n">ok</span><span class="p">,</span> <span class="n">any</span><span class="p">()}.</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_value</span><span class="p">(</span><span class="n">undefined</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="n">undefined</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_value</span><span class="p">({</span><span class="n">ok</span><span class="p">,</span> <span class="s">&#34;none&#34;</span><span class="p">})</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="n">undefined</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_value</span><span class="p">({</span><span class="n">ok</span><span class="p">,</span> <span class="nv">Value</span><span class="p">})</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_value</span><span class="p">({_,</span> <span class="p">_})</span> <span class="o">-&gt;</span>  <span class="c">% The pattern {_, _} can never match since previous clauses
</span></span></span><span class="line"><span class="cl">    <span class="n">undefined</span><span class="p">.</span>        <span class="err">%</span>   <span class="n">completely</span> <span class="n">covered</span> <span class="n">the</span> <span class="n">type</span> <span class="n">&#39;undefined&#39;</span> <span class="p">|</span> <span class="p">{</span><span class="n">&#39;ok&#39;</span><span class="p">,_}</span></span></span></code></pre></div></div>
<p>In the above example, the &ldquo;catch-all&rdquo; <em>might</em> be valid, except that all possible values for the type were covered in previous clauses. Remove one of those clauses though, and the &ldquo;catch-all&rdquo; could be valuable.</p>
<p>Sometimes though, there&rsquo;s a clause that&rsquo;s just plain wrong. It attempts to handle a value that could never be passed to it.</p>
<blockquote><p>The pattern some_pattern can never match the type some_type</p>
</blockquote><p>Take this function, for instance. It passes the result of <code>lists:any</code>, which can only produce a boolean value, to a function with a &ldquo;catch-all&rdquo;. Under no circumstances will that last clause ever be hit, and Dialyzer knows it.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">any_waldos</span><span class="p">([</span><span class="n">string</span><span class="p">()])</span> <span class="o">-&gt;</span> <span class="n">string</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">any_waldos</span><span class="p">(</span><span class="nv">Names</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="n">create_message</span><span class="p">(</span><span class="nn">lists</span><span class="p">:</span><span class="nf">any</span><span class="p">(</span><span class="k">fun</span> <span class="p">(</span><span class="nv">Name</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nv">Name</span> <span class="o">=:=</span> <span class="s">&#34;Waldo&#34;</span> <span class="k">end</span><span class="p">,</span> <span class="nv">Names</span><span class="p">)).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">create_message</span><span class="p">(</span><span class="n">boolean</span><span class="p">()</span> <span class="p">|</span> <span class="n">undefined</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">string</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">create_message</span><span class="p">(</span><span class="n">true</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;Found Waldo!&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">create_message</span><span class="p">(</span><span class="n">false</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;Where&#39;s Waldo?&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">create_message</span><span class="p">(</span><span class="n">undefined</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;Error&#34;</span><span class="p">.</span></span></span></code></pre></div></div>
<hr>

<h2 class="relative group">Matching of Pattern Tagged with a Record Name Violates Declared Type
    <div id="matching-of-pattern-tagged-with-a-record-name-violates-declared-type" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#matching-of-pattern-tagged-with-a-record-name-violates-declared-type" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you&rsquo;ve added type specs to a record, and then attempt to pattern match fields of that record in ways that are inconsistent with the specs, Dialyzer can warn you that you&rsquo;ve violated the rules you set for the record.</p>
<blockquote><p>Matching of pattern {&lsquo;person&rsquo;, _, &lsquo;whatever&rsquo;} tagged with a record name violates the declared type of #person{name::&lsquo;undefined&rsquo; | string(), age::&lsquo;adult&rsquo; | &lsquo;kid&rsquo; | &lsquo;undefined&rsquo;}</p>
</blockquote><p>Assume you&rsquo;ve got a <code>person</code> record, where <code>age</code> can only be two values - <code>kid</code> or <code>adult</code>. If you write a function that tries to pattern match for a value of <code>age</code> that&rsquo;s not one of those two - in this case <code>whatever</code> - Dialyzer will warn you of the violation.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">record</span><span class="p">(</span><span class="nl">person</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">name</span> <span class="p">::</span> <span class="n">string</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">    <span class="n">age</span> <span class="p">::</span> <span class="n">kid</span> <span class="p">|</span> <span class="n">adult</span>
</span></span><span class="line"><span class="cl"><span class="p">}).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">get_age</span><span class="p">(</span><span class="nl">#person</span><span class="p">{})</span> <span class="o">-&gt;</span> <span class="n">atom</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_age</span><span class="p">(</span><span class="nl">#person</span><span class="p">{</span><span class="n">age</span> <span class="o">=</span> <span class="n">whatever</span><span class="p">})</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="n">none</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_age</span><span class="p">(</span><span class="nl">#person</span><span class="p">{</span><span class="n">age</span> <span class="o">=</span> <span class="nv">Age</span><span class="p">})</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Age</span><span class="p">.</span></span></span></code></pre></div></div>
<hr>

<h2 class="relative group">Function Has No Local Return
    <div id="function-has-no-local-return" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#function-has-no-local-return" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>This one can be tricky, and sometimes shows up as (or with) different errors <em>(such as the next one, record construction xyz violates the declared type of field)</em>.</p>
<blockquote><p>Function some_function/2 has no local return</p>
</blockquote><p>Here&rsquo;s a block of code to demonstrate the problem. The <code>sec_level</code> field is a number, but the <code>get_level</code> function returns a string. Knowing what the problem and solution is, I&rsquo;ve gotta say this particular error message is, well&hellip; pretty much crap. I&rsquo;m sure there&rsquo;s some sense to it, but it seems like there&rsquo;s gotta be a better way to say it.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">record</span><span class="p">(</span><span class="nl">employee</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">title</span> <span class="p">::</span> <span class="n">string</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">    <span class="n">sec_level</span> <span class="p">::</span> <span class="n">non_neg_integer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">create_employee</span><span class="p">(</span><span class="n">string</span><span class="p">(),</span> <span class="n">non_neg_integer</span><span class="p">())</span> <span class="o">-&gt;</span> <span class="nl">#employee</span><span class="p">{}.</span>
</span></span><span class="line"><span class="cl"><span class="nf">create_employee</span><span class="p">(</span><span class="nv">Title</span><span class="p">,</span> <span class="nv">SecurityLevel</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nl">#employee</span><span class="p">{</span><span class="n">title</span> <span class="o">=</span> <span class="nv">Title</span><span class="p">,</span> <span class="n">sec_level</span> <span class="o">=</span> <span class="n">get_level</span><span class="p">(</span><span class="nv">Title</span><span class="p">,</span> <span class="nv">SecurityLevel</span><span class="p">)}.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">get_level</span><span class="p">(</span><span class="n">string</span><span class="p">(),</span> <span class="n">string</span><span class="p">())</span> <span class="o">-&gt;</span> <span class="n">string</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_level</span><span class="p">(</span><span class="s">&#34;CEO&#34;</span><span class="p">,</span> <span class="p">_)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;10&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_level</span><span class="p">(</span><span class="s">&#34;Minion&#34;</span><span class="p">,</span> <span class="p">_)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;0&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_level</span><span class="p">(_</span><span class="nv">Title</span><span class="p">,</span> <span class="nv">Security</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Security</span><span class="p">.</span></span></span></code></pre></div></div>
<hr>

<h2 class="relative group">Record Construction Violates the Declared Type of Field
    <div id="record-construction-violates-the-declared-type-of-field" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#record-construction-violates-the-declared-type-of-field" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>You might see this Dialyzer error if you try to store a value type in a field that&rsquo;s not the type you said it should be, like storing a number in a string or vice-versa.</p>
<blockquote><p>Record construction #employee{title::string(), sec_level::string()} violates the declared type of field sec_level::&lsquo;undefined&rsquo; | non_neg_integer()</p>
</blockquote><p>This is the same example used above, because this snippet will produce two warnings. Where &ldquo;function has no local return&rdquo; is just about useless, this error says exactly what the problem is.</p>
<p>Always check to make sure you&rsquo;re storing the right value types&hellip; in this case, storing a string in a field marked as a non-negative integer isn&rsquo;t gonna work, and Dialyzer knows it.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">record</span><span class="p">(</span><span class="nl">employee</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">title</span> <span class="p">::</span> <span class="n">string</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">    <span class="n">sec_level</span> <span class="p">::</span> <span class="n">non_neg_integer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">create_employee</span><span class="p">(</span><span class="n">string</span><span class="p">(),</span> <span class="n">non_neg_integer</span><span class="p">())</span> <span class="o">-&gt;</span> <span class="nl">#employee</span><span class="p">{}.</span>
</span></span><span class="line"><span class="cl"><span class="nf">create_employee</span><span class="p">(</span><span class="nv">Title</span><span class="p">,</span> <span class="nv">SecurityLevel</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nl">#employee</span><span class="p">{</span><span class="n">title</span> <span class="o">=</span> <span class="nv">Title</span><span class="p">,</span> <span class="n">sec_level</span> <span class="o">=</span> <span class="n">get_level</span><span class="p">(</span><span class="nv">Title</span><span class="p">,</span> <span class="nv">SecurityLevel</span><span class="p">)}.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">get_level</span><span class="p">(</span><span class="n">string</span><span class="p">(),</span> <span class="n">string</span><span class="p">())</span> <span class="o">-&gt;</span> <span class="n">string</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_level</span><span class="p">(</span><span class="s">&#34;CEO&#34;</span><span class="p">,</span> <span class="p">_)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;10&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_level</span><span class="p">(</span><span class="s">&#34;Minion&#34;</span><span class="p">,</span> <span class="p">_)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;0&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_level</span><span class="p">(_</span><span class="nv">Title</span><span class="p">,</span> <span class="nv">Security</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Security</span><span class="p">.</span></span></span></code></pre></div></div>
<hr>

<h2 class="relative group">The Call Breaks the Contract
    <div id="the-call-breaks-the-contract" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-call-breaks-the-contract" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I&rsquo;m not exactly sure when this particular warning shows up, but I got it when trying to call a function in a third party library called <a href="https://github.com/Vagabond/gen_smtp/blob/master/src/mimemail.erl"  target="_blank" rel="noreferrer">MimeMail</a>.</p>
<blockquote><p>The call mimemail:encode({[101 | 116 | 120,&hellip;],[104 | 108 | 109 | 116,&hellip;],[{&laquo;<em>:16,</em>:<em>*8&raquo;,binary()},&hellip;],[],</em>}) breaks the contract (MimeMail::mimetuple()) -&gt; binary()</p>
</blockquote><p>If you&rsquo;re lucky, the third party libraries you use took the time to do specs too. You get even more protection against runtime errors, but sometimes it means you&rsquo;ll have to dig into the source code of other libraries to figure out what Dialyzer is complaining about.</p>
<p>I didn&rsquo;t save the code that caused this one, so I couldn&rsquo;t recreate an example, but I&rsquo;m sure I&rsquo;ll come across it again sooner or later&hellip;</p>
<hr>

<h2 class="relative group">The call module:function will never return since it differs from the success typing arguments
    <div id="the-call-modulefunction-will-never-return-since-it-differs-from-the-success-typing-arguments" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-call-modulefunction-will-never-return-since-it-differs-from-the-success-typing-arguments" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>These warning messages are starting to all sound somewhat similar and to blend together. Once again, here&rsquo;s a warning message that&rsquo;s not particularly helpful, but it led to finding a problem that absolutely would&rsquo;ve thrown an exception at runtime.</p>
<blockquote><p>The call lists:map(fun((<em>) -&gt; nonempty_maybe_improper_list()), Groups::&lsquo;undefined&rsquo;) will never return since it differs in the 2nd argument from the success typing arguments: (fun((</em>) -&gt; any()), [any()])</p>
</blockquote><p>Make sure you&rsquo;re not iterating over a variable as if it&rsquo;ll definitely be a list, unless you&rsquo;re positive it absolutely will be&hellip; or can handle it accordingly.</p>
<p>Here&rsquo;s a short code snippet to demonstrate the problem, although the one I found in a production system was buried in a half-dozen or so nested functions and took a full day to find. The <code>modify_groups</code> function loops over the groups to create a new collection, but the <code>create_employee</code> function pattern matches on <code>undefined</code>&hellip; which means that <code>lists:map</code> is guaranteed to try iterating over an undefined value and will throw an exception. After adding specs to a half-dozen modules and records, Dialyzer caught two of these situations, which made me happy.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">record</span><span class="p">(</span><span class="nl">employee</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">title</span> <span class="p">::</span> <span class="n">string</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">    <span class="n">groups</span> <span class="p">::</span> <span class="p">[</span><span class="n">string</span><span class="p">()]</span> <span class="p">|</span> <span class="n">undefined</span>
</span></span><span class="line"><span class="cl"><span class="p">}).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">create_employee</span><span class="p">(</span><span class="nl">#employee</span><span class="p">{})</span> <span class="o">-&gt;</span> <span class="nl">#employee</span><span class="p">{}.</span>
</span></span><span class="line"><span class="cl"><span class="nf">create_employee</span><span class="p">(</span><span class="nl">#employee</span><span class="p">{</span><span class="n">title</span> <span class="o">=</span> <span class="nv">Title</span><span class="p">,</span> <span class="n">groups</span> <span class="o">=</span> <span class="n">undefined</span><span class="p">}</span> <span class="o">=</span> <span class="nv">E</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nl">#employee</span><span class="p">{</span><span class="n">groups</span> <span class="o">=</span> <span class="n">modify_groups</span><span class="p">(</span><span class="nv">Title</span><span class="p">,</span> <span class="nv">E</span><span class="nl">#employee.groups</span><span class="p">)}.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">modify_groups</span><span class="p">(</span><span class="n">string</span><span class="p">(),</span> <span class="p">[</span><span class="n">string</span><span class="p">()])</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="n">string</span><span class="p">()].</span>
</span></span><span class="line"><span class="cl"><span class="nf">modify_groups</span><span class="p">(</span><span class="nv">Title</span><span class="p">,</span> <span class="nv">Groups</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nn">lists</span><span class="p">:</span><span class="nf">map</span><span class="p">(</span><span class="k">fun</span><span class="p">(</span><span class="nv">Group</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nv">Title</span> <span class="o">++</span> <span class="s">&#34; &#34;</span> <span class="o">++</span> <span class="nv">Group</span> <span class="k">end</span><span class="p">,</span> <span class="nv">Groups</span><span class="p">).</span></span></span></code></pre></div></div>
]]></content:encoded><media:content url="https://grantwinney.com/common-dialyzer-errors-and-solutions-in-erlang/feature.webp" medium="image" type="image/webp"/></item><item><title>5 Markdown Tricks for GitHub</title><link>https://grantwinney.com/cool-markdown-tricks-for-github/</link><pubDate>Wed, 06 Feb 2019 11:54:00 +0000</pubDate><guid>https://grantwinney.com/cool-markdown-tricks-for-github/</guid><description>Here&amp;rsquo;s a few tricks for rendering markdown in GitHub that most people wouldn&amp;rsquo;t know about. Oh, and they work for new Issues, Pull Requests, and in the Wiki too!</description><content:encoded><![CDATA[<p>If you frequently use GitHub, then you know any directory with a Readme markdown file in it automagically displays it, making it a convenient place to let visitors know helpful information about a project&hellip; about setting it up, how to contact the author, where to turn for help, etc.</p>
<p>But there are some little tricks you can take advantage of too, which most people wouldn&rsquo;t know about. Here&rsquo;s my top 5. These tricks work in any markdown file, including new Issues, Pull Requests, and in the Wiki.</p>
<hr>

<h2 class="relative group">Create Reusable Links
    <div id="create-reusable-links" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#create-reusable-links" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The normal way to create a link using markdown is this:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">[This is your link text](http://example.com/)</code></pre></div>
<p>But what if you have a long Readme file or wiki page, and the same link is used in multiple places?</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">If you take [issue](http://example.com/) with any [issues](http://example.com/)
I [issued](http://example.com/), grab a tissue.</code></pre></div>
<p>To make updates easier (not to mention, keeping things <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself"  target="_blank" rel="noreferrer">DRY</a>), you can create a list of links at the bottom of the file, and reference them in multiple places by name. The list won&rsquo;t render on the page, so visitors won&rsquo;t even know it&rsquo;s there, and it makes one convenient place to do updates.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">If you take [issue][issues] with any [issues][issues]
I [issued][issues], grab a tissue.

If you find a bug, please report an [issue][issues], or better yet,
fix it and submit a [pull request][pulls].

  [issues]:    https://github.com/grantwinney/BlogCodeSamples/issues
  [pulls]:     https://github.com/grantwinney/BlogCodeSamples/pulls</code></pre></div>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/cool-markdown-tricks-for-github/image-1.png"
    width="822"
      height="161"></figure>
<p>You can use the same technique with images too!</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">[example]: https://user-images.githubusercontent.com/image-4.png &#34;example image&#34;

![example]

lorem ipsum whatever lorem ipsum whatever lorem ipsum whatever 

![example]</code></pre></div>
<hr>

<h2 class="relative group">Add Hidden Comments
    <div id="add-hidden-comments" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#add-hidden-comments" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you want to add a comment to your markdown file on GitHub - something to note but that shouldn&rsquo;t render when the page is viewed - here&rsquo;s a little hack that takes advantage of the same &ldquo;<a href="https://daringfireball.net/projects/markdown/syntax#link"  target="_blank" rel="noreferrer">link</a>&rdquo; syntax used in the previous example. <em>(The double-slash is the link id, the hash is the URL, and the comment in parenthesis is the link title.)</em></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">[//]: # (This comment won&#39;t be rendered to the visitor!)</span></span></code></pre></div></div>
<p>You can add these to anything that accepts a link label, wherever you find them useful - maybe in a <a href="https://help.github.com/articles/creating-a-pull-request-template-for-your-repository/"  target="_blank" rel="noreferrer">Pull Request template</a> to give contributors instructions that won&rsquo;t render when the PR is submitted, or near a confusing part of a wiki page so the next person who tries to edit it sees a brief explanation before submitting their change.</p>
<hr>

<h2 class="relative group">Quickly Insert Images
    <div id="quickly-insert-images" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#quickly-insert-images" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Although the Wiki has a button that lets you upload images to it, and the Issues page lets you drag and drop images, the interface in the main repo has no such button. You can (ab)use the Issues page though, to avoid the pain of having to upload images into your repo&hellip; which keeps the size of your repo down too.</p>
<p>Just create a new issue and drag your image into the editor pane. It&rsquo;ll upload it and generate a unique URL for you. Don&rsquo;t even bother saving the issue&hellip; just copy the markdown it generates and drop it into your Readme.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/cool-markdown-tricks-for-github/issue-example-1.png"
    width="967"
      height="515"></figure>
<p>The only caveat is that it&rsquo;s not under source control, but I can&rsquo;t really see that being an issue. I&rsquo;ve never had a need to keep revisions of images, but if you do then this may not be the tip for you.</p>
<hr>

<h2 class="relative group">Resize Images
    <div id="resize-images" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#resize-images" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>But what if you get your image inserted and it&rsquo;s obnoxiously huge? You can&rsquo;t resize an image using markdown.</p>
<p>Well, GitHub doesn&rsquo;t support <em>all</em> HTML tags - for example the <code>style</code> tag - but it <em>does</em> support a subset. You can <a href="https://github.com/gjtorikian/html-pipeline/blob/main/lib/html_pipeline/sanitization_filter.rb"  target="_blank" rel="noreferrer">check out their filter</a> for yourself, but here&rsquo;s the list of tags they support:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">h1 h2 h3 h4 h5 h6 h7 h8 br b i strong em a pre code img tt div ins del
</span></span><span class="line"><span class="cl">sup sub p ol ul table thead tbody tfoot blockquote dl dt dd kbd q samp
</span></span><span class="line"><span class="cl">var hr ruby rt rp li tr td th s strike summary details caption figure
</span></span><span class="line"><span class="cl">figcaption abbr bdo cite dfn mark small span time wbr</span></span></code></pre></div></div>
<p>The <code>img</code> tag is in the list, so just switch to standard HTML to resize it. It even supports other attributes, allowing things like word wrapping.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://some-img-host.com/1234567/image.png&#34;</span> <span class="na">width</span><span class="o">=</span><span class="s">300</span> <span class="na">align</span><span class="o">=</span><span class="s">right</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<hr>

<h2 class="relative group">Add Some Color to Your Life
    <div id="add-some-color-to-your-life" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#add-some-color-to-your-life" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>You can&rsquo;t color your text using markdown, but you <em>can</em> use an image placeholder service like placeholder.com <em>(<a href="https://www.reddit.com/r/web_design/comments/10lqgja/placeholdercom_is_no_more/"  target="_blank" rel="noreferrer">now defunct</a>, although there&rsquo;s other similar services)</em> to create some useful effects that make sections of your Readme file, etc stand out.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl"><span class="gu">## COLOR!
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">-</span> ![<span class="nt">#ff0000</span>](<span class="na">https://placehold.it/12/ff0000?text=+</span>) red!
</span></span><span class="line"><span class="cl"><span class="k">-</span> ![<span class="nt">#9900c5</span>](<span class="na">https://placehold.it/15/9900c5?text=+</span>) purple!
</span></span><span class="line"><span class="cl"><span class="k">-</span> ![<span class="nt">#157500</span>](<span class="na">https://placehold.it/20/157500?text=+</span>) green!
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">![](https://placehold.it/400x90/ff0000/000000?text=IMPORTANT!)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">![](https://placehold.it/400x90/ff6600/000?text=WARNING!)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">![](https://placehold.it/350x90/009955/fff?text=SUCCESS!)</span></span></code></pre></div></div>
<p>The above markdown is rendered like this:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/cool-markdown-tricks-for-github/markdown-colors.png"
    width="531"
      height="615"></figure>
<hr>
<p><strong>What else?</strong></p>
<p>I was hoping to find some trick for generating a table of contents, but alas after <a href="https://github.com/isaacs/github/issues/215"  target="_blank" rel="noreferrer">years of ongoing discussion</a>&hellip; nada. There are other solutions like <a href="https://github.com/ekalinin/github-markdown-toc"  target="_blank" rel="noreferrer">github-markdown-toc</a> and the <a href="https://chrome.google.com/webstore/detail/github-markdown-outline-e/gccinjjdbfdkkkebfbeipopijjfohfgj"  target="_blank" rel="noreferrer">Github Markdown Outline</a> chrome extension, but nothing native. Who knows though&hellip; maybe since <a href="https://itsfoss.com/microsoft-github/"  target="_blank" rel="noreferrer">Microsoft bought GitHub</a> and is <a href="https://web.archive.org/web/20230203202759/https://dzone.com/articles/github-roadmap-better-for-everyone"  target="_blank" rel="noreferrer">actively adding features</a>, we&rsquo;ll see more features built-in.</p>
<p>If you find any cool tricks of your own, I&rsquo;d love to know about them! Share below&hellip;</p>
]]></content:encoded><media:content url="https://grantwinney.com/cool-markdown-tricks-for-github/feature.webp" medium="image" type="image/webp"/></item><item><title>Hosting a GitHub wiki on Ubuntu (and keeping it in sync)</title><link>https://grantwinney.com/hosting-a-github-wiki-remotely-on-ubuntu/</link><pubDate>Mon, 29 Oct 2018 12:19:38 +0000</pubDate><guid>https://grantwinney.com/hosting-a-github-wiki-remotely-on-ubuntu/</guid><description>I&amp;rsquo;ve always been a fan of wikis, but GitHub&amp;rsquo;s is so poorly designed it doesn&amp;rsquo;t get much love. I once wrote about cloning a wiki locally and editing it using Gollum, but now I&amp;rsquo;m taking a look at hosting it externally on DigitalOcean, using Gollum and keeping it in sync with the repo hosted on GitHub.</description><content:encoded><![CDATA[<p>I&rsquo;ve always been a fan of wikis for documentation and record-keeping. I even keep an instance of <a href="https://grantwinney.com/creating-your-own-secure-wiki-using-dokuwiki/"  target="_blank" rel="noreferrer">Dokuwiki running on DigitalOcean</a> for personal notes, and before that I had Confluence running on a spare machine at home. GitHub uses wikis too, creating one for every project you spin up. Unfortunately it&rsquo;s so poorly designed that I don&rsquo;t think it gets much love. There&rsquo;s no built-in search, no file upload, no table of contents&hellip; it <em>could</em> be a whole lot more.</p>
<p>Awhile back, I wrote about how <a href="https://grantwinney.com/5-things-you-can-do-with-a-locally-cloned-github-wiki/"  target="_blank" rel="noreferrer">GitHub wikis are separate repos</a> you can clone and edit using <a href="https://github.com/gollum/gollum"  target="_blank" rel="noreferrer">Gollum</a>, which gives you a much richer interface, but that was about running locally and assumed no one else would be editing the wiki online. What if we could host a clone of the wiki externally - like on a DigitalOcean vm - using the full capabilities of Gollum to edit it, and keep it in sync with the repo hosted on GitHub? We can.. but first&hellip;</p>

<h2 class="relative group">What this isn&rsquo;t
    <div id="what-this-isnt" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-this-isnt" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>This isn&rsquo;t a production-ready solution. If you follow this through, you&rsquo;ll have a publicly available clone of your GitHub wiki, which can sync with GitHub in both directions (most of the time). But there are security considerations and other caveats, which I listed at the end. Every problem has a solution though&hellip; maybe you&rsquo;ll be the one to find the answer. :)</p>
<p>Now let&rsquo;s see what we can do!</p>
<hr>

<h2 class="relative group">Spin up a DigitalOcean server
    <div id="spin-up-a-digitalocean-server" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#spin-up-a-digitalocean-server" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://m.do.co/c/448f25462030"  target="_blank" rel="noreferrer">Sign up for a DigitalOcean account</a> if you don&rsquo;t already have one, or login to your existing account, and create a new Ubuntu box. You can spin up a vm wherever you&rsquo;d like, but I use DO for this blog and playing around with new ideas, and I&rsquo;d recommend them to anyone - they&rsquo;ve got an awesome service. For this little exercise, a minimal $5/mo box will work just fine, and you can always increase it later if you&rsquo;d like.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/hosting-a-github-wiki-remotely-on-ubuntu/image.png"
    width="1266"
      height="963"></figure>
<p>It takes a minute or two to create it, and then you&rsquo;ll get an email telling you how to login as root. The first thing I&rsquo;d suggest doing after you login and change your root password is to <a href="https://www.digitalocean.com/community/tutorials/how-to-create-a-sudo-user-on-ubuntu-quickstart"  target="_blank" rel="noreferrer">create a new user with sudo access</a>, and then do everything as <em>that</em> user instead.</p>
<p>Note that there&rsquo;s also an option part way down the page to &ldquo;<a href="https://www.digitalocean.com/docs/droplets/how-to/add-ssh-keys/"  target="_blank" rel="noreferrer">Add your SSH keys</a>&rdquo;, which is worth checking out, but which I&rsquo;m not going to cover here. Once it&rsquo;s setup on your machine, you don&rsquo;t have to remember your password anymore.</p>
<hr>

<h2 class="relative group">Install Gollum (and everything else)
    <div id="install-gollum-and-everything-else" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#install-gollum-and-everything-else" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The first thing we&rsquo;ll do is <a href="https://github.com/gollum/gollum/wiki/Installation"  target="_blank" rel="noreferrer">install Gollum</a>, as well as ruby, git, and a few other tools. The <em>&ldquo;Installing ri documentation for &hellip;&rdquo;</em> lines might seem to get stuck, but just wait 5 or 10 minutes and they should complete.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">sudo apt-get update
</span></span><span class="line"><span class="cl">sudo apt-get install ruby ruby-dev make zlib1g-dev libicu-dev build-essential git cmake
</span></span><span class="line"><span class="cl">sudo gem install gollum</span></span></code></pre></div></div>
<p>Eventually, you should see something like this:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">Done installing documentation <span class="k">for</span> charlock_holmes, posix-spawn, mime-types-data, mime-types, diff-lcs, gitlab-grit, gollum-grit_adapter, rouge, mini_portile2, nokogiri, stringex, sanitize, github-markup, gemojione, unf_ext, unf, twitter-text, gollum-lib, kramdown, rack, tilt, rack-protection, sinatra, mustache, useragent, gollum after <span class="m">601</span> seconds
</span></span><span class="line"><span class="cl"><span class="m">26</span> gems installed</span></span></code></pre></div></div>
<hr>

<h2 class="relative group">Clone the wiki repo
    <div id="clone-the-wiki-repo" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#clone-the-wiki-repo" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>We just installed git, so we can clone the repo onto the vm. First though, <a href="https://help.github.com/articles/connecting-to-github-with-ssh/"  target="_blank" rel="noreferrer">setup SSH access to GitHub</a>. Without it, you&rsquo;ll either have to enter your login and password for GitHub every time you push something (which won&rsquo;t work for what we&rsquo;re trying to do), or save your login and password to disk (which isn&rsquo;t very secure).</p>
<p>Start by <a href="https://help.github.com/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent/#platform-linux"  target="_blank" rel="noreferrer">generating a new SSH key</a>, and then <a href="https://help.github.com/articles/adding-a-new-ssh-key-to-your-github-account/#platform-linux"  target="_blank" rel="noreferrer">add the key to your GitHub account</a>. Just follow the instructions they&rsquo;ve laid out - accepting defaults where possible is fine. The only difference is step 1 for adding the key to your account - you&rsquo;re on a vm so copying to your clipboard won&rsquo;t be much help. Type <code>more ~/.ssh/id_rsa.pub</code> to output the key to the console so you can manually copy it over to GitHub instead.</p>
<p>After you&rsquo;ve <a href="https://help.github.com/articles/testing-your-ssh-connection/#platform-linux"  target="_blank" rel="noreferrer">confirmed that the key works</a>, clone the wiki repo into your home directory. You can find the link near the bottom of the wiki, in the lower-right corner (unlike the main project, which lists the &ldquo;clone&rdquo; link right at the top).</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/hosting-a-github-wiki-remotely-on-ubuntu/github-clone-wiki-link.png"
    width="546"
      height="240"></figure>
<p>You&rsquo;ll want to change the link to an SSH format, which they provide for you on a repo but (unhelpfully) not on a wiki repo. That&rsquo;s okay, just manually change it.</p>
<ul>
<li>from <strong>https</strong>: <code>https://github.com/your-account/your-project.wiki.git</code></li>
<li>to <strong>ssh</strong>: <code>git clone git@github.com:your-account/your-project.wiki.git</code></li>
</ul>
<p>While you&rsquo;re here, provide an email and name for <code>git</code> to associate with commits:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">  git config --global user.email <span class="s2">&#34;you@example.com&#34;</span>
</span></span><span class="line"><span class="cl">  git config --global user.name <span class="s2">&#34;Your Name&#34;</span></span></span></code></pre></div></div>
<p>Switch to the &ldquo;your-repo.wiki&rdquo; directory and type &ldquo;gollum&rdquo; to fire up the Gollum server, which uses <a href="http://www.sinatrarb.com/"  target="_blank" rel="noreferrer">Sinatra</a> running <a href="https://ruby-doc.org/stdlib-2.0.0/libdoc/webrick/rdoc/WEBrick.html"  target="_blank" rel="noreferrer">WEBrick</a> to serve up your wiki pages in a browser. If it starts okay, it should show you something like this:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">[2018-10-22 13:16:31] INFO  WEBrick 1.3.1
[2018-10-22 13:16:31] INFO  ruby 2.3.1 (2016-04-26) [x86_64-linux-gnu]
== Sinatra (v1.4.8) has taken the stage on 4567 for development with backup from WEBrick
[2018-10-22 13:16:31] INFO  WEBrick::HTTPServer#start: pid=9445 port=4567</code></pre></div>
<p>Open your browser to <code>http://your-ip-address:4567</code> (replacing the IP address with whatever is in your DigitalOcean dashboard). You should see the cloned wiki being displayed by Gollum. As I mentioned earlier, be aware that this is very insecure - anyone with access to the URL can edit your wiki!</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/hosting-a-github-wiki-remotely-on-ubuntu/gollum-showing-wiki.png"
    width="1914"
      height="694"></figure>
<p>Edit a page in Gollum, and it&rsquo;ll automatically commit your changes. Unfortunately, it doesn&rsquo;t <em>push</em> your changes automatically, so that they&rsquo;re reflected on GitHub right away, but we&rsquo;ll fix that soon. Hit ctrl-c to exit Gollum for now.</p>
<hr>

<h2 class="relative group">Send wiki edits to GitHub
    <div id="send-wiki-edits-to-github" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#send-wiki-edits-to-github" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Gollum commits wiki edits to the locally cloned repo, but it doesn&rsquo;t push them. Normally an executable file in <code>.git/hooks</code> with <code>git push origin master</code> in it would be enough, but for some reason Gollum doesn&rsquo;t trigger the hooks. I&rsquo;m not sure why that is, but <a href="https://github.com/gollum/gollum-lib/issues/12"  target="_blank" rel="noreferrer">there&rsquo;s a fix</a>.</p>
<p>Create a file in your home directory called <code>gollum_config.rb</code> and add the following to it. After Gollum does a commit, it&rsquo;ll pull in changes from anyone else and then push your changes out.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">wiki</span> <span class="o">=</span> <span class="no">Gollum</span><span class="o">::</span><span class="no">Wiki</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">&#34;.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># on commit, pull latest from repo and push changes to repo</span>
</span></span><span class="line"><span class="cl"><span class="no">Gollum</span><span class="o">::</span><span class="no">Hook</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="ss">:post_commit</span><span class="p">,</span> <span class="ss">:hook_id</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">committer</span><span class="p">,</span> <span class="n">sha1</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="n">wiki</span><span class="o">.</span><span class="n">repo</span><span class="o">.</span><span class="n">git</span><span class="o">.</span><span class="n">pull</span><span class="p">(</span><span class="s2">&#34;origin&#34;</span><span class="p">,</span> <span class="s2">&#34;master&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">wiki</span><span class="o">.</span><span class="n">repo</span><span class="o">.</span><span class="n">git</span><span class="o">.</span><span class="n">push</span><span class="p">(</span><span class="s2">&#34;origin&#34;</span><span class="p">,</span> <span class="s2">&#34;master&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div>
<p>Switch back to your repo and start Gollum again, this time <a href="https://github.com/gollum/gollum#configuration"  target="_blank" rel="noreferrer">passing it your config file</a>:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">gollum --config ../gollum_config.rb</span></span></code></pre></div></div>
<p>Now make a change in your wiki and save it. It&rsquo;ll pause for a moment as it runs the code in the hook, and then you&rsquo;ll see the changes on GitHub!</p>
<hr>

<h2 class="relative group">Get wiki edits from GitHub
    <div id="get-wiki-edits-from-github" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#get-wiki-edits-from-github" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>We can make changes to the wiki in the vm and they get pushed to GitHub, but what happens when someone makes a change on GitHub? How do we et those updates? GitHub can notify you when nearly anything happens concerning your repo, by letting you specify a URL it should send notifications to (a webhook). One of these events is called the <a href="https://developer.github.com/v3/activity/events/types/#gollumevent"  target="_blank" rel="noreferrer">GollumEvent</a>, which is <em>&ldquo;triggered when a Wiki page is created or updated&rdquo;</em> and that&rsquo;s the one we need.</p>

<h3 class="relative group">Configure a webhook to send notifications
    <div id="configure-a-webhook-to-send-notifications" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#configure-a-webhook-to-send-notifications" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>On GitHub, find the Settings tab for your repository, and configure it to send out notifications when your wiki is updated. <em>(I removed some available events from the screenshot below for readability - you&rsquo;ll see many more.)</em> The <code>/wiki_update</code> endpoint is what we&rsquo;ll create next, using Sinatra.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/hosting-a-github-wiki-remotely-on-ubuntu/github-webhook-setup.png"
    width="1596"
      height="1436"></figure>

<h3 class="relative group">Receive notifications with Sinatra
    <div id="receive-notifications-with-sinatra" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#receive-notifications-with-sinatra" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>We&rsquo;ll use the Sinatra web server <em>(check out the</em> <a href="http://sinatrarb.com/intro.html"  target="_blank" rel="noreferrer"><em>Getting Started</em></a> <em>guide)</em> to receive notifications and take some action. Create a file in your home directory named &ldquo;receive_updates.rb&rdquo; and add the following code. Whenever a <code>POST</code> is made to the <code>/wiki_update</code> endpoint, it&rsquo;ll pull down the latest changes for your wiki&rsquo;s repo.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;sinatra&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># bind to the public IP address of your vm</span>
</span></span><span class="line"><span class="cl"><span class="n">set</span> <span class="ss">:bind</span><span class="p">,</span> <span class="s1">&#39;142.93.189.174&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">set</span> <span class="ss">:port</span><span class="p">,</span> <span class="mi">5678</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">get</span> <span class="s1">&#39;/&#39;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="s1">&#39;Service Running!&#39;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">post</span> <span class="s1">&#39;/wiki_update&#39;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="sb">`cd BlogCodeSamples.wiki; git pull`</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div>
<p>You have to <a href="http://sinatrarb.com/configuration.html#bind---server-hostname-or-ip-address"  target="_blank" rel="noreferrer">bind to the IP address</a> of your vm (instead of the default 127.0.0.1) and change the default port number (because Gollum already binds to 4567). I considered trying to pull only the file that was updated, but the relative file path is not included in the <a href="https://developer.github.com/v3/activity/events/types/#gollumevent"  target="_blank" rel="noreferrer">payload</a> (though you could probably hack the <code>html_url</code> value) and doing a <code>git pull</code> seems to work just fine.</p>
<p>Start your Sinatra server by running the file:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">ruby ~/receive_updates.rb</span></span></code></pre></div></div>
<p>You should see something like this, if all goes well:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">[2018-10-21 12:38:50] INFO  WEBrick 1.3.1
[2018-10-21 12:38:50] INFO  ruby 2.3.1 (2016-04-26) [x86_64-linux-gnu]
== Sinatra (v2.0.4) has taken the stage on 5678 for development with backup from WEBrick
[2018-10-21 12:38:50] INFO  WEBrick::HTTPServer#start: pid=12286 port=5678</code></pre></div>

<h3 class="relative group">Create a page and see the notifications
    <div id="create-a-page-and-see-the-notifications" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#create-a-page-and-see-the-notifications" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>If you open two terminal windows, and start Gollum in one and your &ldquo;receive_updates&rdquo; app in the other, you should be able to make updates on the GitHub side and see them in your vm, and vice-versa. <a href="/hosting-a-github-wiki-remotely-on-ubuntu/remote_wiki_test1.gif" >Click here to see a short demo of what it should look like</a>. <em>(caution, the file is about 4mb)</em></p>
<hr>

<h2 class="relative group">Automating the services
    <div id="automating-the-services" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#automating-the-services" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Now we can go both ways, but we have two services to startup, and we&rsquo;re doing it manually. Let&rsquo;s automate everything to start at system bootup. Apparently there are <a href="https://askubuntu.com/q/814/322373"  target="_blank" rel="noreferrer">quite a few ways</a> to do this, but we&rsquo;ll use the <code>crontab</code> way. Create a script in your home directory called <code>start_gollum.sh</code> and make it executable with <code>chmod +x start_gollum.sh</code>. Open it and add the following contents, which run both services in the background (otherwise the first one you run blocks the process, and the other won&rsquo;t start).</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">ruby /home/grant/receive_updates.rb <span class="p">&amp;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> /home/grant/BlogCodeSamples.wiki
</span></span><span class="line"><span class="cl">/usr/local/bin/gollum --config /home/grant/gollum_config.rb <span class="p">&amp;</span></span></span></code></pre></div></div>
<p>Then run <code>crontab -e</code> and at the very bottom of the file add the following line, which will run the above script when the machine boots up. Change the path as needed of course.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">@reboot /home/grant/start_gollum.sh</span></span></code></pre></div></div>
<p>Now do a <code>sudo reboot now</code> and let the machine come back up. Do both services start as expected?</p>
<hr>

<h2 class="relative group">Security considerations
    <div id="security-considerations" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#security-considerations" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I&rsquo;ve mentioned it a couple times, but I&rsquo;ll say it again. This setup as I&rsquo;ve laid it out would be very insecure. Here&rsquo;s a few things you&rsquo;d want to think about:</p>
<ul>
<li><strong>Enable a firewall</strong> - Lock down open ports to only those needed (in this demo, it&rsquo;d be 4567, 5678 and 22 for ssh), either by <a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-a-firewall-with-ufw-on-ubuntu-16-04"  target="_blank" rel="noreferrer">configuring ufw</a> or DigitalOcean&rsquo;s <a href="https://www.digitalocean.com/docs/networking/firewalls/"  target="_blank" rel="noreferrer">cloud firewall</a>.</li>
<li><strong>Secure webhooks</strong> - It wouldn&rsquo;t hurt to <a href="https://developer.github.com/webhooks/securing/"  target="_blank" rel="noreferrer">secure your webhooks</a> using GitHub&rsquo;s &ldquo;secret token&rdquo; feature.</li>
<li><strong>Add SSL</strong> - Encrypt the connection with SSL, since by default everything is transmitted in plain text and is susceptible to <a href="https://en.wikipedia.org/wiki/Man-in-the-middle_attack"  target="_blank" rel="noreferrer">mitm attacks</a>.</li>
<li><strong>Authentication / password protection</strong> - Check out <a href="https://github.com/gollum/gollum/issues/107"  target="_blank" rel="noreferrer">this old issue in the Gollum repo</a> for more info and some helpful suggestions. There&rsquo;s even a comment (albeit years old now) from someone who seems to work at GitHub and was involved in their efforts to prevent unauthorized access to a repo&rsquo;s wiki.</li>
</ul>
<hr>

<h2 class="relative group">Caveats
    <div id="caveats" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#caveats" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>These are just the downsides / limitations that I noticed&hellip; I&rsquo;m sure there&rsquo;s more.</p>
<ul>
<li>A delete on the GitHub side doesn&rsquo;t transmit a notification, so it&rsquo;s not reflected in the vm until you make an edit elsewhere that triggers a <code>git pull</code>. But a delete in the vm <em>is</em> reflected on GitHub immediately, because it&rsquo;s pushing the change to the repo.</li>
<li>If someone clones the repo to their machine, then edits a file manually and pushes the change, GitHub kicks off a notification for that file and the vm pulls in the change. However, if someone just uploads an image or other file, without editing a page, no notification is sent.</li>
<li>If two people are editing a wiki page in GitHub, when one saves the other gets a warning that the page has been updated. This doesn&rsquo;t occur in Gollum, so it&rsquo;s possible to save your changes and blow away someone else&rsquo;s edit.</li>
</ul>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/hosting-a-github-wiki-remotely-on-ubuntu/someone-has-edited.png"
    width="1468"
      height="872"></figure>
<hr>

<h2 class="relative group">What next?
    <div id="what-next" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-next" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Since installing Gollum is pretty much all that&rsquo;s needed here, it&rsquo;d be interesting to try setting this up by running <a href="https://github.com/gollum/gollum/wiki/Gollum-via-Docker"  target="_blank" rel="noreferrer">Gollum in a Docker container</a>, perhaps using <a href="https://docs.digitalocean.com/products/marketplace/catalog/docker/"  target="_blank" rel="noreferrer">DigitalOcean&rsquo;s one-click Docker app</a>.</p>
<p>If you want to see what else you can do with Gollum, check out my post on <a href="https://grantwinney.com/5-things-you-can-do-with-a-locally-cloned-github-wiki/"  target="_blank" rel="noreferrer">5 Things You Can Do With a Locally Cloned GitHub Wiki</a>.</p>
]]></content:encoded><media:content url="https://grantwinney.com/hosting-a-github-wiki-remotely-on-ubuntu/feature.webp" medium="image" type="image/webp"/></item><item><title>A few thoughts on date/time handling in Erlang</title><link>https://grantwinney.com/a-few-thoughts-on-datetime-handling-in-erlang/</link><pubDate>Mon, 03 Sep 2018 14:48:51 +0000</pubDate><guid>https://grantwinney.com/a-few-thoughts-on-datetime-handling-in-erlang/</guid><description>Handling date and times is a thorn in every experienced developer&amp;rsquo;s side. If you haven&amp;rsquo;t had the pleasure yet, you will. ;) Coming off a week of standardizing some datetimes across an Erlang app, here&amp;rsquo;s a few personal thoughts.</description><content:encoded><![CDATA[<p>Ask any programmer who&rsquo;s been at it awhile what their biggest aggravations are, and I&rsquo;d bet handling dates and times is nearly always in the top 5. I&rsquo;m just getting off of a week or so of standardizing some date/time handling across an Erlang application, so here&rsquo;s a few thoughts while it&rsquo;s still fresh in my mind <em>(and then I don&rsquo;t want to think about time ever again)</em>.</p>

<h2 class="relative group">Decide how to represent time internally
    <div id="decide-how-to-represent-time-internally" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#decide-how-to-represent-time-internally" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Decide on what&rsquo;s best for your app and <em>stick to it</em>. Erlang has several ways to represent time, each with varying levels of precision. You can <a href="https://learnyousomeerlang.com/time"  target="_blank" rel="noreferrer">read more here</a>, but they include:</p>
<ul>
<li><a href="http://erlang.org/doc/man/calendar.html#universal_time-0"  target="_blank" rel="noreferrer">calendar:universal_time</a> - returns a tuple, max resolution of seconds</li>
<li><a href="https://erldocs.com/18.0/erts/erlang.html#timestamp/0"  target="_blank" rel="noreferrer">erlang:timestamp</a> - returns a <em>different</em> tuple, max resolution of microseconds since <a href="https://stackoverflow.com/a/1090945/301857"  target="_blank" rel="noreferrer">epoch</a></li>
<li><a href="https://erldocs.com/18.0/erts/erlang.html#system_time/0"  target="_blank" rel="noreferrer">erlang:system_time</a> - returns an integer, max res of nanoseconds since the <a href="https://stackoverflow.com/a/1090945/301857"  target="_blank" rel="noreferrer">epoch</a></li>
<li><a href="https://erldocs.com/18.0/erts/erlang.html#monotonic_time/0"  target="_blank" rel="noreferrer">erlang:monotonic_time</a> returns an ever-increasing integer, but not a &ldquo;time&rdquo; in the usual sense</li>
</ul>
<p>An HR app that stores hiring and termination dates, or a time clock app that tracks punch in / punch out times, might only need seconds precision. An app dealing with transactions that occur thousands of times a second may need a better resolution. But whatever you choose, you either end up with an integer, a tuple of integers, or a tuple of tuples of integers.</p>
<p>While <a href="https://learnyousomeerlang.com/dialyzer"  target="_blank" rel="noreferrer">dialyzer</a> and <a href="https://learnyousomeerlang.com/eunit"  target="_blank" rel="noreferrer">writing tests</a> can help, representing the same thing several different ways is at best aggravating&hellip; and at worst, leads to difficult to trace bugs. And if you use <code>erlang:system_time</code> at different resolutions, like nanoseconds and seconds for example, you&rsquo;ll end up with differently sized integers that mean completely different things - and dialyzer won&rsquo;t help at all.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/a-few-thoughts-on-datetime-handling-in-erlang/spec-gandalf.png"
    width="620"
      height="261"></figure>

<h2 class="relative group">Decide how to represent time externally
    <div id="decide-how-to-represent-time-externally" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#decide-how-to-represent-time-externally" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If your app has some sort of GUI for users, or an API providing access to data, you&rsquo;ll need a way to present dates and times that&rsquo;s easy to read. Displaying the number of nanoseconds since the epoch, or expecting values to be supplied to an API in that format, is hardly user-friendly. :)</p>
<p>One of the most consistent ways to deal with dates and times is the <a href="https://www.w3.org/TR/NOTE-datetime"  target="_blank" rel="noreferrer">ISO8601</a> standard, which defines a standardized way to display things that everyone can agree on. I&rsquo;m sure there are other standards besides ISO8601 too (there always are), but this one seems to have stuck.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Standards - https://xkcd.com/927"
    src="/a-few-thoughts-on-datetime-handling-in-erlang/xkcd-standards.webp"
    width="500"
      height="283"></figure>
<p>The <a href="https://github.com/erlsci/iso8601"  target="_blank" rel="noreferrer">iso8601 library</a> works nicely, but only formats times that are tuples, so even though ISO8601 technically supports nanoseconds this particular library does not. It can also parse ISO8601 values back to something Erlang can natively work with.</p>
<p>I&rsquo;d also suggest converting back and forth as soon as it makes sense in your app. In other words, don&rsquo;t pass ISO8601 values around at all levels of your app if what you really want to work with is an integer representing nanoseconds. Just convert the integer to ISO8601 right before you pass it to the user, and convert it to an integer again as they send it back to you.</p>

<h2 class="relative group">Store everything in GMT (UTC +0)
    <div id="store-everything-in-gmt-utc-0" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#store-everything-in-gmt-utc-0" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Another great source of hard-to-trace bugs (if that&rsquo;s your thing) is storing dates and times in a local timezone. Store everything in GMT <em>(</em><a href="https://www.timeanddate.com/time/gmt-utc-time.html"  target="_blank" rel="noreferrer"><em>not the same as UTC</em></a><em>!)</em> so you know exactly where your starting point is, and then convert values to a local timezone at the moment you need them - for a calculation, display purposes, or something else. While a datetime is stored and passed around your system, you really <em>really</em> want high confidence what format it&rsquo;s in so you&rsquo;re not making &ldquo;best guesses&rdquo; later on. And if a time is converted to a particular timezone and then stored as an integer, it&rsquo;ll be impossible to figure out what the original timezone was.</p>
<p>Like so many other things in Erlang, there&rsquo;s little to no native support for timezones - one of the many things I miss about the .NET ecosystem. There are several Erlang libraries out there to help working with timezones, but most of them appear abandoned. The best right now seems to be the <a href="https://github.com/choptastic/qdate"  target="_blank" rel="noreferrer">qdate</a> library, which in turn was (I think) based off the <a href="https://github.com/dmitryme/erlang_localtime"  target="_blank" rel="noreferrer">Erlang Localtime</a> library.</p>

<h2 class="relative group">Familiarize yourself with the native tools
    <div id="familiarize-yourself-with-the-native-tools" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#familiarize-yourself-with-the-native-tools" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If there&rsquo;s some native tools for manipulating dates and times in your language, get familiar with them before turning to third-party libraries. Sometimes they&rsquo;re not much-used or particularly well-known, and you&rsquo;ll only stumble on them by reading the docs. It&rsquo;s worth it.</p>
<p>Erlang provides a handful of functions in the <a href="http://erlang.org/doc/man/calendar.html"  target="_blank" rel="noreferrer">calendar module</a>, as well as in the <a href="http://erlang.org/doc/man/erlang.html"  target="_blank" rel="noreferrer">Erlang BIFs</a> like getting times in various formats and converting from local to universal time and back again. But I just stumbled on a little BIF called <a href="http://erlang.org/doc/man/erlang.html#convert_time_unit-3"  target="_blank" rel="noreferrer">convert_time_unit</a> that converts integer values between time units, like nanoseconds to seconds. Internally, it&rsquo;s probably just performing a <code>div</code> operation, but I find it to be more self-documenting. Just keep in mind that it always rounds down.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="c">% without bif
</span></span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">define</span><span class="p">(</span><span class="no">MILLISECONDS</span><span class="p">,</span> <span class="mi">1000</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="nv">Timeout</span> <span class="o">=</span> <span class="mi">2777</span> <span class="ow">div</span> <span class="o">?</span><span class="nv">MILLISECONDS</span><span class="p">.</span>       <span class="c">% 2
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">% using bif
</span></span></span><span class="line"><span class="cl"><span class="nv">Timeout</span> <span class="o">=</span> <span class="nn">erlang</span><span class="p">:</span><span class="nf">convert_time_unit</span><span class="p">(</span><span class="mi">2777</span><span class="p">,</span> <span class="n">millisecond</span><span class="p">,</span> <span class="n">second</span><span class="p">).</span>  <span class="c">% 2
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">% rounding
</span></span></span><span class="line"><span class="cl"><span class="nv">Timeout</span> <span class="o">=</span> <span class="nb">round</span><span class="p">(</span><span class="mi">2777</span> <span class="o">/</span> <span class="o">?</span><span class="nv">MILLISECONDS</span><span class="p">).</span>  <span class="err">%</span> <span class="mi">3</span></span></span></code></pre></div></div>

<h2 class="relative group">Read more about time&hellip; and weep
    <div id="read-more-about-time-and-weep" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#read-more-about-time-and-weep" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Does time have to be this tricky? I don&rsquo;t know, but right now it&rsquo;s a huge thorn in developers&rsquo; sides everywhere. Toss in timezones, daylight savings, fractions of seconds, etc and it only gets worse. Here are some fun articles that&rsquo;ll make even the most time-saavy among you realize&hellip; it can always get worse. ;)</p>
<ul>
<li><a href="https://infiniteundo.com/post/25326999628/falsehoods-programmers-believe-about-time"  target="_blank" rel="noreferrer">Falsehoods programmers believe about time: @noahsussman: Infinite Undo</a></li>
<li><a href="https://infiniteundo.com/post/25509354022/more-falsehoods-programmers-believe-about-time"  target="_blank" rel="noreferrer">More falsehoods programmers believe about time: @noahsussman: Infinite Undo</a></li>
<li><a href="https://www.youtube.com/watch?v=-5wpm-gesOY"  target="_blank" rel="noreferrer">The Problem with Time &amp; Timezones - Computerphile - YouTube</a>  <em>(if you happen to work with a framework that makes handling time and timezones easy, this last one&rsquo;ll have you thanking its authors!)</em></li>
</ul>
]]></content:encoded><media:content url="https://grantwinney.com/a-few-thoughts-on-datetime-handling-in-erlang/feature.webp" medium="image" type="image/webp"/></item><item><title>How to select an earlier .NET version on Visual Studio for Mac (tl;dr: you can't)</title><link>https://grantwinney.com/installing-earlier-net-versions-on-visual-studio-for-mac/</link><pubDate>Mon, 20 Aug 2018 16:20:12 +0000</pubDate><guid>https://grantwinney.com/installing-earlier-net-versions-on-visual-studio-for-mac/</guid><description>Despite its marketing, Visual Studio for Mac is not the Visual Studio that millions love, ported to the Mac. Something that&amp;rsquo;s absolutely trivial in standard VS, switching between .NET Frameworks, wasted several of my evenings. Maybe it&amp;rsquo;ll help someone else.</description><content:encoded><![CDATA[<p>I saw an implementation of some C# code this week that looked like it <em>should</em> work, but wasn&rsquo;t producing the expected results for me using .NET 4.6. I thought I&rsquo;d setup a local project in <a href="https://visualstudio.microsoft.com/vs/mac/"  target="_blank" rel="noreferrer">Visual Studio for Mac</a> and then turn the clock back a bit to see if maybe how the code was implemented changed between .NET versions. That&rsquo;d actually be pretty unusual, since .NET values backwards compatibility, but <a href="https://blogs.msdn.microsoft.com/ericlippert/2009/11/16/closing-over-the-loop-variable-part-two/"  target="_blank" rel="noreferrer">it&rsquo;s not unheard of</a>.</p>
<p>So I spent a few evenings trying to target a C# project for an earlier version of .NET, which seemed as if it were going to be trivial. You can right-click a project and choose options to find a dropdown under the &ldquo;Build&rdquo; settings, which is very similar to Visual Studio on Windows.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="vs4mac-target-framework"
    src="/installing-earlier-net-versions-on-visual-studio-for-mac/vs4mac-target-framework.png"
    width="1340"
      height="468"></figure>
<p>However, switching to an earlier version of .NET alternated between showing an error in the console when I tried to run my tiny app:</p>
<blockquote><p>WARNING: The runtime version supported by this application is unavailable.<br>
Using default runtime: v4.0.30319</p>
</blockquote><p>And sometimes the IDE blew up completely by underlining everything and claiming it could no longer find <code>System.Object</code> or <code>System.Int32</code>. This is fairly typical of my experience in VS4Mac&hellip; it works okay as long as you stay in the lines. Once you start doing anything remotely interesting though&hellip;</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="vs4mac-whats-an-integer"
    src="/installing-earlier-net-versions-on-visual-studio-for-mac/vs4mac-whats-an-integer.png"
    width="1170"
      height="588"></figure>
<p>Even after restarting (and reinstalling) VS4Mac, that project appeared to be permanently hosed, and I had to create a new one. I even tried committing the project to git before this happened so I could restore it, but it showed no changes, so whatever got borked must&rsquo;ve been in some hidden file.</p>

<h2 class="relative group">Try a different Mono version?
    <div id="try-a-different-mono-version" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#try-a-different-mono-version" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>When I checked the .NET Runtimes tab in preferences, all I had was Mono 5. Okay, so maybe each Mono version supports whatever version of the .NET Framework was out when it was released, and I needed to install them? Sure, just a guess, but it seemed logical.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="mono-5-only"
    src="/installing-earlier-net-versions-on-visual-studio-for-mac/mono-5-only.png"
    width="1920"
      height="500"></figure>
<p>I <a href="https://download.mono-project.com/archive/"  target="_blank" rel="noreferrer">downloaded</a> the last release for 2.x, 3.x, etc and installed them all. Afterwards, I tried different combinations of Mono release <em>(Project / Active Runtime)</em> to .NET framework, but no luck there either. Same error.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="all-the-monos"
    src="/installing-earlier-net-versions-on-visual-studio-for-mac/all-the-monos.png"
    width="1904"
      height="592"></figure>

<h2 class="relative group">Getting official help
    <div id="getting-official-help" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#getting-official-help" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>After awhile, I <a href="https://developercommunity.visualstudio.com/content/problem/309591/getting-the-runtime-version-supported-by-this-appl.html"  target="_blank" rel="noreferrer">opened a ticket</a> in the developer community forums with all the details above, including what I&rsquo;d tried. A few days later, it was closed with this response from a Microsoft employee:</p>
<blockquote><p>Thank you for your feedback! We have determined that this issue is not a bug. Mono by design only supports latest versions of .NET and is not .NET 2.0, 3.5 versions of CRL. So warning is valid and intended, hence closing this as not a bug. If you want to compare behavior between .NET 3.5 and 4.5 or something similar, I suggest installing very old Mono or even better do it on Windows with .NET instead of Mono.</p>
</blockquote><p>In other words, the marketing team should&rsquo;ve probably thought a little longer about how to brand this. There&rsquo;s no need to &ldquo;Visual Studio&rdquo; all the things. I replied asking for any other hints or tips on how to do that, but haven&rsquo;t heard back yet. I can only assume he means I should compile the <a href="https://github.com/mono/monodevelop"  target="_blank" rel="noreferrer">MonoDevelop source code</a>&hellip; or just use Windows.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="not-the-ide-loved-by-millions"
    src="/installing-earlier-net-versions-on-visual-studio-for-mac/not-the-ide-loved-by-millions.png"
    width="500"
      height="218"></figure>
<p>After doing some more research, I found <a href="https://www.mono-project.com/docs/about-mono/releases/4.0.0/#dropped-support-for-old-frameworks"  target="_blank" rel="noreferrer">release notes for MonoDevelop 4.0.0</a> that corraborated what he said. Although I don&rsquo;t know why the 2.x and 3.x versions of Mono don&rsquo;t work then&hellip; or why there&rsquo;s an available selection for the .NET Framework at all. Or why the MonoDevelop team decided to drop support for devs writing apps in a corporate environment, where only an older version of .NET is installed.</p>
<blockquote><p>Dropped Support for Old Frameworks</p>
<p>Reference Assemblies</p>
<p>We no longer build the reference assemblies for the .NET 2.0, .NET 3.5 or .NET 4.0 APIs, we now ship binaries of the reference assemblies (API contracts, without any actual executable code in them).</p>
<p>Mono will now only build the .NET 4.5 assemblies as well as the mobile-based profiles.</p>
<p>Note: You can still run assemblies compiled for earlier .NET profiles on Mono, there&rsquo;s no need to recompile them (they’ll just run on the .NET 4.5 assemblies instead).</p>
</blockquote>
<h2 class="relative group">What now?
    <div id="what-now" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-now" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Use Visual Studio on Windows, I guess. Seriously. I found at least <a href="https://blog.rubenwardy.com/2016/07/20/rimworld-install-monodevelop-with-dot-net-3.5/"  target="_blank" rel="noreferrer">one workaround</a> but it seems to be for MonoDevelop only, not Visual Studio. Realizing your only option is to compile from source code makes you take a long, hard look at how badly you need to test a piece of code. For me, not that badly. 😩</p>
<p>I think the most frustrating thing is that this app, which is really a <a href="https://developer.xamarin.com/releases/studio/xamarin.studio_6.3/xamarin.studio_6.3/"  target="_blank" rel="noreferrer">rebranded Xamarin Studio</a>, is marketed like it&rsquo;s the full Visual Studio IDE ported from Windows to Mac. It is absolutely <em><strong>not</strong></em>. A better name would&rsquo;ve helped avoid confusion <em>(</em><a href="https://news.ycombinator.com/item?id=14308754"  target="_blank" rel="noreferrer"><em>something that frustrated devs from the moment it launched</em></a><em>),</em> but MS has had a rough history of finding good names for products.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-earlier-net-versions-on-visual-studio-for-mac/vs4mac-installation.jpg"
    width="944"
      height="942"></figure>
]]></content:encoded><media:content url="https://grantwinney.com/installing-earlier-net-versions-on-visual-studio-for-mac/feature.webp" medium="image" type="image/webp"/></item><item><title>Convert code from C# to VB.NET and back</title><link>https://grantwinney.com/how-do-i-convert-my-code-from-c-to-vb-net-or-vice-versa/</link><pubDate>Wed, 18 Jul 2018 04:41:38 +0000</pubDate><guid>https://grantwinney.com/how-do-i-convert-my-code-from-c-to-vb-net-or-vice-versa/</guid><description>If you work with the .NET Framework long enough, you may eventually find yourself tasked with converting one language to another, either by request or necessity. But conversion isn&amp;rsquo;t always necessary - it&amp;rsquo;s possible (and easy!) to have one solution with multiple languages.</description><content:encoded><![CDATA[<p>If you work with the .NET Framework long enough, you may eventually find yourself tasked with converting one .NET language to another. There are hundreds of questions on Stack Overflow for converting <a href="https://stackoverflow.com/questions/tagged/c%23-to-vb.net"  target="_blank" rel="noreferrer">C# to VB.NET</a>, <a href="https://stackoverflow.com/questions/tagged/vb.net-to-c%23"  target="_blank" rel="noreferrer">VB.NET to C#</a>, and even <a href="https://stackoverflow.com/questions/tagged/c%23-to-f%23"  target="_blank" rel="noreferrer">C# to F#</a> - and maybe thousands more that aren&rsquo;t tagged. But first, ask yourself&hellip;</p>
<hr>

<h2 class="relative group">Does it really need to be converted?
    <div id="does-it-really-need-to-be-converted" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#does-it-really-need-to-be-converted" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>First, consider whether or not that other code really has to be translated, because you don&rsquo;t <em>need</em> to!</p>
<p>If it&rsquo;s a relatively small block of code, by all means just translate it and be done. A little upfront effort, and you&rsquo;ll have the undying gratitude of your team&hellip; or at least yourself when you need to revisit it six months later. But what if it&rsquo;s a third-party library from GitHub that&rsquo;s frequently updated, and you&rsquo;d like to keep pulling in the latest changes? Every update shouldn&rsquo;t mean another several days of translation.</p>
<p>Or what if it&rsquo;s a legacy app that your company invested 10 years in, complete with hundreds of tests proving it works <em>(or what if it has NO tests??</em> 😱 <em>)</em> There may be thousands of hours worth of slight tweaks and bug fixes that you&rsquo;ll never fully incorporate into a rewrite, no matter how diligent you are. There&rsquo;s a time to refactor, but <a href="https://www.joelonsoftware.com/2000/04/06/things-you-should-never-do-part-i/"  target="_blank" rel="noreferrer">there&rsquo;s a time to leave things alone</a>.</p>
<p><strong>💡</strong><em><strong>You can have several .NET languages in one solution, as long as they&rsquo;re separated by project.</strong></em></p>
<p>That&rsquo;s right. It&rsquo;s been possible for a <a href="https://stackoverflow.com/questions/862723/use-vb-net-and-c-sharp-in-the-same-application"  target="_blank" rel="noreferrer">long time</a>, but I&rsquo;d bet those fancy blue shoes that even some experienced devs don&rsquo;t know it - it&rsquo;s easy to live entirely within a single language. So move that legacy VB.NET or C# code into its own project and reference it from the project you&rsquo;re working in. I wrote a quickie example <em>(pictured at the top of this post -</em> <a href="https://github.com/grantwinney/BlogCodeSamples/tree/master/Languages/CSharp/CSharpAndVbNetTogether"  target="_blank" rel="noreferrer"><em>grab the source code</em></a><em>)</em> that has a C# project referencing F# and VB.NET projects.</p>
<p>All you need to do is open the project with the language you want to use, and add references to the projects that use the other .NET languages. Here&rsquo;s a C# project with references to F# and VB.NET projects:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="netlanguagereferences"
    src="/how-do-i-convert-my-code-from-c-to-vb-net-or-vice-versa/netlanguagereferences.png"
    width="1010"
      height="641"></figure>
<p>If you check out my sample app, you&rsquo;ll see the modules in the other two languages are treated by C# like any other C# static class.</p>
<hr>

<h2 class="relative group">Try translating it automatically
    <div id="try-translating-it-automatically" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#try-translating-it-automatically" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>So you gave it a solid minute or two, but the thought of dealing with multiple languages just makes you itch all over?</p>
<p>If it&rsquo;s small enough to not warrant a whole separate project, then you might start off by trying an online translation service. Here&rsquo;s a few, but unless it&rsquo;s a very simple piece of code you&rsquo;re likely to still have a few errors to resolve. Still, they might get you pretty far along.</p>
<ul>
<li><a href="http://converter.telerik.com/"  target="_blank" rel="noreferrer">Telerik Code Converter</a></li>
<li><a href="https://www.developerfusion.com/tools/"  target="_blank" rel="noreferrer">developerFusion Code Converter</a></li>
<li><a href="https://www.carlosag.net/tools/codetranslator/"  target="_blank" rel="noreferrer">CodeTranslator</a> by Carlos Aguilar Mares</li>
<li><a href="http://www.dotnetspider.com/convert/"  target="_blank" rel="noreferrer">DotNetSpider Code Converters</a></li>
<li><a href="https://codeconverter.icsharpcode.net/"  target="_blank" rel="noreferrer">Roslyn Code Converter</a> by SharpDevelop</li>
</ul>
<p>People seem to have mixed experiences with the automatic translators, at least the online ones. Although C# and VB.NET are closer in functionality than they have been in the past, sometimes things just don&rsquo;t translate nicely.</p>
<p>If those don&rsquo;t work, here are some other tools. These ones need to be installed, and some of them cost money, but they&rsquo;ll almost certainly work more reliably. To use decompilers, you&rsquo;ll need to compile the source code you want to translate by pasteing it into Visual Studio, building it, and checking the <code>bin</code> folder for the DLL file. Then the tool can decompile it into the target language for you.</p>
<ul>
<li><a href="https://www.telerik.com/products/decompiler.aspx"  target="_blank" rel="noreferrer">Telerik JustDecompile</a> decompiles to C# or VB.NET <em>(free)</em></li>
<li><a href="https://github.com/icsharpcode/ILSpy/releases"  target="_blank" rel="noreferrer">ILSpy</a> can decompile assemblies to C#. <em>(free)</em></li>
<li><a href="http://www.jetbrains.com/decompiler/"  target="_blank" rel="noreferrer">JetBrains dotPeek</a> decompiles to C# as well. <em>(free)</em></li>
<li><a href="http://www.devextras.com/decompiler/"  target="_blank" rel="noreferrer">DevExtras .NET CodeReflect</a> decompiles to C# or VB.NET. <em>(free)</em></li>
<li><a href="https://www.red-gate.com/products/dotnet-development/reflector/"  target="_blank" rel="noreferrer">.NET Reflector</a> can decompile assemblies into C# or VB.NET, so compile the source language and then decompile into the target language. <em>($95 - $195)</em></li>
<li>Tangible Software Solutions has converters for <a href="https://www.tangiblesoftwaresolutions.com/product_details/vb-to-csharp-converter.html"  target="_blank" rel="noreferrer">VB.NET to C#</a>, <a href="https://www.tangiblesoftwaresolutions.com/product_details/csharp-to-vb-converter.html"  target="_blank" rel="noreferrer">C# to VB.NET</a>, and C++ and Java too. <em>($119 - $499)</em></li>
<li><a href="http://www.vbconversions.com/"  target="_blank" rel="noreferrer">VBConversions</a> converts VB.Net to C# and if the testimonials they post are honest, then it&rsquo;s pretty impressive&hellip; 110,000 lines of code converted perfectly? <em>($50/month - $500)</em></li>
</ul>
<hr>

<h2 class="relative group">And if all else fails?
    <div id="and-if-all-else-fails" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#and-if-all-else-fails" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If the automatic translators fail, the only options left are to hire someone or get your hands dirty. Here&rsquo;s a couple books that might help. The second one is a few years old, but the first one looks intriguing.</p>
<ul>
<li><a href="https://www.amazon.com/gp/product/0692433694/ref=as_li_qf_asin_il_tl?ie=UTF8&amp;tag=gwin04-20&amp;creative=9325&amp;linkCode=as2&amp;creativeASIN=0692433694&amp;linkId=da35c09eb1e79b589bc9cb04ecbe5179"  target="_blank" rel="noreferrer">C#-Visual Basic Bilingual Dictionary: Visual Studio 2015 Edition</a>, by Tim Patrick</li>
<li><a href="https://www.amazon.com/Beginning-ASP-NET-4-5-C-VB/dp/1118311809"  target="_blank" rel="noreferrer">Beginning ASP.NET 4.5: in C# and VB</a>, by Imar Spaanjaars</li>
</ul>
<p>And here&rsquo;s a few more links to documentation:</p>
<ul>
<li><a href="https://docs.microsoft.com/en-us/dotnet/index"  target="_blank" rel="noreferrer">.NET Documentation - Microsoft Docs</a> with guides and examples for C#, VB.NET, F#, etc.</li>
<li><a href="https://github.com/dotnet/roslyn/wiki/Languages-features-in-C%23-6-and-VB-14"  target="_blank" rel="noreferrer">Languages features in C# and VB - dotnet/roslyn Wiki</a></li>
<li><a href="https://github.com/dotnet/try-samples/tree/main/101-linq-samples/"  target="_blank" rel="noreferrer">101 LINQ samples - Code Samples | Microsoft Docs</a></li>
<li><a href="https://stackoverflow.com/questions/1337253/converting-c-sharp-knowledge-to-vb-net-any-potential-problems"  target="_blank" rel="noreferrer">Converting C# knowledge to VB.NET - any potential problems?</a> (an old Stack Overflow thread, but may still be helpful)</li>
</ul>
]]></content:encoded><media:content url="https://grantwinney.com/how-do-i-convert-my-code-from-c-to-vb-net-or-vice-versa/feature.webp" medium="image" type="image/webp"/></item><item><title>Calculate Easter and other holidays in Erlang</title><link>https://grantwinney.com/how-to-calculate-easter-and-other-holidays-in-erlang/</link><pubDate>Sun, 01 Jul 2018 00:32:52 +0000</pubDate><guid>https://grantwinney.com/how-to-calculate-easter-and-other-holidays-in-erlang/</guid><description>I wrote a small library for calculating Easter and other holidays in Erlang. Here&amp;rsquo;s how I did it and what I learned.</description><content:encoded><![CDATA[<p>On a whim, I created an Erlang module for calculating holidays, and things were going okay until it came to Easter. Have you ever tried to calculate Easter? It&rsquo;s surprisingly difficult.</p>
<p>Easter doesn&rsquo;t occur on the same day of the month, or on the Xth Sunday, or anything that simple like most holidays. It&rsquo;s based on the occurrence of a particular full moon, among other things. I started by trying to recreate <a href="https://www.assa.org.au/edm#Calculator"  target="_blank" rel="noreferrer">this explanation</a>, but trying to recreate it in code got pretty ugly pretty quick.</p>
<p>Here&rsquo;s what I came up with, but it&rsquo;s only the Catholic (aka western) date. The Orthodox (aka eastern) date is a completely different calculation, which I implemented with the help of <a href="https://en.wikipedia.org/wiki/Computus#Meeus.27s_Julian_algorithm"  target="_blank" rel="noreferrer">Meeus&rsquo;s Julian algorithm</a>.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">get_easter</span><span class="p">(</span><span class="n">atom</span><span class="p">(),</span> <span class="n">pos_integer</span><span class="p">())</span> <span class="o">-&gt;</span> <span class="p">{</span><span class="n">pos_integer</span><span class="p">(),</span> <span class="n">pos_integer</span><span class="p">(),</span> <span class="n">pos_integer</span><span class="p">()}.</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_easter</span><span class="p">(</span><span class="n">catholic</span><span class="p">,</span> <span class="nv">Year</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">G</span> <span class="o">=</span> <span class="nb">trunc</span><span class="p">(</span><span class="nn">math</span><span class="p">:</span><span class="nf">fmod</span><span class="p">(</span><span class="nv">Year</span><span class="p">,</span><span class="mi">19</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">    <span class="nv">C</span> <span class="o">=</span> <span class="nv">Year</span> <span class="ow">div</span> <span class="mi">100</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nv">H</span> <span class="o">=</span> <span class="nb">trunc</span><span class="p">(</span><span class="nn">math</span><span class="p">:</span><span class="nf">fmod</span><span class="p">(</span><span class="nv">C</span> <span class="o">-</span> <span class="p">(</span><span class="nv">C</span> <span class="ow">div</span> <span class="mi">4</span><span class="p">)</span> <span class="o">-</span> <span class="p">((</span><span class="mi">8</span> <span class="o">*</span> <span class="nv">C</span> <span class="o">+</span> <span class="mi">13</span><span class="p">)</span> <span class="ow">div</span> <span class="mi">25</span><span class="p">)</span> <span class="o">+</span> <span class="mi">19</span> <span class="o">*</span> <span class="nv">G</span> <span class="o">+</span> <span class="mi">15</span><span class="p">,</span> <span class="mi">30</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">    <span class="nv">I</span> <span class="o">=</span> <span class="nv">H</span> <span class="o">-</span> <span class="p">(</span><span class="nv">H</span> <span class="ow">div</span> <span class="mi">28</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="p">(</span><span class="nv">H</span> <span class="ow">div</span> <span class="mi">28</span><span class="p">)</span> <span class="o">*</span> <span class="nb">trunc</span><span class="p">(</span><span class="mi">29</span> <span class="o">/</span> <span class="p">(</span><span class="nv">H</span> <span class="o">+</span> <span class="mi">1</span><span class="p">))</span> <span class="o">*</span> <span class="p">((</span><span class="mi">21</span> <span class="o">-</span> <span class="nv">G</span><span class="p">)</span> <span class="ow">div</span> <span class="mi">11</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Day</span> <span class="o">=</span> <span class="nb">trunc</span><span class="p">(</span><span class="nv">I</span> <span class="o">-</span> <span class="nn">math</span><span class="p">:</span><span class="nf">fmod</span><span class="p">((</span><span class="nv">Year</span> <span class="o">+</span> <span class="p">(</span><span class="nv">Year</span> <span class="ow">div</span> <span class="mi">4</span><span class="p">))</span> <span class="o">+</span> <span class="nv">I</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">-</span> <span class="nv">C</span> <span class="o">+</span> <span class="p">(</span><span class="nv">C</span> <span class="ow">div</span> <span class="mi">4</span><span class="p">),</span> <span class="mi">7</span><span class="p">)</span> <span class="o">+</span> <span class="mi">28</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nv">Day</span> <span class="k">of</span>
</span></span><span class="line"><span class="cl">        <span class="p">_</span> <span class="k">when</span> <span class="nv">Day</span> <span class="o">&gt;</span> <span class="mi">31</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span><span class="nv">Year</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="nv">Day</span> <span class="o">-</span> <span class="mi">31</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">        <span class="p">_</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span><span class="nv">Year</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="nv">Day</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">get_easter_test_</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="o">?</span><span class="p">_</span><span class="n">assertEqual</span><span class="p">({</span><span class="mi">2019</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">21</span><span class="p">},</span> <span class="nn">holidays</span><span class="p">:</span><span class="nf">get_easter</span><span class="p">(</span><span class="n">catholic</span><span class="p">,</span> <span class="mi">2019</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">        <span class="o">?</span><span class="p">_</span><span class="n">assertEqual</span><span class="p">({</span><span class="mi">2020</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">12</span><span class="p">},</span> <span class="nn">holidays</span><span class="p">:</span><span class="nf">get_easter</span><span class="p">(</span><span class="n">catholic</span><span class="p">,</span> <span class="mi">2020</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">        <span class="o">?</span><span class="p">_</span><span class="n">assertEqual</span><span class="p">({</span><span class="mi">2021</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">4</span><span class="p">},</span> <span class="nn">holidays</span><span class="p">:</span><span class="nf">get_easter</span><span class="p">(</span><span class="n">catholic</span><span class="p">,</span> <span class="mi">2021</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">        <span class="o">?</span><span class="p">_</span><span class="n">assertEqual</span><span class="p">({</span><span class="mi">2022</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">17</span><span class="p">},</span> <span class="nn">holidays</span><span class="p">:</span><span class="nf">get_easter</span><span class="p">(</span><span class="n">catholic</span><span class="p">,</span> <span class="mi">2022</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">        <span class="o">?</span><span class="p">_</span><span class="n">assertEqual</span><span class="p">({</span><span class="mi">2023</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">9</span><span class="p">},</span> <span class="nn">holidays</span><span class="p">:</span><span class="nf">get_easter</span><span class="p">(</span><span class="n">catholic</span><span class="p">,</span> <span class="mi">2023</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">        <span class="o">?</span><span class="p">_</span><span class="n">assertEqual</span><span class="p">({</span><span class="mi">2024</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">31</span><span class="p">},</span> <span class="nn">holidays</span><span class="p">:</span><span class="nf">get_easter</span><span class="p">(</span><span class="n">catholic</span><span class="p">,</span> <span class="mi">2024</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">        <span class="o">?</span><span class="p">_</span><span class="n">assertEqual</span><span class="p">({</span><span class="mi">2025</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">20</span><span class="p">},</span> <span class="nn">holidays</span><span class="p">:</span><span class="nf">get_easter</span><span class="p">(</span><span class="n">catholic</span><span class="p">,</span> <span class="mi">2025</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">        <span class="o">?</span><span class="p">_</span><span class="n">assertEqual</span><span class="p">({</span><span class="mi">2026</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">},</span> <span class="nn">holidays</span><span class="p">:</span><span class="nf">get_easter</span><span class="p">(</span><span class="n">catholic</span><span class="p">,</span> <span class="mi">2026</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">        <span class="o">?</span><span class="p">_</span><span class="n">assertEqual</span><span class="p">({</span><span class="mi">2027</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">28</span><span class="p">},</span> <span class="nn">holidays</span><span class="p">:</span><span class="nf">get_easter</span><span class="p">(</span><span class="n">catholic</span><span class="p">,</span> <span class="mi">2027</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">        <span class="o">?</span><span class="p">_</span><span class="n">assertEqual</span><span class="p">({</span><span class="mi">2028</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">16</span><span class="p">},</span> <span class="nn">holidays</span><span class="p">:</span><span class="nf">get_easter</span><span class="p">(</span><span class="n">catholic</span><span class="p">,</span> <span class="mi">2028</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">        <span class="o">?</span><span class="p">_</span><span class="n">assertEqual</span><span class="p">({</span><span class="mi">2029</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">1</span><span class="p">},</span> <span class="nn">holidays</span><span class="p">:</span><span class="nf">get_easter</span><span class="p">(</span><span class="n">catholic</span><span class="p">,</span> <span class="mi">2029</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">        <span class="o">?</span><span class="p">_</span><span class="n">assertEqual</span><span class="p">({</span><span class="mi">2030</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">21</span><span class="p">},</span> <span class="nn">holidays</span><span class="p">:</span><span class="nf">get_easter</span><span class="p">(</span><span class="n">catholic</span><span class="p">,</span> <span class="mi">2030</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">].</span></span></span></code></pre></div></div>
<p>There&rsquo;s a heavy use of <code>div</code> instead of <code>/</code> because the former behaves similar to integer arithmetic in C#, whereas the latter behaves like floating point arithmetic.</p>
<p>For example, <code>7 div 4 == 1</code> but <code>7 / 4 == 1.75</code>.</p>

<h2 class="relative group">The Importance of Tests
    <div id="the-importance-of-tests" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-importance-of-tests" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I ended up converting an <a href="https://www.codeproject.com/Articles/10860/Calculating-Christian-Holidays"  target="_blank" rel="noreferrer">algorithm in C#</a>, which was a <a href="https://www.codeproject.com/Articles/1595/Calculating-Easter-Sunday"  target="_blank" rel="noreferrer">conversion from c++</a>, which was in turn converted from Pascal code, so unit tests seemed like a good idea. I&rsquo;m reasonably sure it&rsquo;s behaving!</p>
<p>Since we can pass functions around in Erlang, I added a function that allows for passing a date and a list of holidays to test it against.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">is_holiday</span><span class="p">(</span><span class="n">atom</span><span class="p">(),</span> <span class="n">date_timestamp</span><span class="p">(),</span> <span class="p">[</span><span class="k">fun</span><span class="p">()])</span> <span class="o">-&gt;</span> <span class="n">boolean</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">is_holiday</span><span class="p">(</span><span class="nv">CountryCode</span><span class="p">,</span> <span class="nv">Date</span><span class="p">,</span> <span class="nv">Holidays</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nn">lists</span><span class="p">:</span><span class="nf">any</span><span class="p">(</span><span class="k">fun</span><span class="p">(</span><span class="nv">Holiday</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nv">Holiday</span><span class="p">(</span><span class="nv">CountryCode</span><span class="p">,</span> <span class="nv">Date</span><span class="p">)</span> <span class="o">=:=</span> <span class="n">true</span> <span class="k">end</span><span class="p">,</span> <span class="nv">Holidays</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">MyDate</span> <span class="o">=</span> <span class="p">{{</span><span class="mi">2019</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">25</span><span class="p">},</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">}},</span>
</span></span><span class="line"><span class="cl"><span class="nn">holidays</span><span class="p">:</span><span class="nf">is_holiday</span><span class="p">(</span><span class="n">us</span><span class="p">,</span> <span class="nv">MyDate</span><span class="p">,</span> <span class="p">[</span><span class="k">fun</span> <span class="nn">holidays</span><span class="p">:</span><span class="n">is_easter</span><span class="o">/</span><span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                 <span class="k">fun</span> <span class="nn">holidays</span><span class="p">:</span><span class="n">is_thanksgiving</span><span class="o">/</span><span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                 <span class="k">fun</span> <span class="nn">holidays</span><span class="p">:</span><span class="n">is_new_years</span><span class="o">/</span><span class="mi">2</span><span class="p">]).</span>   <span class="err">%</span> <span class="n">returns</span> <span class="n">false</span></span></span></code></pre></div></div>
<p>If you use Erlang and you need to know if a date is a holiday, try this out. If you have your own holidays to add, open an issue or PR, or just leave a comment below. Contributions welcome!</p>
]]></content:encoded><media:content url="https://grantwinney.com/how-to-calculate-easter-and-other-holidays-in-erlang/feature.webp" medium="image" type="image/webp"/></item><item><title>Access yesterday's Internet with the Wayback Machine API</title><link>https://grantwinney.com/access-yesterdays-internet-with-the-wayback-machine-apis/</link><pubDate>Sun, 24 Jun 2018 20:02:24 +0000</pubDate><guid>https://grantwinney.com/access-yesterdays-internet-with-the-wayback-machine-apis/</guid><description>The Wayback Machine, a product of the Internet Archive, is an ambitious tool that&amp;rsquo;s been documenting websites for many years. It&amp;rsquo;s useful when a page you need is removed by the original author. Let&amp;rsquo;s take a look at their API and how we might make use of it.</description><content:encoded><![CDATA[<p>Today I&rsquo;m checking out the Wayback Machine APIs from the Internet Archive. If you haven&rsquo;t heard of the IA before, it&rsquo;s a site that&rsquo;s aiming to&hellip; well.. archive everything. They&rsquo;re probably best known for helping people find <a href="https://web.archive.org/"  target="_blank" rel="noreferrer">archived versions of deleted pages</a>, but they have a lot of <a href="https://archive.org/details/software"  target="_blank" rel="noreferrer">old software and games</a>, <a href="https://openlibrary.org/"  target="_blank" rel="noreferrer">books</a>, and <a href="https://archive.org/projects/"  target="_blank" rel="noreferrer">much more</a>. Unfortunately, they only have a <a href="https://archive.org/details/F19StealthFighter_1020"  target="_blank" rel="noreferrer">demo version</a> of F-19 stealth fighter, the first game I ever played&hellip; well, other than <a href="https://archive.org/details/msdos_Oregon_Trail_The_1990"  target="_blank" rel="noreferrer">Oregon Trail</a>. And <a href="https://archive.org/details/msdos_Number_Munchers_1990"  target="_blank" rel="noreferrer">Number Munchers</a>!!</p>
<p>It sure is convenient that all the games of my&hellip; um&hellip; youth are on a uh&hellip; archival website. 😢</p>
<p>Okay, let&rsquo;s see what we can do! But first, two things before you get started:</p>
<ul>
<li>If you&rsquo;re new to this, consider reading &ldquo;<a href="https://grantwinney.com/what-is-an-api/"  target="_blank" rel="noreferrer">What do we mean by API?</a>&rdquo; to familiarize yourself.</li>
<li>You also may want to install <a href="https://www.getpostman.com/"  target="_blank" rel="noreferrer">Postman</a>, which lets you access API endpoints without having to write an app, plus you can save/sync everything to the cloud.</li>
</ul>
<hr>

<h2 class="relative group">Request a webpage
    <div id="request-a-webpage" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#request-a-webpage" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>When I started writing this I really thought there would be more to their API, but there&rsquo;s just <a href="https://archive.org/help/wayback_api.php"  target="_blank" rel="noreferrer">one call with a couple parameters</a>, so&hellip; we&rsquo;ll look at it really closely. 😁 In fact, the Internet Archive has <a href="https://archive.org/projects/"  target="_blank" rel="noreferrer">a number of projects</a> which may each have their own APIs and warrant a separate look at some future point (like the <a href="https://openlibrary.org/dev/docs/api/books"  target="_blank" rel="noreferrer">Open Library</a>).</p>
<p>There&rsquo;s no authentication necessary, and no posted rate limits, so to get the latest cached version of a page you just pass in the URL. Here&rsquo;s a post I wrote years ago, but have since deleted:</p>
<p><a href="http://archive.org/wayback/available?url=grantwinney.com/how-to-make-security-essentials-ignore-directories/"  target="_blank" rel="noreferrer">http://archive.org/wayback/available?url=grantwinney.com/how-to-make-security-essentials-ignore-directories/</a></p>
<p>If there&rsquo;s anything available, you&rsquo;ll get the latest snapshot:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;grantwinney.com/how-to-make-security-essentials-ignore-directories/&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;archived_snapshots&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;closest&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;200&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;available&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;http://web.archive.org/web/20140808005655/http://www.grantwinney.com:80/how-to-make-security-essentials-ignore-directories/&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;timestamp&#34;</span><span class="p">:</span> <span class="s2">&#34;20140808005655&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h3 class="relative group">Getting an older version
    <div id="getting-an-older-version" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#getting-an-older-version" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>If you want an older version, you can pass in a timestamp. It&rsquo;d be nice if all you had to do was subtract one second from the previous timestamp to get the next oldest one, but it seems to do a <em>&ldquo;closest to the timestamp&rdquo;</em> match, so the timestamp you specify has to be <em>closer</em> to the older version than the newer one. It&rsquo;s in <code>YYYYMMDDhhmmss</code> format, so here I&rsquo;ll subtract 1 month and try again:</p>
<p><a href="https://archive.org/wayback/available?url=grantwinney.com/how-to-make-security-essentials-ignore-directories/&amp;timestamp=20140708005654"  target="_blank" rel="noreferrer">https://archive.org/wayback/available?url=grantwinney.com/how-to-make-security-essentials-ignore-directories/&amp;timestamp=20140708005654</a></p>
<p>And the response that comes back is a slightly older version from 2 months previous:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;grantwinney.com/how-to-make-security-essentials-ignore-directories/&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;timestamp&#34;</span><span class="p">:</span> <span class="s2">&#34;20140708005654&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;archived_snapshots&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;closest&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;200&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;available&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;http://web.archive.org/web/20140607150143/http://www.grantwinney.com:80/how-to-make-security-essentials-ignore-directories/&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;timestamp&#34;</span><span class="p">:</span> <span class="s2">&#34;20140607150143&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h3 class="relative group">What if nothing&rsquo;s available?
    <div id="what-if-nothings-available" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-if-nothings-available" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>If there&rsquo;s nothing available for a page, you&rsquo;ll just get an empty object:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;grantwinney.com/how-to-make-security-essentials-ignore-directories/asdf&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;archived_snapshots&#34;</span><span class="p">:</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<hr>

<h2 class="relative group">JSONP Callbacks
    <div id="jsonp-callbacks" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#jsonp-callbacks" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There&rsquo;s one other parameter besides <code>timestamp</code>, and that&rsquo;s <code>callback</code> which produces a JSONP response.</p>
<p>To see the difference, try requesting the resource in any of the major browsers&hellip;</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">Httpreq</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">apiUrl</span> <span class="o">=</span> <span class="s1">&#39;https://archive.org/wayback/available&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">reqUrl</span> <span class="o">=</span> <span class="s1">&#39;grantwinney.com/how-to-make-security-essentials-ignore-directories/&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">Httpreq</span><span class="p">.</span><span class="nx">onreadystatechange</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">Httpreq</span><span class="p">.</span><span class="nx">readyState</span> <span class="o">==</span> <span class="nx">XMLHttpRequest</span><span class="p">.</span><span class="nx">DONE</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">alert</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">Httpreq</span><span class="p">.</span><span class="nx">responseText</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">Httpreq</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s2">&#34;GET&#34;</span><span class="p">,</span><span class="sb">`</span><span class="si">${</span><span class="nx">apiUrl</span><span class="si">}</span><span class="sb">?url=</span><span class="si">${</span><span class="nx">reqUrl</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">Httpreq</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span></span></span></code></pre></div></div>
<p><em>(or alternatively using jQuery)</em></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">apiUrl</span> <span class="o">=</span> <span class="s1">&#39;https://archive.org/wayback/available&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">reqUrl</span> <span class="o">=</span> <span class="s1">&#39;grantwinney.com/how-to-make-security-essentials-ignore-directories&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">$</span><span class="p">.</span><span class="nx">getJSON</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">apiUrl</span><span class="si">}</span><span class="sb">?url=</span><span class="si">${</span><span class="nx">reqUrl</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span></span></span></code></pre></div></div>
<p>&hellip; and you&rsquo;re likely to get an error similar to this one:</p>
<blockquote><p>Failed to load <a href="https://archive.org/wayback/available?url=grantwinney.com/how-to-make-security-essentials-ignore-directories/:"  target="_blank" rel="noreferrer">https://archive.org/wayback/available?url=grantwinney.com/how-to-make-security-essentials-ignore-directories/:</a> No &lsquo;Access-Control-Allow-Origin&rsquo; header is present on the requested resource. Origin &lsquo;<a href="https://fiddle.jshell.net"  target="_blank" rel="noreferrer">https://fiddle.jshell.net</a>&rsquo; is therefore not allowed access.</p>
<p>Uncaught DOMException: Failed to execute &lsquo;send&rsquo; on &lsquo;XMLHttpRequest&rsquo;: Failed to load &lsquo;<a href="https://archive.org/wayback/available?url=grantwinney.com/how-to-make-security-essentials-ignore-directories/"  target="_blank" rel="noreferrer">https://archive.org/wayback/available?url=grantwinney.com/how-to-make-security-essentials-ignore-directories/</a>&rsquo;. at XMLHttpRequest.send (&lt;anonymous&gt;:1:781)</p>
</blockquote><p>This is new territory for me, but from what I understand it&rsquo;s a security measure in most browsers, that prevents scripts from requesting resources from domains that are different than the one they&rsquo;re running in. It helps prevent malicious scripts from doing Bad Things with your data. Here&rsquo;s a couple sites on <a href="https://www.getfilecloud.com/blog/using-jsonp-for-cross-domain-requests/"  target="_blank" rel="noreferrer">using JSONP for cross domain requests</a> and <a href="https://www.sitepoint.com/jsonp-examples/"  target="_blank" rel="noreferrer">using JSONP with jQuery</a>.</p>
<p>While <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS"  target="_blank" rel="noreferrer">CORS</a> appears to be the newer/better way to make cross-domain requests, there&rsquo;s also JSONP. In either case the server has to support it, and the Wayback Machine supports JSONP, so let&rsquo;s see what that request looks like.</p>
<p><em>Using an inline function:</em></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">apiUrl</span> <span class="o">=</span> <span class="s1">&#39;https://archive.org/wayback/available&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">reqUrl</span> <span class="o">=</span> <span class="s1">&#39;grantwinney.com/how-to-make-security-essentials-ignore-directories&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">$</span><span class="p">.</span><span class="nx">getJSON</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">apiUrl</span><span class="si">}</span><span class="sb">?url=</span><span class="si">${</span><span class="nx">reqUrl</span><span class="si">}</span><span class="sb">&amp;callback=?`</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">json</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">dir</span><span class="p">(</span><span class="nx">json</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>  <span class="c1">// inline function
</span></span></span></code></pre></div></div>
<p><em>Or a separate function, if that&rsquo;s more your style:</em></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">handleData</span><span class="p">(</span><span class="nx">json</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">dir</span><span class="p">(</span><span class="nx">json</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">apiUrl</span> <span class="o">=</span> <span class="s1">&#39;https://archive.org/wayback/available&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">reqUrl</span> <span class="o">=</span> <span class="s1">&#39;grantwinney.com/how-to-make-security-essentials-ignore-directories&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">$</span><span class="p">.</span><span class="nx">getJSON</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">apiUrl</span><span class="si">}</span><span class="sb">?url=</span><span class="si">${</span><span class="nx">reqUrl</span><span class="si">}</span><span class="sb">&amp;callback=?`</span><span class="p">,</span> <span class="nx">handleData</span><span class="p">);</span>  <span class="c1">// separate function
</span></span></span></code></pre></div></div>
<p>In either case, you&rsquo;ll get a response like this in the console:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Object
</span></span><span class="line"><span class="cl">  archived_snapshots:
</span></span><span class="line"><span class="cl">    closest:
</span></span><span class="line"><span class="cl">      available:true
</span></span><span class="line"><span class="cl">      status:&#34;200&#34;
</span></span><span class="line"><span class="cl">      timestamp:&#34;20140808005655&#34;
</span></span><span class="line"><span class="cl">      url:&#34;http://web.archive.org/web/20140808005655/http://www.grantwinney.com:80/how-to-make-security-essentials-ignore-directories/&#34;
</span></span><span class="line"><span class="cl">  url: &#34;grantwinney.com/how-to-make-security-essentials-ignore-directories&#34;</span></span></code></pre></div></div>
<p>With a little adjustment, we can get just the archived URL:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">apiUrl</span> <span class="o">=</span> <span class="s1">&#39;https://archive.org/wayback/available&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">reqUrl</span> <span class="o">=</span> <span class="s1">&#39;grantwinney.com/how-to-make-security-essentials-ignore-directories&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">$</span><span class="p">.</span><span class="nx">getJSON</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">apiUrl</span><span class="si">}</span><span class="sb">?url=</span><span class="si">${</span><span class="nx">reqUrl</span><span class="si">}</span><span class="sb">&amp;callback=?`</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">json</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">  <span class="kd">let</span> <span class="nx">snapshot</span> <span class="o">=</span> <span class="nx">json</span><span class="p">[</span><span class="s1">&#39;archived_snapshots&#39;</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nb">Object</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="nx">snapshot</span><span class="p">).</span><span class="nx">length</span> <span class="o">!==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  	<span class="nx">console</span><span class="p">.</span><span class="nx">dir</span><span class="p">(</span><span class="nx">snapshot</span><span class="p">.</span><span class="nx">closest</span><span class="p">.</span><span class="nx">url</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// prints http://web.archive.org/web/20140808005655/http://www.grantwinney.com:80/how-to-make-security-essentials-ignore-directories/
</span></span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span></span></span></code></pre></div></div>

<h2 class="relative group">Wayback Machine as a browser extension
    <div id="wayback-machine-as-a-browser-extension" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#wayback-machine-as-a-browser-extension" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>You can see how this could be implemented as a browser extension fairly easily. If you request a resource and get a 404, check the Wayback Machine and automatically redirect to the cached version if there is one.</p>
<p>In fact, there&rsquo;s a Chrome extension called <a href="https://chrome.google.com/webstore/detail/wayback-machine/fpnmgdkabkmnadcjpehmlllkndpkmiak"  target="_blank" rel="noreferrer">Wayback Machine</a> that does just that, and it&rsquo;s actually from the Internet Archive! How nice.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="wayback-machine-extension"
    src="/access-yesterdays-internet-with-the-wayback-machine-apis/wayback-machine-extension.png"
    width="1000"
      height="794"></figure>
<p>If you&rsquo;re curious what it does, check it out using the awesome <a href="https://chrome.google.com/webstore/detail/chrome-extension-source-v/jifpbeccnghkjeaalbbjmodiffmgedin"  target="_blank" rel="noreferrer">Chrome extension source viewer</a> extension, or check out the main crux of it below.</p>
<p>It adds a listener, so that if any page you visit returns a 404 or any one of a handful of other codes indicating &ldquo;failure&rdquo;, and assuming you&rsquo;re not in &ldquo;incognito&rdquo; mode <em>(nice),</em> it&rsquo;ll go out and check the Wayback Machine for the most recent copy available. If it finds one, it&rsquo;ll popup a banner like the one above, offering to load it. So if you see the banner, an archived copy definitely exists. Clicking the link in the banner loads the archived page.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm"> * LICENSE: AGPL-3
</span></span></span><span class="line"><span class="cl"><span class="cm"> * Copyright 2016, Internet Archive
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="nx">chrome</span><span class="p">.</span><span class="nx">webRequest</span><span class="p">.</span><span class="nx">onCompleted</span><span class="p">.</span><span class="nx">addListener</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">details</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">function</span> <span class="nx">tabIsReady</span><span class="p">(</span><span class="nx">isIncognito</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">var</span> <span class="nx">httpFailCodes</span> <span class="o">=</span> <span class="p">[</span><span class="mi">404</span><span class="p">,</span> <span class="mi">408</span><span class="p">,</span> <span class="mi">410</span><span class="p">,</span> <span class="mi">451</span><span class="p">,</span> <span class="mi">500</span><span class="p">,</span> <span class="mi">502</span><span class="p">,</span> <span class="mi">503</span><span class="p">,</span> <span class="mi">504</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="mi">509</span><span class="p">,</span> <span class="mi">520</span><span class="p">,</span> <span class="mi">521</span><span class="p">,</span> <span class="mi">523</span><span class="p">,</span> <span class="mi">524</span><span class="p">,</span> <span class="mi">525</span><span class="p">,</span> <span class="mi">526</span>
</span></span><span class="line"><span class="cl">        <span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">isIncognito</span> <span class="o">===</span> <span class="kc">false</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">            <span class="nx">details</span><span class="p">.</span><span class="nx">frameId</span> <span class="o">===</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">            <span class="nx">httpFailCodes</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="nx">details</span><span class="p">.</span><span class="nx">statusCode</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">            <span class="nx">isValidUrl</span><span class="p">(</span><span class="nx">details</span><span class="p">.</span><span class="nx">url</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">Globalstatuscode</span> <span class="o">=</span> <span class="nx">details</span><span class="p">.</span><span class="nx">statusCode</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="nx">wmAvailabilityCheck</span><span class="p">(</span><span class="nx">details</span><span class="p">.</span><span class="nx">url</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">wayback_url</span><span class="p">,</span> <span class="nx">url</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="p">(</span><span class="nx">details</span><span class="p">.</span><span class="nx">statusCode</span> <span class="o">==</span> <span class="mi">504</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="c1">//notify(wayback_url,&#39;View an archived version courtesy of the Internet Archive WayBack Machine&#39;);
</span></span></span><span class="line"><span class="cl">                    <span class="nx">chrome</span><span class="p">.</span><span class="nx">notifications</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="s1">&#39;wayback-notification&#39;</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                            <span class="nx">type</span><span class="o">:</span> <span class="s1">&#39;basic&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="nx">requireInteraction</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="nx">iconUrl</span><span class="o">:</span> <span class="s1">&#39;/images/logo.gif&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;Page not available ?&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="nx">message</span><span class="o">:</span> <span class="s2">&#34;View an archived version courtesy of the WayBack Machine&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="nx">buttons</span><span class="o">:</span> <span class="p">[{</span>
</span></span><span class="line"><span class="cl">                                <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;Click here to see archived version&#34;</span>
</span></span><span class="line"><span class="cl">                            <span class="p">}]</span>
</span></span><span class="line"><span class="cl">                        <span class="p">},</span>
</span></span><span class="line"><span class="cl">                        <span class="kd">function</span><span class="p">(</span><span class="nx">id</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                            <span class="nx">myNotID</span> <span class="o">=</span> <span class="nx">id</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                        <span class="p">}</span>
</span></span><span class="line"><span class="cl">                    <span class="p">);</span>
</span></span><span class="line"><span class="cl">                    <span class="nx">chrome</span><span class="p">.</span><span class="nx">notifications</span><span class="p">.</span><span class="nx">onButtonClicked</span><span class="p">.</span><span class="nx">addListener</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">notifId</span><span class="p">,</span> <span class="nx">btnIdx</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="k">if</span> <span class="p">(</span><span class="nx">notifId</span> <span class="o">===</span> <span class="nx">myNotID</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                            <span class="k">if</span> <span class="p">(</span><span class="nx">btnIdx</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nx">chrome</span><span class="p">.</span><span class="nx">tabs</span><span class="p">.</span><span class="nx">create</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">                                    <span class="nx">url</span><span class="o">:</span> <span class="nx">wayback_url</span>
</span></span><span class="line"><span class="cl">                                <span class="p">});</span>
</span></span><span class="line"><span class="cl">                                <span class="nx">chrome</span><span class="p">.</span><span class="nx">notifications</span><span class="p">.</span><span class="nx">clear</span><span class="p">(</span><span class="nx">myNotID</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                                <span class="nx">myNotID</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                            <span class="p">}</span>
</span></span><span class="line"><span class="cl">                        <span class="p">}</span>
</span></span><span class="line"><span class="cl">                    <span class="p">});</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nx">chrome</span><span class="p">.</span><span class="nx">tabs</span><span class="p">.</span><span class="nx">executeScript</span><span class="p">(</span><span class="nx">details</span><span class="p">.</span><span class="nx">tabId</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="nx">file</span><span class="o">:</span> <span class="s2">&#34;scripts/client.js&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">},</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="nx">chrome</span><span class="p">.</span><span class="nx">tabs</span><span class="p">.</span><span class="nx">sendMessage</span><span class="p">(</span><span class="nx">details</span><span class="p">.</span><span class="nx">tabId</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                            <span class="nx">type</span><span class="o">:</span> <span class="s2">&#34;SHOW_BANNER&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="nx">wayback_url</span><span class="o">:</span> <span class="nx">wayback_url</span>
</span></span><span class="line"><span class="cl">                        <span class="p">});</span>
</span></span><span class="line"><span class="cl">                    <span class="p">});</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="p">});</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">details</span><span class="p">.</span><span class="nx">tabId</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">chrome</span><span class="p">.</span><span class="nx">tabs</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="nx">details</span><span class="p">.</span><span class="nx">tabId</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">tab</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">tabIsReady</span><span class="p">(</span><span class="nx">tab</span><span class="p">.</span><span class="nx">incognito</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">urls</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;&lt;all_urls&gt;&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">types</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;main_frame&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm"> * Checks Wayback Machine API for url snapshot
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">wmAvailabilityCheck</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">onsuccess</span><span class="p">,</span> <span class="nx">onfail</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">xhr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">requestUrl</span> <span class="o">=</span> <span class="s2">&#34;https://archive.org/wayback/available&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">requestParams</span> <span class="o">=</span> <span class="s2">&#34;url=&#34;</span> <span class="o">+</span> <span class="nb">encodeURI</span><span class="p">(</span><span class="nx">url</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">xhr</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s2">&#34;POST&#34;</span><span class="p">,</span> <span class="nx">requestUrl</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">xhr</span><span class="p">.</span><span class="nx">setRequestHeader</span><span class="p">(</span><span class="s2">&#34;Content-type&#34;</span><span class="p">,</span> <span class="s2">&#34;application/x-www-form-urlencoded&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">xhr</span><span class="p">.</span><span class="nx">setRequestHeader</span><span class="p">(</span><span class="s2">&#34;Wayback-Api-Version&#34;</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">xhr</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">var</span> <span class="nx">response</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">responseText</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="kd">var</span> <span class="nx">wayback_url</span> <span class="o">=</span> <span class="nx">getWaybackUrlFromResponse</span><span class="p">(</span><span class="nx">response</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">wayback_url</span> <span class="o">!==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">onsuccess</span><span class="p">(</span><span class="nx">wayback_url</span><span class="p">,</span> <span class="nx">url</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">onfail</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">onfail</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="nx">xhr</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="nx">requestParams</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<hr>

<h2 class="relative group">A Final Note
    <div id="a-final-note" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#a-final-note" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>It&rsquo;s an ambitious project, but as a minimalist I just don&rsquo;t get the value of archiving <em>everything</em>. The Library of Congress has some <a href="https://www.loc.gov/collections/"  target="_blank" rel="noreferrer">amazing collections</a> too, like notes from <a href="https://www.loc.gov/collections/abraham-lincoln-papers/about-this-collection/"  target="_blank" rel="noreferrer">Abraham Lincoln</a> and <a href="https://www.loc.gov/collections/alexander-hamilton-papers/about-this-collection/"  target="_blank" rel="noreferrer">Alexander Hamilton</a>, but it&rsquo;s sensibly curated. LoC even had access to the Twitter floodgates but decided <a href="https://blogs.loc.gov/loc/files/2017/12/2017dec_twitter_white-paper.pdf"  target="_blank" rel="noreferrer">most of it isn&rsquo;t worth collecting</a> - but I&rsquo;d bet IA would love access to it all. In fact, I seem to remember a tweet from Brewster Kahle (IA founder) that appeared to call out Twitter for not opening their floodgates.</p>
<p>I also question the legality of what&rsquo;s been collected, which makes me hesitant to actually download software and books from their site. Is all that software really available to download, or is it basically piracy? What about the digitized books? What about <a href="https://lauren.vortex.com/2017/04/23/more-regarding-a-terrible-decision-by-the-internet-archive"  target="_blank" rel="noreferrer">websites who didn&rsquo;t want to be archived</a>? In their drive to archive &ldquo;all the things&rdquo;, I hope they&rsquo;re diligent about the rights and preferences of others. Maybe these issues are already addressed somewhere on their site - if you know where, please share links. Thanks!</p>
<p>If you&rsquo;re interested in learning more about APIs, check out my comparison of <a href="https://grantwinney.com/a-look-at-the-many-ways-apis-can-authorize-access/"  target="_blank" rel="noreferrer">the many ways APIs can authorize access</a>. And if you&rsquo;ve ever thought about writing an API wrapper in the language of your choice, <a href="https://grantwinney.com/what-is-an-api-wrapper/"  target="_blank" rel="noreferrer">you might find this helpful</a>.</p>
]]></content:encoded><media:content url="https://grantwinney.com/access-yesterdays-internet-with-the-wayback-machine-apis/feature.webp" medium="image" type="image/webp"/></item><item><title>A look at the many ways APIs can authorize access</title><link>https://grantwinney.com/a-look-at-the-many-ways-apis-can-authorize-access/</link><pubDate>Tue, 19 Jun 2018 13:45:34 +0000</pubDate><guid>https://grantwinney.com/a-look-at-the-many-ways-apis-can-authorize-access/</guid><description>After writing about so many APIs and having to figure out the auth process for each, I wanted to compare and contrast how some of these services approach authentication and authorization, and why they might&amp;rsquo;ve decided to do it the way they did.</description><content:encoded><![CDATA[<p>When you encounter an <a href="https://grantwinney.com/what-is-an-api/"  target="_blank" rel="noreferrer">API</a> that gives you access to some data - maybe yours (tweets, photos), maybe someone else&rsquo;s (space photos, climate stats) - you&rsquo;ll usually encounter some form of required <a href="https://www.cyberciti.biz/faq/authentication-vs-authorization/"  target="_blank" rel="noreferrer">authentication/authorization</a> before being allowed to access it. That&rsquo;s because the usual purpose of an API is to safely expose specific data for easy consumption by some other application that builds on top of it. Maybe it&rsquo;s an app that helps you schedule tweets like <a href="https://buffer.com/"  target="_blank" rel="noreferrer">Buffer</a>, or a browser extension that adds new functionality to your Trello boards like <a href="https://chrome.google.com/webstore/detail/plus-for-trello-time-trac/gjjpophepkbhejnglcmkdnncmaanojkf"  target="_blank" rel="noreferrer">Plus for Trello</a>. In the case of Ghost, the platform my blog runs on, <a href="https://api.ghost.org/v1.22.0/docs"  target="_blank" rel="noreferrer">the consumer is Ghost itself</a> (although you could develop your own app on top of it too).</p>
<p>If you write an API to expose data to the outside world, there&rsquo;s questions you&rsquo;ll want to ask. How do you know the user is who they claim to be? Or that they actually granted access to the app requesting their data? Or whether the requester even <em>is</em> a user or an app? How do you limit the rate at which data can be accessed? It depends on how sensitive the data is, what you want to let them do with it, whether there are different levels of access for different users, etc.</p>
<p>I&rsquo;ve written about a number of <a href="https://grantwinney.com/tags/api/"  target="_blank" rel="noreferrer">APIs</a> in the past, and wanted to compare how these different services approach authentication and authorization, and why they might&rsquo;ve decided to do it the way they did.</p>
<hr>

<h2 class="relative group">Nothing
    <div id="nothing" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#nothing" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The easiest way to authenticate users is to, uh&hellip; not. If you&rsquo;ve got an API that only allows read operations, like the the <a href="https://grantwinney.com/what-is-penguin-random-house-api/"  target="_blank" rel="noreferrer">Penguin Random House API</a> that provides access to author and book data, then this is at least a fairly safe operation. Safe, in that you don&rsquo;t have random unknown users uploading data.</p>
<p>But potentially unsafe too, in that it&rsquo;s more difficult to rate limit. Even the most powerful server serving up resources behind an API has its limits. And as an API grows in popularity, or if it&rsquo;s possible to request extraordinarily large amounts of data, then this might cause slow response times for everyone&hellip; or even take the server down.</p>
<p>I&rsquo;m not sure exactly how you can limit rates if you don&rsquo;t know who&rsquo;s making the request, other than blocking an IP address after a certain threshold is reached. But the use of <a href="https://www.howtogeek.com/133680/htg-explains-what-is-a-vpn/"  target="_blank" rel="noreferrer">VPNs</a> that mask true IPs can make this difficult. And accessing an API from a university or corporation, where thousands of users may be masked behind a few outward-facing IP addresses, could result in blocking way more people than intended. Off the top of my head, I&rsquo;d think that caching similar result sets, or integrating with a service like <a href="https://www.cloudflare.com/rate-limiting/"  target="_blank" rel="noreferrer">Cloudflare</a>, might help there.</p>
<p>Other examples include the <a href="https://grantwinney.com/what-is-us-census-bureau-api/"  target="_blank" rel="noreferrer">US Census Bureau</a>, which (interestingly) provides a way to request a key, but doesn&rsquo;t seem to require it. Same goes for the <a href="https://grantwinney.com/what-is-google-maps-api/"  target="_blank" rel="noreferrer">Google Maps API</a>. The <a href="https://grantwinney.com/what-is-iss-notify-api/"  target="_blank" rel="noreferrer">ISS Notify API</a> and <a href="https://grantwinney.com/passwordrandom-api/"  target="_blank" rel="noreferrer">PasswordRandom API</a> have no auth or documented rate limits, but they&rsquo;re relatively small so it&rsquo;s probably not a big deal for them.</p>
<p>The upside is ease of access. It doesn&rsquo;t get any easier than having to do absolutely <em>nothing</em>. But it&rsquo;s difficult to limit which, or how much, data can be accessed when there&rsquo;s nothing to easily identify the consumer.</p>
<hr>

<h2 class="relative group">API Key
    <div id="api-key" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#api-key" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Some APIs require that consumers create an account and generate an API key, which is either appended to the end of the query string or as a header on every API request. This allows a service to know who the requester is and enforce rate limits, among other things.</p>
<p>The <a href="https://grantwinney.com/what-is-nasa-api/"  target="_blank" rel="noreferrer">NASA API</a> lets you use a generic &ldquo;demo&rdquo; key with a rate limit of 50 requests per day <em>(enough to try it out),</em> but if you request an API key <em>(an easy process)</em> the rate limit increases to 1000 requests <em>per hour</em>. From what I&rsquo;ve observed, rate limits tend to usually be greater (when they&rsquo;re specified at all) for APIs where some sort of authentication is required.</p>
<p>The <a href="https://grantwinney.com/what-is-internet-game-database-api/"  target="_blank" rel="noreferrer">Internet Game Database API</a> has API keys and clearly defined rate limits, providing a <a href="https://api.igdb.com/pricing"  target="_blank" rel="noreferrer">free tier</a> with 3000 requests per month. The <a href="https://grantwinney.com/what-is-noaa-api/"  target="_blank" rel="noreferrer">NOAA API</a> emails you a token upon request, and once you have it, the rate limits are high for a personal account - 5/second and 10,000/day.</p>
<p>The <a href="https://grantwinney.com/what-is-openweathermap-api/"  target="_blank" rel="noreferrer">OpenWeatherMap API</a>, unlike some others I looked at, provides a very nice interface for deleting keys and creating new ones as needed, with the push of a single button. Since it&rsquo;s conceivable a user&rsquo;s key could be exposed by accident, or shared with a malicious application, giving them a way to reset it is a very good practice.</p>
<p>The upside is that the user is now known, so the API can customize their access, such as letting them update their profile or setting limits. The downside is that a single API key is an all or nothing proposition, providing access to <em>everything</em>. As a user you would want to be <em>very</em> careful who you handed it out to! Additionally, the API has no clue who&rsquo;s trying to access it&hellip; is it you? An application on your behalf? It&rsquo;s not very fine-grained, and someone writing an application needs to show users how they can find their own key.</p>
<hr>

<h2 class="relative group">Auth Token
    <div id="auth-token" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#auth-token" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>A step up from a simple key is an authentication token. It involves an extra step, but better supports the idea of third-party applications consuming an API on a user&rsquo;s behalf, since it combines two identifiers - one for the user, one for the application - into a single token.</p>
<p><a href="https://secure.backblaze.com/r/00d15h"  target="_blank" rel="noreferrer">Backblaze</a> has their <a href="https://grantwinney.com/what-is-backblaze-b2-api/"  target="_blank" rel="noreferrer">B2 API</a> that requires applications to register for an application id, combine it with a user&rsquo;s account id <em>(which the user can find after logging in),</em> <a href="https://stackoverflow.com/a/201510/301857"  target="_blank" rel="noreferrer">base64 encode</a> it, and make a one-off request for an &ldquo;auth token&rdquo;. If you use Postman, select &ldquo;Basic Auth&rdquo; in the Authorization tab and set the username and password fields to your account id and application id, respectively, and it&rsquo;ll create the header for you. The one-off request returns an authorization token, which gets attached as an &ldquo;Authorization&rdquo; header on other requests.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;accountId&#34;</span><span class="p">:</span> <span class="err">&lt;your_account_id&gt;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;authorizationToken&#34;</span><span class="p">:</span> <span class="err">&lt;your_auth_token&gt;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;apiUrl&#34;</span><span class="p">:</span> <span class="s2">&#34;https://api001.backblazeb2.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadUrl&#34;</span><span class="p">:</span> <span class="s2">&#34;https://f001.backblazeb2.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The upside over a simple API key is that applications could potentially request specific permissions, which become part of the application id and are completely separate from the user id. The downside is that users would still have to know how to find their account id. Also, once you hand over your account id (or API key, for that matter) to an application, there&rsquo;s no guarantee you can make it <em>forget</em>. The best you could hope for is that the account ID (or API key) could be reset, which could affect a dozen other applications you gave it to and be a total pain.</p>
<hr>

<h2 class="relative group">OAuth 1.0
    <div id="oauth-10" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#oauth-10" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Then there&rsquo;s the OAuth protocol. The previous process seems to be their own (very similar) implementation, but OAuth is an agreed-upon standard, so all parties involved know exactly what to expect.</p>
<p>If an API provider supports it, then an application can register its name, other basic info, and the specific permissions it&rsquo;d like to access on a user&rsquo;s behalf. At this point, no user exists yet - the application is simply letting its existence be known. In return, the API provider generates a consumer key and consumer secret <em>(</em><a href="https://stackoverflow.com/a/28057700/301857"  target="_blank" rel="noreferrer"><em>see a definition of each</em></a><em>)</em>.</p>
<p>When a user decides to use the application, they don&rsquo;t hand over their keys or IDs, usernames or passwords, or anything else personal. Instead, the application redirects users to the API provider&rsquo;s authentication page where they can login and confirm access to their data. The API provider handles authenticating the user, remembering the connection between user and application, and generating an access token and access secret that authorizes the application to act on the user&rsquo;s behalf - but because of that earlier registration process, the app can <em>only</em> access what it initially requested (and usually only for a limited amount of time).</p>
<p>Here&rsquo;s a thread with some helpful analogies: <a href="https://stackoverflow.com/q/4201431/301857"  target="_blank" rel="noreferrer">What exactly is OAuth (Open Authorization)?</a></p>
<p>The <a href="https://grantwinney.com/what-is-twitter-api/"  target="_blank" rel="noreferrer">Twitter API</a> supports <a href="https://tools.ietf.org/pdf/rfc5849.pdf"  target="_blank" rel="noreferrer">OAuth 1.0</a>. If you&rsquo;re using Postman to test it out, just select OAuth 1.0 as the type in the Authorization tab, fill in the keys and tokens <em>(</em><a href="https://grantwinney.com/what-is-twitter-api/"  target="_blank" rel="noreferrer"><em>find out how to access them</em></a><em>),</em> and make a call to an endpoint. Postman adds an <code>Authorization</code> header that looks something like this:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">OAuth
oauth_consumer_key=&#34;&lt;consumer-key&gt;&#34;,
oauth_token=&#34;&lt;access-token&gt;&#34;,
oauth_signature_method=&#34;HMAC-SHA1&#34;,
oauth_timestamp=&#34;1528911816&#34;,
oauth_nonce=&#34;&lt;random-nonce&gt;&#34;,
oauth_version=&#34;1.0&#34;,
oauth_signature=&#34;&lt;oauth-signature&gt;&#34;</code></pre></div>
<p>The consumer key (generated after registration) and access token (generated after a user signs in) are sent as-is, but the secrets are combined to create a &ldquo;signature&rdquo;. I tried implementing all this on my own for a script I wrote - then I got smart and found an existing solution called <a href="https://github.com/linvi/tweetinvi"  target="_blank" rel="noreferrer">Tweetinvi</a> that already figured it out. You can dig through their code to see how they do it, but that&rsquo;s a wheel I don&rsquo;t care to reinvent unless I have to. 😣</p>
<p>Here&rsquo;s an app trying to authorize with Twitter to gain access to a user&rsquo;s account:</p>
<p>The <a href="https://grantwinney.com/what-is-trello-api/"  target="_blank" rel="noreferrer">Trello API</a> supports two methods. They have their own <a href="https://developers.trello.com/page/authorization#authorizing-a-client"  target="_blank" rel="noreferrer">authorization route</a>, which combines an application key with a token that&rsquo;s generated when a user visits a particular link and authenticates with Trello. They also support <a href="https://developers.trello.com/page/authorization#using-basic-oauth"  target="_blank" rel="noreferrer">OAuth 1.0</a>, combining the aforementioned application key with an application &ldquo;secret&rdquo;, and providing some URLs through which the user can authenticate themselves and authorize the application.</p>
<p>Here&rsquo;s an app trying to authorize with Trello:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/a-look-at-the-many-ways-apis-can-authorize-access/trello-authorize-hubstaff.png"
    width="595"
      height="478"></figure>
<p>The upsides include users of a service not having to hand any personally identifiable information to third-party applications, and access can (or should be able to) be revoked at any time from the API provider&rsquo;s side.</p>
<p>Say I login to Twitter and authorize Buffer to post tweets on my behalf. I can login to Twitter again later, find Buffer in the list of apps I authorized, and revoke access&hellip; I don&rsquo;t even have to tell Buffer. Now if Buffer attempts further action using the same auth token, Twitter can just reject the request. If a user authenticates 20 different apps to access their data, they can easily cut one or more them off without affecting the others.</p>
<p>The downside is that it&rsquo;s more complicated to implement and use. &ldquo;More complicated&rdquo; doesn&rsquo;t always mean &ldquo;better&rdquo;, but in this case it&rsquo;s worth it.</p>
<hr>

<h2 class="relative group">OAuth 2.0
    <div id="oauth-20" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#oauth-20" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Now this gets interesting&hellip; depending on your definition. 😏</p>
<p>There are two versions of OAuth, but 2.0 is <em>not</em> an upgrade to 1.0 - it&rsquo;s a replacement. It&rsquo;s a complete rewrite with similar end-goals, that attempts to reduce complexity, improve the experience in obtaining an auth token, and help separate the job of issuing tokens vs serving up the API endpoints. If only it were that simple!</p>
<p>You can <a href="https://hueniverse.com/introducing-oauth-2-0-b5681da60ce2"  target="_blank" rel="noreferrer">read more about OAuth 2.0</a> in this (still applicable, I think) article by Eran Hammer, who was closely involved with its design. I think it&rsquo;s enough to know <a href="https://stackoverflow.com/q/4113934/301857"  target="_blank" rel="noreferrer">there&rsquo;s a difference</a>, and that its design has been somewhat contentious, with one of the original authors even <a href="https://hueniverse.com/oauth-2-0-and-the-road-to-hell-8eec45921529"  target="_blank" rel="noreferrer">disowning it</a> and then <a href="https://hueniverse.com/auth-to-see-the-wizard-4a7c3572f618"  target="_blank" rel="noreferrer">writing his own replacement</a>. Worth reading about if you&rsquo;re planning on implementing your own API and wavering between which protocol to support.</p>
<p>The <a href="https://grantwinney.com/what-is-slack-api/"  target="_blank" rel="noreferrer">Slack API</a> requires an application to specify which permissions and scopes it needs, and which channel in the workspace it should be attached to. Then they issue an an OAuth access token, which is attached to each request. In Postman, you can click the &ldquo;Authorization&rdquo; tab and select the OAuth 2.0 type. Paste the access token into the field on the right, and your request will automatically get an &ldquo;Authorization&rdquo; header with a value of <code>Bearer &lt;your-auth-token&gt;</code>.</p>
<p>Once an application obtains the auth token to access a user&rsquo;s data, that&rsquo;s the only thing it needs (there&rsquo;s no signature to calculate from multiple values like OAuth 1.0) until the token expires, so it <em>must</em> be submitted over https or risk a <a href="https://www.incapsula.com/web-application-security/man-in-the-middle-mitm.html"  target="_blank" rel="noreferrer">mitm attack</a>.</p>
<p><a href="https://grantwinney.com/what-is-dropbox-api/"  target="_blank" rel="noreferrer">Dropbox</a> works similarly, where an app is registered with the permissions it needs&hellip; although with Dropbox the only permissions are pretty much &ldquo;access everything&rdquo; or &ldquo;access a single folder&rdquo;. But then it provides an OAuth 2.0 token, which you attach to the requests, and has the same look as the Slack one.</p>
<p>The <a href="https://grantwinney.com/what-is-the-ghost-api/"  target="_blank" rel="noreferrer">Ghost API</a> works a bit differently. You can set a portion of the API, the part that requests data, to be public if you&rsquo;d like. If you do that, then GETs on most data can be performed by attaching a publicly available client id and secret <em>(a bit of a misnomer)</em> to a request like <code>&amp;client_id=&lt;client_id&gt;&amp;client_secret=&lt;client_secret&gt;</code>. Getting the id and secret are a little awkward but not hard. If you keep it private, then it&rsquo;s two steps. You have to call one endpoint and pass it an <code>x-www-form-urlencoded</code> body like <code>grant_type=password&amp;username=&lt;username&gt;&amp;password=&lt;password&gt;&amp;client_id=&lt;client_id&gt;&amp;client_secret=&lt;client_secret&gt;</code>. That returns an OAuth 2.0 token for use with other requests.</p>
<p>The <a href="https://grantwinney.com/what-is-the-google-books-api/"  target="_blank" rel="noreferrer">Google Books API</a> provides <a href="https://developers.google.com/books/docs/v1/using"  target="_blank" rel="noreferrer">two methods</a>. An API key for public data requests, or OAuth 2.0 token for private data. What&rsquo;s nice about Google is that they provide a whole series of <a href="https://developers.google.com/api-client-library/"  target="_blank" rel="noreferrer">libraries in various languages</a> to make accessing their APIs easier.</p>
<hr>
<p>I hope you found something of interest here! After writing about <a href="https://grantwinney.com/tags/api/"  target="_blank" rel="noreferrer">quite a few APIs</a> and having to figure out the authorization process for each, I&rsquo;ve been considering their various approaches.</p>
<p>It seems like most services are using OAuth, and that OAuth 1.0 is not going away soon due to concerns that 2.0 is less secure. It also seems like a simple API key is fairly popular for services that only allow GET operations, but that there needs to be a way to reset an API key in the event it&rsquo;s accidentally exposed or shared with an application that turns out to be untrustworthy.</p>
]]></content:encoded><media:content url="https://grantwinney.com/a-look-at-the-many-ways-apis-can-authorize-access/feature.webp" medium="image" type="image/webp"/></item><item><title>A random selection algorithm that factors in age (weighted selection)</title><link>https://grantwinney.com/writing-a-random-selection-algorithm-that-factors-in-the-age-of-an-item/</link><pubDate>Thu, 07 Jun 2018 03:44:14 +0000</pubDate><guid>https://grantwinney.com/writing-a-random-selection-algorithm-that-factors-in-the-age-of-an-item/</guid><description>Have you ever had a collection of items and needed to select a random one from the lot? What if you have a class with some property (i.e. &amp;lsquo;age&amp;rsquo; or &amp;lsquo;weight&amp;rsquo;) that you want to take into account when doing the random selection? Let&amp;rsquo;s see how we might approach that&amp;hellip;</description><content:encoded><![CDATA[
<h2 class="relative group">Random Selection
    <div id="random-selection" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#random-selection" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Have you ever had a collection of items and needed to select a random one from the lot? That&rsquo;s easy enough in most languages, since they generally provide a way to generate a random number which you can use as the index.</p>
<p>C# has its <a href="https://msdn.microsoft.com/en-us/library/2dx6wyd4%5C%28v=vs.110%5C%29.aspx"  target="_blank" rel="noreferrer">Random</a> class:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">System.Collections.Generic</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">System.Linq</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Program</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>	
</span></span><span class="line"><span class="cl">	<span class="kd">static</span> <span class="n">Random</span> <span class="n">rnd</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Random</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">	<span class="kd">public</span> <span class="kd">static</span> <span class="k">void</span> <span class="n">Main</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">	<span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="kt">var</span> <span class="n">names</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="p">{</span> <span class="s">&#34;Tom&#34;</span><span class="p">,</span> <span class="s">&#34;Mary&#34;</span><span class="p">,</span> <span class="s">&#34;Sam&#34;</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">		<span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Random name: {names[rnd.Next(names.Count())]}&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Ruby also has a <a href="https://ruby-doc.org/core-2.4.0/Random.html"  target="_blank" rel="noreferrer">Random</a> class:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">names</span> <span class="o">=</span> <span class="o">[</span><span class="s1">&#39;Tom&#39;</span><span class="p">,</span><span class="s1">&#39;Mary&#39;</span><span class="p">,</span><span class="s1">&#39;Sam&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="nb">puts</span> <span class="s2">&#34;Random name: </span><span class="si">#{</span><span class="n">names</span><span class="o">[</span><span class="no">Random</span><span class="o">.</span><span class="n">rand</span><span class="p">(</span><span class="n">names</span><span class="o">.</span><span class="n">length</span><span class="p">)</span><span class="o">]</span><span class="si">}</span><span class="s2">&#34;</span></span></span></code></pre></div></div>
<p>And Python has a <a href="https://docs.python.org/3.5/library/random.html"  target="_blank" rel="noreferrer">Random</a> module:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">randint</span>
</span></span><span class="line"><span class="cl"><span class="n">names</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;Tom&#39;</span><span class="p">,</span><span class="s1">&#39;Mary&#39;</span><span class="p">,</span><span class="s1">&#39;Sam&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">names</span><span class="p">[</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">names</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">)])</span></span></span></code></pre></div></div>

<h2 class="relative group">Weighted Random Selection
    <div id="weighted-random-selection" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#weighted-random-selection" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>But what if you have a class with some property (i.e. &ldquo;age&rdquo; or &ldquo;weight&rdquo;) that you want to take into account when doing the random selection? For example, I recently wrote an app to <a href="https://grantwinney.com/using-aws-lambda-and-tweetinvi-to-tweet-a-random-ghost-blog-post/"  target="_blank" rel="noreferrer">randomly tweet blog posts</a>, and I wanted to make sure newer posts were far more likely to be selected than older ones&hellip; but I wanted even the oldest post to still have some tiny chance of being selected.</p>
<p>Here&rsquo;s a sample of the object I was dealing with. I was interested in the <code>PublishedAt</code> field, where a post that had been published more recently should be more likely to be selected.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Post</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Id</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Title</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">CreatedAt</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">CreatedBy</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">UpdatedAt</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">UpdatedBy</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">PublishedAt</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">PublishedBy</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h3 class="relative group">Attempt 1: Raffle Ticket System
    <div id="attempt-1-raffle-ticket-system" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#attempt-1-raffle-ticket-system" aria-label="Anchor">#</a>
    </span>
    
</h3>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="raffle-ticket-system"
    src="/writing-a-random-selection-algorithm-that-factors-in-the-age-of-an-item/raffle-ticket-system.jpg"
    width="940"
      height="771"></figure>
<p>My first attempt was to use my oldest post as &ldquo;ground 0&rdquo; and give all posts after it a &ldquo;weight&rdquo; that was calculated using the number of days that had passed since that original post. So the oldest published post would have a &ldquo;weight&rdquo; of 1, but a post written a week later would have a &ldquo;weight&rdquo; of 8, and one a year later a weight of 366. It&rsquo;s almost like a raffle drawing where one person got 1 ticket to try and win, but someone else got hundreds. The person with hundreds is more likely to win, but not guaranteed.</p>
<p>Using that weight, I created a second collection, which included a post&rsquo;s ID a number of times equal to its &ldquo;weight&rdquo;. So the ID of that week-old post would occur 8 times in the list, but the oldest post would only occur once. <em>(The</em> <em><code>_posts_</code></em> <em>variable is ordered so that the latest (newest) post is first.)</em></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">static</span> <span class="kt">string</span> <span class="n">GetRandomPostId</span><span class="p">(</span><span class="n">List</span><span class="p">&lt;</span><span class="n">Post</span><span class="p">&gt;</span> <span class="n">posts</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">lastDate</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">Parse</span><span class="p">(</span><span class="n">posts</span><span class="p">.</span><span class="n">Last</span><span class="p">().</span><span class="n">PublishedAt</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">postIds</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl">    <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">p</span> <span class="k">in</span> <span class="n">posts</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">weight</span> <span class="p">=</span> <span class="p">(</span><span class="n">DateTime</span><span class="p">.</span><span class="n">Parse</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">PublishedAt</span><span class="p">)</span> <span class="p">-</span> <span class="n">lastDate</span><span class="p">).</span><span class="n">Days</span> <span class="p">+</span> <span class="m">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">postIds</span><span class="p">.</span><span class="n">AddRange</span><span class="p">(</span><span class="n">Enumerable</span><span class="p">.</span><span class="n">Repeat</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">Id</span><span class="p">,</span> <span class="n">weight</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">postIds</span><span class="p">[</span><span class="n">rnd</span><span class="p">.</span><span class="n">Next</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="n">postIds</span><span class="p">.</span><span class="n">Count</span><span class="p">)];</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Assuming I started with some posts like these ones:</p>
<p>I&rsquo;d end up with a collection of strings like this, which I&rsquo;d call Random on to get a random item.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">3456-qwer-8
3456-qwer-8
3456-qwer-8
... repeated 363 more times
2345-zxcv-6
2345-zxcv-6
2345-zxcv-6
2345-zxcv-6
2345-zxcv-6
2345-zxcv-6
2345-zxcv-6
2345-zxcv-6
1234-abcd-9</code></pre></div>
<p>The problem with that approach was the potential to gobble up a ton of memory with a huge list, and I&rsquo;m trying to run this job on the free AWS Lambda tier. My oldest posts are from years ago, which means the most recent ones had a weight of nearly 2000, so they show in the list 2000 times. The list was a quarter-million strings - and each post afterwards would continue to increase it. I was curious if there was a more efficient way to tackle this.</p>

<h3 class="relative group">Attempt 2: Overflow System
    <div id="attempt-2-overflow-system" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#attempt-2-overflow-system" aria-label="Anchor">#</a>
    </span>
    
</h3>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="buckets"
    src="/writing-a-random-selection-algorithm-that-factors-in-the-age-of-an-item/buckets.jpg"
    width="940"
      height="788"></figure>
<p>I came across a Stack Overflow post on <a href="https://stackoverflow.com/q/56692/301857"  target="_blank" rel="noreferrer">weighted choice</a>, which led to this bit of code. It&rsquo;s still calculating a weight dependent on the earliest post&rsquo;s publish date compared with each subsequent&rsquo;s post publish date. <em>(Again, posts are organized with most recent on the top.)</em></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">static</span> <span class="kt">string</span> <span class="n">GetRandomPostIdWeightedOnAge</span><span class="p">(</span><span class="n">List</span><span class="p">&lt;</span><span class="n">Post</span><span class="p">&gt;</span> <span class="n">posts</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">firstPubDate</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">Parse</span><span class="p">(</span><span class="n">posts</span><span class="p">.</span><span class="n">Last</span><span class="p">().</span><span class="n">PublishedAt</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">postIdsAndWeights</span>
</span></span><span class="line"><span class="cl">        <span class="p">=</span> <span class="n">posts</span><span class="p">.</span><span class="n">Select</span><span class="p">(</span><span class="n">p</span> <span class="p">=&gt;</span> <span class="n">Tuple</span><span class="p">.</span><span class="n">Create</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">Id</span><span class="p">,</span> <span class="p">(</span><span class="n">DateTime</span><span class="p">.</span><span class="n">Parse</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">PublishedAt</span><span class="p">)</span> <span class="p">-</span> <span class="n">firstPubDate</span><span class="p">).</span><span class="n">Days</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">randomWeight</span> <span class="p">=</span> <span class="n">rnd</span><span class="p">.</span><span class="n">Next</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="n">postIdsAndWeights</span><span class="p">.</span><span class="n">Sum</span><span class="p">(</span><span class="n">p</span> <span class="p">=&gt;</span> <span class="n">p</span><span class="p">.</span><span class="n">Item2</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">p</span> <span class="k">in</span> <span class="n">postIdsAndWeights</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">randomWeight</span> <span class="p">&lt;=</span> <span class="n">p</span><span class="p">.</span><span class="n">Item2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">p</span><span class="p">.</span><span class="n">Item1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">randomWeight</span> <span class="p">-=</span> <span class="n">p</span><span class="p">.</span><span class="n">Item2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>  <span class="c1">// required to compile, but won&#39;t happen unless there are no posts</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Assuming I started with some posts like these ones:</p>
<p>I end up with a list of tuples like this, with the post ID and its &ldquo;weight&rdquo; (age in days).</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">{ 6789-mnbv-3, 366 },
{ 5678-poiu-4, 40 },
{ 4567-lkjh-6, 37 },
{ 3456-qwer-8, 32 },
{ 2345-zxcv-6, 8 },
{ 1234-abcd-9, 1 }</code></pre></div>
<p>I added even more weight, thus skewing the random selection even <em>more</em> in favor of newer posts, by replacing the second line above with this:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">postsCount</span> <span class="p">=</span> <span class="n">posts</span><span class="p">.</span><span class="n">Count</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">postIdsAndWeights</span>
</span></span><span class="line"><span class="cl">    <span class="p">=</span> <span class="n">posts</span><span class="p">.</span><span class="n">Select</span><span class="p">((</span><span class="n">p</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">Tuple</span><span class="p">.</span><span class="n">Create</span><span class="p">(</span><span class="n">posts</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">Id</span><span class="p">,</span> <span class="p">((</span><span class="n">DateTime</span><span class="p">.</span><span class="n">Parse</span><span class="p">(</span><span class="n">posts</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">PublishedAt</span><span class="p">)</span> <span class="p">-</span> <span class="n">earliestPublishDate</span><span class="p">).</span><span class="n">Days</span> <span class="p">*</span> <span class="p">(</span><span class="n">postsCount</span> <span class="p">-</span> <span class="n">i</span><span class="p">)));</span></span></span></code></pre></div></div>
<p>Giving something more akin to this:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">{ 6789-mnbv-3, 1996 },
{ 5678-poiu-4, 200 },
{ 4567-lkjh-6, 148 },
{ 3456-qwer-8, 96 },
{ 2345-zxcv-6, 16 },
{ 1234-abcd-9, 1 }</code></pre></div>
<p>The total weight of all six posts is 2457, so the random number will fall between 0 and 2456 (upper limit is exclusive, at least in C#). When the <code>foreach</code> loop in the above code executes, the first thing it&rsquo;ll test is whether the random selected number is less than or equal to the weight of the first post - 1996.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">p</span> <span class="k">in</span> <span class="n">postIdsAndWeights</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">randomWeight</span> <span class="p">&lt;=</span> <span class="n">p</span><span class="p">.</span><span class="n">Item2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">p</span><span class="p">.</span><span class="n">Item1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">randomWeight</span> <span class="p">-=</span> <span class="n">p</span><span class="p">.</span><span class="n">Item2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Let&rsquo;s say the randomly selected weight is 2048. That&rsquo;s not less than the first record, so it&rsquo;ll skip that record and subtract the weight of the first record from the random number. So now we have <code>2048 - 1996 = 52</code>. That&rsquo;s definitely less than the weight of the second record, so the second record is the winner.</p>
<p>Over many test runs, 1996 out of 2457 random numbers (81%) will favor the first record. 2196 out of 2457 random numbers (89%) will favor one of the first two records. It&rsquo;s still possible for the last record to be selected, but only in 1 out of 2457 times (0.04%). And with each newer post that gets added, the less likely it is that older posts will be randomly selected.</p>
<p>When I ran it against my blog 50 times, which has about 200 posts going back nearly 5 years, these were the results. There are 26 from 2018 (and it&rsquo;s only June), 18 from 2017, 4 from 2016, and nothing earlier. Seems to be working as expected to me.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Selected post from: 2018-05-23T18:00:44.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2018-01-05T19:06:40.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2016-12-03T16:03:01.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2018-02-26T01:11:54.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2018-02-12T04:59:35.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2018-01-05T19:06:40.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2018-04-13T17:10:14.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2016-04-03T09:03:25.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2017-09-12T23:18:32.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2018-04-24T12:21:13.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2017-05-19T10:15:00.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2018-01-31T00:07:00.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2017-05-28T05:07:53.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2018-02-04T03:03:21.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2018-02-03T14:08:21.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2017-12-16T04:58:47.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2017-06-16T12:30:21.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2017-06-30T12:14:21.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2016-10-31T13:23:44.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2017-02-14T13:04:24.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2017-12-16T21:32:52.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2018-06-02T12:32:54.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2017-12-23T04:10:01.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2017-06-30T12:14:21.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2018-01-21T04:56:35.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2018-01-31T00:07:00.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2017-02-17T09:03:29.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2017-01-04T08:17:32.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2018-03-03T18:52:51.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2018-03-14T11:57:50.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2018-06-02T12:32:54.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2017-10-09T12:28:11.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2018-04-24T12:21:13.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2016-09-24T13:30:49.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2018-04-13T17:10:14.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2017-12-29T15:33:04.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2018-06-01T17:28:01.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2017-10-16T17:31:00.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2018-05-22T16:55:18.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2018-01-25T04:59:32.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2017-12-23T04:10:01.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2018-06-02T12:32:54.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2018-01-01T05:00:00.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2018-05-22T16:55:18.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2017-07-23T19:41:19.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2018-05-22T16:55:18.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2017-12-17T19:37:29.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2018-02-06T12:36:34.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2017-10-09T12:28:11.000Z
</span></span><span class="line"><span class="cl">Selected post from: 2018-01-25T04:59:32.000Z</span></span></code></pre></div></div>
]]></content:encoded><media:content url="https://grantwinney.com/writing-a-random-selection-algorithm-that-factors-in-the-age-of-an-item/feature.webp" medium="image" type="image/webp"/></item><item><title>Getting rid of unused function errors when using timers in Erlang</title><link>https://grantwinney.com/getting-rid-of-unused-function-errors-when-using-timers-in-erlang/</link><pubDate>Wed, 06 Jun 2018 18:18:06 +0000</pubDate><guid>https://grantwinney.com/getting-rid-of-unused-function-errors-when-using-timers-in-erlang/</guid><description>Have you ever tried to execute a function at some future time in Erlang? You can, with a timer, but the compiler may complain that the function you&amp;rsquo;re calling via the timer is unused. Why is that and what can you do?</description><content:encoded><![CDATA[
<h2 class="relative group">The Problem
    <div id="the-problem" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-problem" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Have you ever tried to execute a function at some future time in Erlang? You can, with the <a href="http://erlang.org/doc/man/timer.html#apply_after-4"  target="_blank" rel="noreferrer">timer:apply_after</a> (and related) functions, but you&rsquo;re likely to run into an error when compiling. Let&rsquo;s say you have a module with two functions - one is exported, and the other is simply used to print your age.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">module</span><span class="p">(</span><span class="n">test</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">export</span><span class="p">([</span><span class="n">main</span><span class="o">/</span><span class="mi">0</span><span class="p">]).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">print_age</span><span class="p">(</span><span class="nv">Age</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nn">io</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34;Your age: </span><span class="si">~p~n</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[</span><span class="nv">Age</span><span class="p">]).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">main</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nn">timer</span><span class="p">:</span><span class="nf">apply_after</span><span class="p">(</span><span class="mi">2000</span><span class="p">,</span> <span class="n">test</span><span class="p">,</span> <span class="n">print_age</span><span class="p">,</span> <span class="p">[</span><span class="mi">20</span><span class="p">]).</span></span></span></code></pre></div></div>
<p>If you try compiling the above module, you&rsquo;ll get an error like this:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">&gt; c(test).
test.erl:4: Warning: function print_age/1 is unused
{ok,test}</code></pre></div>
<p>Or if you have Dialyzer configured, you&rsquo;ll see a very similar error:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">test.erl:4: function print_age/1 is unused</code></pre></div>
<p>But, but&hellip; we know this function <em>will</em> be used! How do we get rid of the error?</p>

<h2 class="relative group">Solution #1 (meh - suppressing the warning)
    <div id="solution-1-meh---suppressing-the-warning" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#solution-1-meh---suppressing-the-warning" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>You can <a href="http://erlang.org/doc/man/dialyzer.html#suppression"  target="_blank" rel="noreferrer">suppress warnings</a> such as this one with a <a href="http://erlang.org/doc/man/compile.html"  target="_blank" rel="noreferrer">compiler option</a>. The following will make the compiler silent about the unused function. And in certain cases, that&rsquo;s what you need to do&hellip; but not in this case.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">compile</span><span class="p">({</span><span class="n">nowarn_unused_function</span><span class="p">,</span> <span class="p">{</span><span class="n">print_age</span><span class="p">,</span><span class="mi">1</span><span class="p">}}).</span></span></span></code></pre></div></div>
<p>Oddly, the following seems like it should work similar to the above, since I&rsquo;m using Dialyzer and <a href="http://erlang.org/doc/man/dialyzer.html#suppression"  target="_blank" rel="noreferrer">Dialyzer has its own options</a>, but it had no effect. 🤷‍♂</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">dialyzer</span><span class="p">({</span><span class="n">no_unused</span><span class="p">,</span> <span class="p">[</span><span class="n">print_age</span><span class="o">/</span><span class="mi">1</span><span class="p">]}).</span></span></span></code></pre></div></div>
<p>If you compile the file again and then call <code>main()</code> it&rsquo;ll wait 2 seconds and print an error message instead of the age:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">=ERROR REPORT==== 6-Jun-2018::13:37:14 ===
Error in process &lt;0.89.0&gt; with exit value:
{undef,[{test,print_age,[20],[]}]}</code></pre></div>

<h2 class="relative group">Solution #2 (better - exporting the function)
    <div id="solution-2-better---exporting-the-function" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#solution-2-better---exporting-the-function" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There&rsquo;s nothing in the <a href="http://erlang.org/doc/man/timer.html"  target="_blank" rel="noreferrer">timer docs</a> saying the function you&rsquo;re calling has to be exported, but it does, so export the function that you want the timer to call:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">module</span><span class="p">(</span><span class="n">test</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">export</span><span class="p">([</span><span class="n">main</span><span class="o">/</span><span class="mi">0</span><span class="p">,</span><span class="n">print_age</span><span class="o">/</span><span class="mi">1</span><span class="p">]).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">print_age</span><span class="p">(</span><span class="nv">Age</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nn">io</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34;Your age: </span><span class="si">~p~n</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[</span><span class="nv">Age</span><span class="p">]).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">main</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nn">timer</span><span class="p">:</span><span class="nf">apply_after</span><span class="p">(</span><span class="mi">2000</span><span class="p">,</span> <span class="n">test</span><span class="p">,</span> <span class="n">some_func</span><span class="p">,</span> <span class="p">[</span><span class="mi">20</span><span class="p">]).</span></span></span></code></pre></div></div>
<p>Now when you compile and run it, you&rsquo;ll see the age printed:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">Your age: 20</code></pre></div>

<h2 class="relative group">What Happened?
    <div id="what-happened" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-happened" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Why does this happen? I&rsquo;m no expert, but <a href="https://stackoverflow.com/a/25056598/301857"  target="_blank" rel="noreferrer">here&rsquo;s a post</a> that suggests the timer is running in a <code>gen_server</code> in a separate process.</p>
<blockquote><p>The timer module is a standard gen_server running in a separate process. All the function in the timer module are public interfaces that execute a hidden gen_server:call or gen_server:cast to the timer server. This is a common usage to hide the internal of a server and allow further evolutions without impact on existing applications.</p>
</blockquote><p>You can read more about the <a href="http://erlang.org/doc/man/gen_server.html"  target="_blank" rel="noreferrer">gen_server</a> here, but think of it this way. After passing a function to the <code>timer</code> module, two things need to be able to happen:</p>
<ul>
<li>The rest of your codebase has to continue running, so it can&rsquo;t wait at the timer.</li>
<li>Your function call has to be stored somewhere until it&rsquo;s ready to execute (after the delay you specify).</li>
</ul>
<p>And where it&rsquo;s stored is in a separate process with its own modules and functions, out of the way of the current process that needs to keep running. But in order for the new process to access the original function you specified, that function <em>must</em> be exported. One module can&rsquo;t access a function in another module unless it&rsquo;s exported.</p>
<p>Unfortunately, that&rsquo;s a little messy since you may not want other modules to be able to call that function (which is possible once it&rsquo;s exported), but the only way I see around that is to <a href="http://erlang.org/doc/man/edoc.html"  target="_blank" rel="noreferrer">leave good documentation</a> on your code.</p>
]]></content:encoded><media:content url="https://grantwinney.com/getting-rid-of-unused-function-errors-when-using-timers-in-erlang/feature.webp" medium="image" type="image/webp"/></item><item><title>Tweet random blog posts from an RSS feed using AWS Lambda</title><link>https://grantwinney.com/using-aws-lambda-to-tweet-random-posts-from-an-rss-feed/</link><pubDate>Sat, 02 Jun 2018 12:32:54 +0000</pubDate><guid>https://grantwinney.com/using-aws-lambda-to-tweet-random-posts-from-an-rss-feed/</guid><description>If you&amp;rsquo;ve got a Twitter account, and a blog with a lot of content, sharing your posts can be a nice way to help someone out, and drive a little extra traffic to your site. If your site generates an RSS feed, here&amp;rsquo;s how you can automate the process - for free!</description><content:encoded><![CDATA[<p>Following on the heels of writing a small app to <a href="https://grantwinney.com/using-aws-lambda-and-tweetinvi-to-tweet-a-random-ghost-blog-post/"  target="_blank" rel="noreferrer">tweet random posts from a Ghost blog using AWS Lambda</a>, I thought I&rsquo;d write a similar implementation that operated on an RSS feed instead of specifically just Ghost.</p>
<p>Manually selecting and sharing your blog posts on Twitter is time consuming, and using a third-party service could get costly and/or lack flexibility in selecting older posts. What if you could have the best of both worlds, and automate posting random blog posts for free? Thanks to some great OSS libraries, and AWS Lambda (which has a generous <a href="https://aws.amazon.com/lambda/pricing/#Lambda_pricing_details"  target="_blank" rel="noreferrer">free tier plan</a>), you can.</p>
<p>I&rsquo;ll explain a little more about how I did it, but here&rsquo;s the tl;dr if you just want to try it out. I get it - sometimes the best way to learn something is to dig in and get your hands dirty as soon as possible.</p>

<h2 class="relative group">Usage
    <div id="usage" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#usage" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>This is a C# console app that runs in AWS Lambda. Schedule it to run as often as you like using Lambda&rsquo;s <a href="https://docs.aws.amazon.com/lambda/latest/dg/tutorial-scheduled-events-schedule-expressions.html"  target="_blank" rel="noreferrer">cron scheduling</a> capabilities.</p>

<h3 class="relative group">Clean up existing tags
    <div id="clean-up-existing-tags" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#clean-up-existing-tags" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Before you do anything else, you might want to clean up tags/categories if they&rsquo;re included in your RSS feed. Here&rsquo;s what I&rsquo;d recommend (or you can leave them as-is and modify my code to handle them).</p>
<ul>
<li>Remove any leading <code>#</code> from tags, since the app prepends a <code>#</code> to each tag.</li>
<li>If you have any characters in tags that won&rsquo;t translate well to Twitter hashtags, either remove them or add them to <code>var pattern = new Regex(&quot;[- ]&quot;);</code> in the code so they get removed before posting the tweet.</li>
<li>This is a good time to revisit <em>all</em> your tags and just remove/rename ones that won&rsquo;t look good in Twitter.</li>
<li>While you&rsquo;re at it, you might want to revisit your posts as well - anything you forgot about that you&rsquo;d rather not post to Twitter?</li>
</ul>
<p>Other stuff to think about:</p>
<ul>
<li>You can leave spaces and hyphens - the app will remove them.</li>
<li>You can leave leading numbers, since Twitter seems to handle those just fine.</li>
<li>Any existing <code>#</code> will be replaced with <code>sharp</code> (c# » csharp), and <code>.</code> with <code>dot</code> (.net » dotnet).</li>
</ul>

<h3 class="relative group">Grab the code
    <div id="grab-the-code" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#grab-the-code" aria-label="Anchor">#</a>
    </span>
    
</h3>
<ol>
<li>Clone the repo: <code>https://github.com/grantwinney/BlogCodeSamples</code></li>
<li>Find the project under &ldquo;Misc/TweetRandomFeedItem&rdquo; and build it, either in Visual Studio or at the command line.</li>
<li>Find the <code>bin</code> directory on disk, and drill down until you get to the assemblies (dll files), most likely in <code>bin/Debug/netcoreapp2.0</code></li>
<li>Select all the files inside <code>netcoreapp2.0</code> (but not the directory itself) and zip them up. You&rsquo;ll be uploading these to an AWS Lambda function.</li>
</ol>

<h3 class="relative group">Create a Twitter app
    <div id="create-a-twitter-app" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#create-a-twitter-app" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>You&rsquo;ll need to <a href="https://apps.twitter.com/"  target="_blank" rel="noreferrer">register a new app with Twitter</a> (an app with one user - you!). The name of your app doesn&rsquo;t matter, but it does have to be unique system-wide (not just unique in your account).</p>
<p>To get the values you need for the app to post to Twitter, generate an &ldquo;access&rdquo; token under the &ldquo;Keys and Access Tokens&rdquo; section, and note these four pieces of data: <em>Consumer Key, Consumer Secret, Access Token,</em> and <em>Access Token Secret</em></p>

<h3 class="relative group">Setup AWS Lambda
    <div id="setup-aws-lambda" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#setup-aws-lambda" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Now you need to create a new Lambda function. Here&rsquo;s a brief <a href="https://vickylai.com/verbose/free-twitter-bot-aws-lambda/#setting-up-aws-lambda"  target="_blank" rel="noreferrer">intro to setting up AWS Lambda</a>, which you may want to check out. Once you&rsquo;re signed up, continue on&hellip;</p>
<ol>
<li>Create a new function (author from scratch) and choose <code>C# (.NET Core 2.0)</code> for the runtime.</li>
<li>The name of the function, and the role it makes you create, don&rsquo;t matter.</li>
<li>Upload the zip file you previously created.</li>
<li>Set the handler as <code>TweetRandomFeedItem::TweetRandomFeedItem.Program::Main</code></li>
</ol>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="function-code"
    src="/using-aws-lambda-to-tweet-random-posts-from-an-rss-feed/function-code.png"
    width="1918"
      height="534"></figure>
<ol>
<li>Under &ldquo;Basic settings&rdquo;, decrease the memory to 128MB and increase the timeout to a minute. For me, it generally takes about 15-20 seconds to run, and uses 50MB or less of memory.</li>
</ol>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="basic-settings"
    src="/using-aws-lambda-to-tweet-random-posts-from-an-rss-feed/basic-settings.png"
    width="1920"
      height="772"></figure>

<h3 class="relative group">Create the environment variables
    <div id="create-the-environment-variables" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#create-the-environment-variables" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>I use quite a few <a href="https://docs.aws.amazon.com/lambda/latest/dg/env_variables.html"  target="_blank" rel="noreferrer">environment variables</a>, so credentials and other settings can easily be changed between runs, without having to recompile the code and upload it again.</p>
<table>
  <thead>
      <tr>
          <th>Field</th>
          <th>Required</th>
          <th>Description</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>RSS_URI</code></td>
          <td>Yes</td>
          <td>The RSS feed to read in, like: <code>https://grantwinney.com/rss/</code></td>
      </tr>
      <tr>
          <td><code>RSS_ITEM_RETRIEVAL_LIMIT</code></td>
          <td>No</td>
          <td>Specify the number of posts you want to retrieve.<br><br>If you omit this, or leave the value empty, the app will read the entire RSS feed.</td>
      </tr>
      <tr>
          <td><code>TWITTER_CONSUMER_KEY</code>  <br><code>TWITTER_CONSUMER_SECRET</code>  <br><code>TWITTER_USER_ACCESS_TOKEN</code>  <br><code>TWITTER_USER_ACCESS_TOKEN_SECRET</code></td>
          <td>Yes</td>
          <td>These values all come from your Twitter account. You need to create a new app to get these values, which allows you to post tweets.</td>
      </tr>
      <tr>
          <td><code>TWITTER_MAX_TAG_COUNT</code></td>
          <td>No</td>
          <td>Although tag spamming is somewhat prevalent on Facebook, and even more-so on Instagram, I don&rsquo;t see a lot of it on Twitter. If you tend to use a lot of tags for posts on your blog, then you can limit how many of those transfer to your tweet.<br><br>If you omit this, or leave the value empty, the app will use the first 3 tags.</td>
      </tr>
  </tbody>
</table>
<p>When you&rsquo;re done, it should look something like this:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="env-vars-aws-lambda"
    src="/using-aws-lambda-to-tweet-random-posts-from-an-rss-feed/env-vars-aws-lambda.jpg"
    width="2536"
      height="978"></figure>

<h3 class="relative group">Take it out for a spin!
    <div id="take-it-out-for-a-spin" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#take-it-out-for-a-spin" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>That <em>should</em> be everything you need to run the job. To try it out, hit the &ldquo;<strong>Test</strong>&rdquo; button at the top of the screen. It might have you configure a new &ldquo;test event&rdquo;. Just do it, name it whatever, and change the code to an empty set of curly braces like <code>{}</code>. Hopefully everything goes smoothly and you get a screen like this one.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="successful-lambda-run"
    src="/using-aws-lambda-to-tweet-random-posts-from-an-rss-feed/successful-lambda-run.jpg"
    width="1000"
      height="416"></figure>
<p>Check your Twitter feed - did it post something from your RSS feed? My feed only returns the most recent 15 posts, but the app did pick one at random, and tweeted it:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-aws-lambda-to-tweet-random-posts-from-an-rss-feed/successful-tweet.png"
    width="1187"
      height="560"></figure>

<h3 class="relative group">Schedule it
    <div id="schedule-it" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#schedule-it" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>If you&rsquo;re ready to let it do your work for you, schedule it to run via cron.</p>
<ul>
<li>Select &ldquo;CloudWatch Events&rdquo; under &ldquo;Add Trigger&rdquo; in the Lambda configuration screen, and a new &ldquo;Configure triggers&rdquo; panel appears just below it.</li>
<li>Select &ldquo;Create a new rule&rdquo; from the drop-down and give the new rule some random name.</li>
<li>Enter a cron command in the &ldquo;Schedule expression&rdquo; box, such as <code>cron(0 12 * * ? *)</code> to run your job at 12 UTC every day.</li>
</ul>
<hr>

<h2 class="relative group">The Stack
    <div id="the-stack" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-stack" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>This project makes use of some nice OSS libraries. Oh, and there&rsquo;s even one that&rsquo;s mine, albeit it&rsquo;s not as finished as I&rsquo;d like.</p>

<h3 class="relative group">Tweetinvi
    <div id="tweetinvi" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#tweetinvi" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p><a href="https://developer.twitter.com/en/docs/developer-utilities/twitter-libraries.html"  target="_blank" rel="noreferrer">Twitter has a page that references libraries in different languages for their API</a>, including a handful for C#. Unfortunately, <a href="https://stackoverflow.com/q/6705087/301857"  target="_blank" rel="noreferrer">TweetSharp is dead</a>. Fortunately, <a href="https://github.com/JoeMayo/LinqToTwitter"  target="_blank" rel="noreferrer">LinqToTwitter</a> and <a href="https://github.com/linvi/tweetinvi"  target="_blank" rel="noreferrer">Tweetinvi</a> are both alive and kicking.</p>
<p>I looked at each, but they&rsquo;ve both been recently updated, and each have a some open issues but a lot more closed ones. Someone&rsquo;s working on them, which is good! Both <a href="https://github.com/linvi/tweetinvi/wiki/Introduction#compatibility"  target="_blank" rel="noreferrer">Tweetinvi</a> and <a href="https://www.nuget.org/packages/linqtotwitter"  target="_blank" rel="noreferrer">LinqToTwitter</a> support .NET Core 2.0 too, which is required since <a href="https://visualstudiomagazine.com/articles/2018/01/17/aws-lambda-net-core.aspx"  target="_blank" rel="noreferrer">AWS Lambda runs on the .NET Core 2.0 runtime</a>.</p>
<p>After spending 15 minutes checking the two out, I decided to just go with Tweetinvi. I&rsquo;m glad I did. It proved extremely easy to implement! I spent a couple evenings trying to write a my own library awhile back, but it&rsquo;s a complex task to tackle. I&rsquo;m glad someone already did the work.</p>

<h3 class="relative group">SyndicationFeedReaderWriter
    <div id="syndicationfeedreaderwriter" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#syndicationfeedreaderwriter" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>I implemented a <a href="https://github.com/dotnet/SyndicationFeedReaderWriter"  target="_blank" rel="noreferrer">SyndicationFeedReaderWriter</a> (available via <a href="https://www.nuget.org/packages/Microsoft.SyndicationFeed.ReaderWriter/"  target="_blank" rel="noreferrer">NuGet</a>), which worked well. I did a little cleanup of the tags to remove illegal characters; nothing too crazy though. There&rsquo;s debate about <a href="https://stackoverflow.com/q/36895543/301857"  target="_blank" rel="noreferrer">which characters</a> are <a href="https://stackoverflow.com/q/14823376/301857"  target="_blank" rel="noreferrer">allowed in hashtags</a>, so I decided to focus on <em>my</em> tags. YMMV, and you may have to adjust the code accordingly.</p>

<h3 class="relative group">AWS Lambda
    <div id="aws-lambda" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#aws-lambda" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>I&rsquo;m only just starting out with AWS Lambda, so I&rsquo;m not sure what all it&rsquo;s capable of yet. A few days ago I created a function that keeps my personal Twitter timeline clean.. so far, it&rsquo;s awesome. Their <a href="https://aws.amazon.com/lambda/pricing/#Lambda_pricing_details"  target="_blank" rel="noreferrer">free tier plan</a> is generous enough to let you try out <em>lots</em> of different things before you need to pay a single penny.</p>
<hr>

<h2 class="relative group">Thoughts? Comments?
    <div id="thoughts-comments" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#thoughts-comments" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Let me know how it goes, what you think of it, and whether you have any problems!</p>
]]></content:encoded><media:content url="https://grantwinney.com/using-aws-lambda-to-tweet-random-posts-from-an-rss-feed/feature.webp" medium="image" type="image/webp"/></item><item><title>Tweet random posts from a Ghost blog using AWS Lambda</title><link>https://grantwinney.com/using-aws-lambda-and-tweetinvi-to-tweet-a-random-ghost-blog-post/</link><pubDate>Fri, 01 Jun 2018 17:28:01 +0000</pubDate><guid>https://grantwinney.com/using-aws-lambda-and-tweetinvi-to-tweet-a-random-ghost-blog-post/</guid><description>If you&amp;rsquo;ve got a Twitter account, and a blog with a lot of content, sharing your posts can be a nice way to help someone out, and drive a little extra traffic to your site. Here&amp;rsquo;s how you can automate the process on your Ghost blog - for free!</description><content:encoded><![CDATA[<p>If you&rsquo;ve got a Twitter (now X) account, and a blog with a lot of content, sharing your posts from time to time can be a nice way to drive a little extra traffic to your site. Plus, sharing your experience and knowledge could very well help someone out who would not have found your post otherwise.</p>
<p>Manually selecting and sharing posts is time consuming, and using a third-party service could get costly and/or lack flexibility in selecting older posts. What if you could have the best of both worlds, and automate posting random blog posts for free? Thanks to some great OSS libraries, and AWS Lambda (which has a generous <a href="https://aws.amazon.com/lambda/pricing/#Lambda_pricing_details"  target="_blank" rel="noreferrer">free tier plan</a>), you can.</p>
<p>I&rsquo;ll explain a little more about how I did it, but here&rsquo;s the tl;dr if you just want to try it out. I get it - sometimes the best way to learn something is to dig in and get your hands dirty as soon as possible.</p>

<h2 class="relative group">Usage
    <div id="usage" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#usage" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>This is a C# console app that runs in AWS Lambda. You can schedule it to run as often as you like. Maybe once or twice a day to start.</p>

<h3 class="relative group">Clean up existing tags
    <div id="clean-up-existing-tags" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#clean-up-existing-tags" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Before you do anything else, you might want to revisit your existing tags (under <code>settings/tags</code>) and clean certain things up. Here&rsquo;s what I&rsquo;d recommend (or you can leave them as-is and modify my code to handle them).</p>
<ul>
<li>Remove any leading <code>#</code> from tags, since the app prepends a <code>#</code> to each tag.</li>
<li>If you have any characters in tags that won&rsquo;t translate well to Twitter hashtags, either remove them in Ghost, or add them to <code>var pattern = new Regex(&quot;[- ]&quot;);</code> in my project so they get removed before posting the tweet.</li>
<li>This is a good time to revisit <em>all</em> your tags and just remove/rename ones that won&rsquo;t look good in Twitter.</li>
<li>While you&rsquo;re at it, you might want to revisit your posts as well - anything you forgot about that you&rsquo;d rather not post to Twitter?</li>
</ul>
<p>Other stuff to think about:</p>
<ul>
<li>You can leave spaces and hyphens - the app will remove them.</li>
<li>You can leave leading numbers, since Twitter seems to handle those just fine.</li>
<li>Any <code>#</code> will be replaced with <code>sharp</code> (c# » csharp), and <code>.</code> with <code>dot</code> (.net » dotnet).</li>
</ul>

<h3 class="relative group">Get the code and compile it
    <div id="get-the-code-and-compile-it" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#get-the-code-and-compile-it" aria-label="Anchor">#</a>
    </span>
    
</h3>
<ol>
<li>Clone the repo: <code>https://github.com/grantwinney/BlogCodeSamples</code></li>
<li>Find the project under &ldquo;Misc/TweetRandomPostFromAGhostBlog&rdquo; and build it, either in Visual Studio or at the command line.</li>
<li>Find the <code>bin</code> directory on disk, and drill down until you get to the assemblies (dll files), most likely in <code>bin/Debug/netcoreapp3.1</code></li>
<li>Select all the files inside <code>netcoreapp3.1</code> (but not the directory itself) and zip them up. You&rsquo;ll be uploading these to an AWS Lambda function later.</li>
</ol>

<h3 class="relative group">Create a Twitter app
    <div id="create-a-twitter-app" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#create-a-twitter-app" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p><a href="https://developer.twitter.com/en/apps"  target="_blank" rel="noreferrer">Register a new app with Twitter</a>&hellip; yeah, it&rsquo;ll be an app with one user. You&rsquo;ll get a prompt to &ldquo;please apply for a Twitter developer account&rdquo;, so pick &ldquo;something else&rdquo; as the reason, share how you&rsquo;ll use it, deselect everything but <em>&ldquo;Will your app use Tweet &hellip; functionality?&rdquo;,</em> and paste something similar in again for that field.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-aws-lambda-and-tweetinvi-to-tweet-a-random-ghost-blog-post/twitter-app-purpose.png"
    width="559"
      height="861"></figure>
<p>When you get the email for your new developer account (mine was nearly immediate), click the link and give your app a unique name. I just used <a href="https://www.passwordrandom.com/query?command=guid"  target="_blank" rel="noreferrer">a random GUID</a> - the name doesn&rsquo;t really matter. I think it creates the app outside of a project. If it does, delete it, create a project, and then create the app inside that. Note the API key and secret for your app - you&rsquo;ll need those in a bit.</p>
<p>Under &ldquo;App permissions&rdquo;, change the default &ldquo;read&rdquo; permissions to &ldquo;read and write&rdquo;, since the app needs to be able to send tweets and not just read them. Then click back into your new app and press &ldquo;Generate&rdquo; under the &ldquo;Access token &amp; secret&rdquo; section, and note the additional access token and secret that are generated - you&rsquo;ll need those in a bit too! <em>(Make sure it says &ldquo;created with read and write permissions&rdquo; underneath it.)</em></p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-aws-lambda-and-tweetinvi-to-tweet-a-random-ghost-blog-post/image-4.png"
    width="1223"
      height="809"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-aws-lambda-and-tweetinvi-to-tweet-a-random-ghost-blog-post/image-10.png"
    width="818"
      height="555"></figure>

<h3 class="relative group">Setup AWS Lambda
    <div id="setup-aws-lambda" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#setup-aws-lambda" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Open the <a href="https://aws.amazon.com/free"  target="_blank" rel="noreferrer">AWS Free Tier</a> page and create an account if you don&rsquo;t already have one. Then open the Products dropdown and look for AWS Lambda, or <a href="https://aws.amazon.com/lambda"  target="_blank" rel="noreferrer">just go here</a>. Click the button in middle of the page, leave &ldquo;root&rdquo; selected, and enter your credentials. You should end up at <a href="https://console.aws.amazon.com/lambda"  target="_blank" rel="noreferrer">console.aws.amazon.com/lambda</a>, on a screen like this:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-aws-lambda-and-tweetinvi-to-tweet-a-random-ghost-blog-post/image-2.png"
    width="1408"
      height="503"></figure>
<p>Now you need to create a new Lambda function.</p>
<ol>
<li>Create a new function (author from scratch) and choose <code>C# (.NET Core 3.1)</code> for the runtime.</li>
<li>The name of the function doesn&rsquo;t matter, nor does the role it makes you create.</li>
<li>Under &ldquo;Function code&rdquo;, upload the zip file you previously created.</li>
<li>Under &ldquo;Runtime settings&rdquo;, change the handler to: <code>TweetRandomFeedItem::TweetRandomFeedItem.Program::Main</code></li>
</ol>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-aws-lambda-and-tweetinvi-to-tweet-a-random-ghost-blog-post/image-5.png"
    width="1110"
      height="218"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-aws-lambda-and-tweetinvi-to-tweet-a-random-ghost-blog-post/image-6.png"
    width="1109"
      height="188"></figure>
<p>Under &ldquo;Basic settings&rdquo;, decrease the memory to 128MB and increase the timeout to a minute. For me, it generally takes about 15-20 seconds to run, and uses 50MB or less of memory.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-aws-lambda-and-tweetinvi-to-tweet-a-random-ghost-blog-post/image-7.png"
    width="849"
      height="296"></figure>

<h3 class="relative group">Create the environment variables
    <div id="create-the-environment-variables" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#create-the-environment-variables" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>I make heavy use of <a href="https://docs.aws.amazon.com/lambda/latest/dg/env_variables.html"  target="_blank" rel="noreferrer">environment variables</a>, so that credentials and other settings can easily be changed without having to recompile the code and upload it again. Here&rsquo;s a description of each field you can set.</p>
<p><strong>Required</strong></p>
<p>Go to the settings page for your blog, click Integrations, and then Add Custom Integration. Provide a name, and then note the values on the next page.</p>
<ul>
<li><code>API_URL</code> - Set this to the API URL you get above, so the app can find your blog.</li>
<li><code>ADMIN_API_KEY</code> - Set this to the Admin API Key you get above, to authenticate with your blog.</li>
</ul>
<p>These values all come from your Twitter account, from the app you created before.</p>
<ul>
<li><code>TWITTER_CONSUMER_KEY</code> / <code>TWITTER_CONSUMER_SECRET</code> - Set these to the values for API key &amp; secret under the &ldquo;Consumer Keys&rdquo; header after you create an app.</li>
<li><code>TWITTER_USER_ACCESS_TOKEN</code> / <code>TWITTER_USER_ACCESS_TOKEN_SECRET</code> - Set these to the Access token &amp; secret you created separately under &ldquo;Authentication Tokens&rdquo;.</li>
</ul>
<p><strong>Optional</strong></p>
<ul>
<li><code>POST_RETRIEVAL_LIMIT</code> - The number of posts to retrieve, from which a random post will be selected. (defaults to 9999999, basically &ldquo;all posts&rdquo;)</li>
<li><code>TAGS_TO_TWEET</code> - A comma-delimited list of tags (no spaces after each comma) to consider when selecting a random post. (defaults to empty string, which means tags are ignored)</li>
<li><code>TAGS_TO_REMOVE</code> - A comma-delimited list of tags (again, no spaces after commas) to not include in the tweet message. This doesn&rsquo;t affect whether the post is selected, just how it looks in a tweet. (defaults to empty string too)</li>
<li><code>TWITTER_MAX_TAG_COUNT</code> - If you tend to use a lot of tags for blog posts, this limits how many are used in your tweet to the first <code>n</code>. (defaults to 3)</li>
<li><code>FACTOR_IN_AGE_OF_POST</code> - Set to &ldquo;true&rdquo; to make it more likely that a newer post will be selected than an older. (defaults to &ldquo;false&rdquo;)</li>
</ul>
<p>When you&rsquo;re done, it should look something like this:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-aws-lambda-and-tweetinvi-to-tweet-a-random-ghost-blog-post/image-8.png"
    width="1074"
      height="683"></figure>

<h3 class="relative group">Take it out for a spin!
    <div id="take-it-out-for-a-spin" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#take-it-out-for-a-spin" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>That <em>should</em> be everything you need to run the job. To try it out, hit the &ldquo;<strong>Test</strong>&rdquo; button in the top-right corner of the screen. It might have you configure a new &ldquo;test event&rdquo;. Just do it, name it whatever, and change the code to an empty set of curly braces like <code>{}</code>.</p>
<p>If it seems to have done nothing, press &ldquo;Test&rdquo; again. Hopefully everything goes smoothly and you get a screen like this one. Check your Twitter feed - did it post one of your posts from your Ghost blog?</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="successful-lambda-run"
    src="/using-aws-lambda-and-tweetinvi-to-tweet-a-random-ghost-blog-post/successful-lambda-run.jpg"
    width="2530"
      height="1052"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-aws-lambda-and-tweetinvi-to-tweet-a-random-ghost-blog-post/image-11.png"
    width="771"
      height="481"></figure>

<h3 class="relative group">Schedule it
    <div id="schedule-it" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#schedule-it" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>If you&rsquo;re ready to let it do your work for you, schedule it to run via cron.</p>
<ul>
<li>Select &ldquo;EventBridge (CloudWatch Events)&rdquo; under &ldquo;Add Trigger&rdquo; in the Lambda configuration screen, and a new &ldquo;Configure triggers&rdquo; panel appears just below it.</li>
<li>Select &ldquo;Create a new rule&rdquo; from the drop-down and give the new rule some random name.</li>
<li>Enter a cron command in the &ldquo;Schedule expression&rdquo; box, such as <code>cron(0 12 * * ? *)</code> to run your job at 12 UTC every day. You can find more help in their developer guide: <a href="https://docs.aws.amazon.com/lambda/latest/dg/services-cloudwatchevents-expressions.html"  target="_blank" rel="noreferrer">Schedule expressions using rate or cron</a></li>
</ul>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/using-aws-lambda-and-tweetinvi-to-tweet-a-random-ghost-blog-post/image-12.png"
    width="923"
      height="959"></figure>
<hr>

<h2 class="relative group">The Stack
    <div id="the-stack" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-stack" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>This project makes use of some nice OSS libraries. Oh, and there&rsquo;s even one that&rsquo;s mine, albeit it&rsquo;s not as finished as I&rsquo;d like.</p>

<h3 class="relative group">Tweetinvi
    <div id="tweetinvi" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#tweetinvi" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p><a href="https://developer.twitter.com/en/docs/developer-utilities/twitter-libraries.html"  target="_blank" rel="noreferrer">Twitter has a page that references libraries in different languages for their API</a>, including a handful for C#. Unfortunately, <a href="https://web.archive.org/web/20240927005346/https://stackoverflow.com/questions/6705087/where-is-tweetsharp"  target="_blank" rel="noreferrer">TweetSharp is dead</a>. Fortunately, <a href="https://github.com/JoeMayo/LinqToTwitter"  target="_blank" rel="noreferrer">LinqToTwitter</a> and <a href="https://github.com/linvi/tweetinvi"  target="_blank" rel="noreferrer">Tweetinvi</a> are both alive and kicking.</p>
<p>I looked at each, but they&rsquo;ve both been recently updated, and each have a some open issues but a lot more closed ones. Someone&rsquo;s working on them, which is good! Both <a href="https://github.com/linvi/tweetinvi/wiki/Introduction#compatibility"  target="_blank" rel="noreferrer">Tweetinvi</a> and <a href="https://www.nuget.org/packages/linqtotwitter"  target="_blank" rel="noreferrer">LinqToTwitter</a> support .NET Core 2.0 too, which is required since <a href="https://visualstudiomagazine.com/articles/2018/01/17/aws-lambda-net-core.aspx"  target="_blank" rel="noreferrer">AWS Lambda runs on the .NET Core 2.0 runtime</a>.</p>
<p>After spending 15 minutes checking the two out, I decided to just go with Tweetinvi. I&rsquo;m glad I did. It proved extremely easy to implement! I spent a couple evenings trying to write a my own library awhile back, but it&rsquo;s a complex task to tackle. I&rsquo;m glad someone already did the work.</p>

<h3 class="relative group">GhostSharp
    <div id="ghostsharp" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#ghostsharp" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p><a href="https://grantwinney.com/ghostsharp/"  target="_blank" rel="noreferrer">GhostSharp</a> is a C# library I wrote that wraps the Ghost blog&rsquo;s v3 API, and allows you to programmatically manipulate your own Ghost blog site. You can modify posts and pages, upload images and themes, etc. It&rsquo;s available on <a href="https://www.nuget.org/packages/GhostSharp"  target="_blank" rel="noreferrer">NuGet</a> if you&rsquo;re interested.</p>

<h3 class="relative group">AWS Lambda
    <div id="aws-lambda" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#aws-lambda" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>I&rsquo;m only just starting out with AWS Lambda, so I&rsquo;m not sure what all it&rsquo;s capable of yet. A few days ago I created a function that keeps my personal Twitter timeline clean.. so far, it&rsquo;s awesome. Their <a href="https://aws.amazon.com/lambda/pricing/#Lambda_pricing_details"  target="_blank" rel="noreferrer">free tier plan</a> is generous enough to let you try out <em>lots</em> of different things before you need to pay a single penny.</p>
<hr>

<h2 class="relative group">Thoughts? Comments?
    <div id="thoughts-comments" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#thoughts-comments" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Let me know how it goes, what you think of it, and whether you have any problems! This project is tightly coupled to the Ghost platform, but I also wrote a slightly more generic version, to <a href="https://grantwinney.com/using-aws-lambda-to-tweet-random-posts-from-an-rss-feed/"  target="_blank" rel="noreferrer">use your blog&rsquo;s RSS feed to randomly tweet</a>.</p>
]]></content:encoded><media:content url="https://grantwinney.com/using-aws-lambda-and-tweetinvi-to-tweet-a-random-ghost-blog-post/feature.webp" medium="image" type="image/webp"/></item><item><title>Are property accessors possible in Erlang records?</title><link>https://grantwinney.com/are-property-accessors-possible-in-erlang/</link><pubDate>Tue, 22 May 2018 16:55:18 +0000</pubDate><guid>https://grantwinney.com/are-property-accessors-possible-in-erlang/</guid><description>I ran into a problem in Erlang yesterday that made me think&amp;hellip; is there anyway to implement a property accessor on a record?</description><content:encoded><![CDATA[<p>I was tackling a new requirement the other day, which needed a new record. One of the fields is a list of items, while another happens to represent a count of those items - not that the consumer of the record would necessarily be aware of that relationship.</p>
<p>It would&rsquo;ve been convenient to be able to define a record like this, where as soon as a <code>class</code> was created and a list of <code>person</code> records assigned to it, the <code>number_of_students</code> was somehow automatically set to the length of the <code>students</code> list.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">record</span><span class="p">(</span><span class="nl">person</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">name</span> <span class="p">::</span> <span class="n">string</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">            <span class="n">grade</span> <span class="p">::</span> <span class="n">string</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="p">}).</span>
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">record</span><span class="p">(</span><span class="nl">class</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">subject</span> <span class="p">::</span> <span class="n">string</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">            <span class="n">professor</span> <span class="p">::</span> <span class="n">string</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">            <span class="n">students</span> <span class="p">::</span> <span class="p">[</span><span class="nl">#person</span><span class="p">{}],</span>
</span></span><span class="line"><span class="cl">            <span class="n">number_of_students</span> <span class="o">=</span> <span class="nb">length</span><span class="p">(</span><span class="n">students</span><span class="p">)</span> <span class="p">::</span> <span class="n">integer</span><span class="p">()</span>  <span class="c">% won&#39;t work
</span></span></span><span class="line"><span class="cl">        <span class="p">}).</span></span></span></code></pre></div></div>

<h2 class="relative group">What&rsquo;s a property accessor look like?
    <div id="whats-a-property-accessor-look-like" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whats-a-property-accessor-look-like" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you&rsquo;re unfamiliar with the concept of property accessors - maybe because Erlang is your first language - let&rsquo;s take a look at a couple examples.</p>
<p>C# has <a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties"  target="_blank" rel="noreferrer">property accessors</a>, like this one where the <code>Greeting</code> property concatenates a person&rsquo;s first and last names and prepends &ldquo;Hello&rdquo;, to form a greeting. Anytime a name is modified, the property will return the updated name the next time it&rsquo;s called.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">					
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Program</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="kd">public</span> <span class="kd">static</span> <span class="k">void</span> <span class="n">Main</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">	<span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="kt">var</span> <span class="n">p</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Person</span> <span class="p">{</span> <span class="n">FirstName</span> <span class="p">=</span> <span class="s">&#34;Jane&#34;</span><span class="p">,</span> <span class="n">LastName</span> <span class="p">=</span> <span class="s">&#34;Doe&#34;</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">		<span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">Greeting</span><span class="p">);</span>  <span class="c1">// &#34;Hello, Jane Doe!&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Person</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="kd">public</span> <span class="kt">string</span> <span class="n">FirstName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="kd">public</span> <span class="kt">string</span> <span class="n">LastName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">	
</span></span><span class="line"><span class="cl">	<span class="kd">public</span> <span class="kt">string</span> <span class="n">Greeting</span>
</span></span><span class="line"><span class="cl">	<span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="s">$&#34;Hello, {FirstName} {LastName}!&#34;</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>And here&rsquo;s <a href="https://www.sitepoint.com/properties-and-methods-in-ruby-from-a-net-pov/"  target="_blank" rel="noreferrer">similar functionality in Ruby</a>. Again, if either the first or last name is modified, calling <code>Greeting</code> will return a string with the updated names in it.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Person</span>
</span></span><span class="line"><span class="cl">  <span class="kp">attr_accessor</span> <span class="ss">:first_name</span>
</span></span><span class="line"><span class="cl">  <span class="kp">attr_accessor</span> <span class="ss">:last_name</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">first_name</span><span class="p">,</span> <span class="n">last_name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@first_name</span> <span class="o">=</span> <span class="n">first_name</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@last_name</span> <span class="o">=</span> <span class="n">last_name</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">Greeting</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;Hello, </span><span class="si">#{</span><span class="vi">@first_name</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="vi">@last_name</span><span class="si">}</span><span class="s2">!&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">person</span> <span class="o">=</span> <span class="no">Person</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">&#34;Jane&#34;</span><span class="p">,</span><span class="s2">&#34;Doe&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">puts</span> <span class="n">person</span><span class="o">.</span><span class="n">Greeting</span>  <span class="c1"># &#34;Hello, Jane Doe!&#34;</span></span></span></code></pre></div></div>

<h2 class="relative group">Is it even necessary in Erlang?
    <div id="is-it-even-necessary-in-erlang" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#is-it-even-necessary-in-erlang" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The strength of the property accessor, in my mind, is that it abstracts away some detail that someone else using it will not then have to worry about. There&rsquo;s a string that happens to return a greeting, but who cares what it&rsquo;s doing behind the scenes? If I update the person&rsquo;s name, the greeting changes - yay!</p>
<p>Abstracting away details when possible, in any language, is usually a good thing.</p>
<p>But in Erlang, everything is immutable. You can&rsquo;t change a field after it&rsquo;s been set - you can only return a whole new record. If someone didn&rsquo;t know that fact applied to records, I could understand why. Erlang happens to make it <em>appear</em> that updating a field is possible.</p>
<p>Let&rsquo;s say we have a function in an Erlang module that returns a <code>class</code> for us. We call the function to get a <code>class</code> record, &ldquo;modify&rdquo; a field (but not really), and lastly inspect the original reference again. I put modify in quotes because you&rsquo;re really just creating a new instance, and the original is still referenced by <code>ClassOne</code>.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="nf">c</span><span class="p">(</span><span class="n">sample</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="nf">rr</span><span class="p">(</span><span class="n">sample</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">% Get a class record
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">ClassOne</span> <span class="o">=</span> <span class="nn">sample</span><span class="p">:</span><span class="nf">get_class</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="c">% #class{subject = &#34;forensics&#34;, professor = &#34;dr moriarty&#34;,
</span></span></span><span class="line"><span class="cl"><span class="c">%        students = [#person{name = &#34;joe&#34;, grade = &#34;A&#34;},
</span></span></span><span class="line"><span class="cl"><span class="c">%                    #person{name = &#34;suzy&#34;, grade = &#34;B&#34;}]}
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">% Update one of the fields, which really just creates a new class record
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">ClassOne</span><span class="nl">#class</span> <span class="p">{</span><span class="n">subject</span> <span class="o">=</span> <span class="s">&#34;math&#34;</span><span class="p">}.</span>
</span></span><span class="line"><span class="cl"><span class="c">% #class{subject = &#34;math&#34;, professor = &#34;dr moriarty&#34;,
</span></span></span><span class="line"><span class="cl"><span class="c">%        students = [#person{name = &#34;joe&#34;, grade = &#34;A&#34;},
</span></span></span><span class="line"><span class="cl"><span class="c">%                    #person{name = &#34;suzy&#34;, grade = &#34;B&#34;}]}
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">ClassOne</span><span class="p">.</span>
</span></span><span class="line"><span class="cl"><span class="c">% #class{subject = &#34;forensics&#34;, professor = &#34;dr moriarty&#34;,
</span></span></span><span class="line"><span class="cl"><span class="c">%        students = [#person{name = &#34;joe&#34;, grade = &#34;A&#34;},
</span></span></span><span class="line"><span class="cl"><span class="err">%</span>                    <span class="nl">#person</span><span class="p">{</span><span class="n">name</span> <span class="o">=</span> <span class="s">&#34;suzy&#34;</span><span class="p">,</span> <span class="n">grade</span> <span class="o">=</span> <span class="s">&#34;B&#34;</span><span class="p">}]}</span></span></span></code></pre></div></div>
<p>The Erlang syntax makes it <em>look</em> as if you can update a single field in an existing record - something that would work as expected in C#, Ruby, or other languages - but it&rsquo;s actually creating a new record.</p>
<p>The nice thing about property accessors in other languages is that they appear to update automatically, returning a new value as a result of other fields being modified. But since fields in an Erlang record don&rsquo;t get updated, maybe there isn&rsquo;t a point.</p>
<p>Still&hellip; abstraction is a useful thing, so what <em>can</em> we do?</p>

<h2 class="relative group">Can we cobble something together?
    <div id="can-we-cobble-something-together" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#can-we-cobble-something-together" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>A record is really just <a href="http://learnyousomeerlang.com/a-short-visit-to-common-data-structures#records"  target="_blank" rel="noreferrer">syntactic sugar for a tuple</a>. Here are two representations of the same data. The first is formatted as a record, but under the covers it&rsquo;s a tuple with the name of the record first, followed by any data it contains.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="nl">#class</span><span class="p">{</span><span class="n">subject</span> <span class="o">=</span> <span class="s">&#34;forensics&#34;</span><span class="p">,</span> <span class="n">professor</span> <span class="o">=</span> <span class="s">&#34;dr moriarty&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">       <span class="n">students</span> <span class="o">=</span> <span class="p">[</span><span class="nl">#person</span><span class="p">{</span><span class="n">name</span> <span class="o">=</span> <span class="s">&#34;joe&#34;</span><span class="p">,</span> <span class="n">grade</span> <span class="o">=</span> <span class="s">&#34;A&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">                   <span class="nl">#person</span><span class="p">{</span><span class="n">name</span> <span class="o">=</span> <span class="s">&#34;suzy&#34;</span><span class="p">,</span> <span class="n">grade</span> <span class="o">=</span> <span class="s">&#34;B&#34;</span><span class="p">}]}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="n">class</span><span class="p">,</span> <span class="s">&#34;forensics&#34;</span><span class="p">,</span> <span class="s">&#34;dr moriarty&#34;</span><span class="p">,</span> <span class="p">[{</span><span class="n">person</span><span class="p">,</span> <span class="s">&#34;joe&#34;</span><span class="p">,</span> <span class="s">&#34;A&#34;</span><span class="p">},</span> <span class="p">{</span><span class="n">person</span><span class="p">,</span> <span class="s">&#34;suzy&#34;</span><span class="p">,</span> <span class="s">&#34;B&#34;</span><span class="p">}]}</span></span></span></code></pre></div></div>
<p>So we&rsquo;re somewhat limited. The only idea I could come up with was stuffing a function in one of the fields, like this. It accepts an instance of the record, and returns the length of the <code>students</code> list. I used <code>element</code> because you can&rsquo;t reference a record from within the record itself.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">record</span><span class="p">(</span><span class="nl">class</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">subject</span> <span class="p">::</span> <span class="n">string</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">            <span class="n">professor</span> <span class="p">::</span> <span class="n">string</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">            <span class="n">students</span> <span class="p">::</span> <span class="p">[</span><span class="nl">#person</span><span class="p">{}],</span>
</span></span><span class="line"><span class="cl">            <span class="n">number_of_students</span> <span class="o">=</span> <span class="k">fun</span><span class="p">(</span><span class="nv">Class</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">length</span><span class="p">(</span><span class="nb">element</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="nv">Class</span><span class="p">))</span> <span class="k">end</span> <span class="p">::</span> <span class="n">integer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="p">}).</span></span></span></code></pre></div></div>
<p>And that produces this beauty. 🤢</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="nv">ClassOne</span> <span class="o">=</span> <span class="nn">sample</span><span class="p">:</span><span class="nf">get_class</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nv">ClassOne</span><span class="nl">#class.number_of_students</span><span class="p">)(</span><span class="nv">ClassOne</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="err">%</span> <span class="n">returns</span> <span class="mi">2</span></span></span></code></pre></div></div>

<h2 class="relative group">What&rsquo;s the <em>right</em> thing to do?
    <div id="whats-the-right-thing-to-do" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whats-the-right-thing-to-do" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The only reasonable thing you can really do is create some &ldquo;helper&rdquo; functions that accept the class you&rsquo;re interested in, and return the information you&rsquo;re looking for.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">module</span><span class="p">(</span><span class="n">sample</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">export</span><span class="p">([</span><span class="n">get_class</span><span class="o">/</span><span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">         <span class="n">get_classroom_size</span><span class="o">/</span><span class="mi">1</span><span class="p">,</span> <span class="n">get_student_names</span><span class="o">/</span><span class="mi">1</span><span class="p">]).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">record</span><span class="p">(</span><span class="nl">person</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">name</span> <span class="p">::</span> <span class="n">string</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">            <span class="n">grade</span> <span class="p">::</span> <span class="n">string</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="p">}).</span>
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">record</span><span class="p">(</span><span class="nl">class</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">subject</span> <span class="p">::</span> <span class="n">string</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">            <span class="n">professor</span> <span class="p">::</span> <span class="n">string</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">            <span class="n">students</span> <span class="p">::</span> <span class="p">[</span><span class="nl">#person</span><span class="p">{}]</span>
</span></span><span class="line"><span class="cl">        <span class="p">}).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">get_class</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="nl">#class</span><span class="p">{}.</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_class</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nl">#class</span> <span class="p">{</span><span class="n">subject</span> <span class="o">=</span> <span class="s">&#34;science&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">professor</span> <span class="o">=</span> <span class="s">&#34;ms frizzle&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">students</span> <span class="o">=</span> <span class="p">[</span><span class="nl">#person</span> <span class="p">{</span> <span class="n">name</span> <span class="o">=</span> <span class="s">&#34;dorothy&#34;</span><span class="p">,</span> <span class="n">grade</span> <span class="o">=</span> <span class="s">&#34;A&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">                        <span class="nl">#person</span> <span class="p">{</span> <span class="n">name</span> <span class="o">=</span> <span class="s">&#34;arnold&#34;</span><span class="p">,</span> <span class="n">grade</span> <span class="o">=</span> <span class="s">&#34;B&#34;</span> <span class="p">}]}.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">get_classroom_size</span><span class="p">(</span><span class="nl">#class</span><span class="p">{})</span> <span class="o">-&gt;</span> <span class="n">integer</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_classroom_size</span><span class="p">(</span><span class="nv">Class</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">length</span><span class="p">(</span><span class="nv">Class</span><span class="nl">#class.students</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">get_student_names</span><span class="p">(</span><span class="nl">#class</span><span class="p">{})</span> <span class="o">-&gt;</span> <span class="n">string</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_student_names</span><span class="p">(</span><span class="nl">#class</span><span class="p">{</span><span class="n">students</span><span class="o">=</span><span class="nv">Students</span><span class="p">})</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nn">string</span><span class="p">:</span><span class="nf">join</span><span class="p">([</span><span class="nv">S</span><span class="nl">#person.name</span> <span class="p">||</span> <span class="nv">S</span> <span class="o">&lt;-</span> <span class="nv">Students</span><span class="p">],</span> <span class="s">&#34;, &#34;</span><span class="p">).</span></span></span></code></pre></div></div>
<p>Now you can pass an instance of your record into a helper function, and it&rsquo;ll extract the data you&rsquo;re interested in, and format it the way you&rsquo;d like. Similar to the property accessors, no one has to worry about what&rsquo;s going on inside the function.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="nf">c</span><span class="p">(</span><span class="n">sample</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="nf">rr</span><span class="p">(</span><span class="n">sample</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">ClassOne</span> <span class="o">=</span> <span class="nn">sample</span><span class="p">:</span><span class="nf">get_class</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="c">% #class{subject = &#34;science&#34;, professor = &#34;ms frizzle&#34;,
</span></span></span><span class="line"><span class="cl"><span class="c">%        students = [#person{name = &#34;dorothy&#34;, grade = &#34;A&#34;},
</span></span></span><span class="line"><span class="cl"><span class="c">%                    #person{name = &#34;arnold&#34;, grade = &#34;B&#34;}]}
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nn">sample</span><span class="p">:</span><span class="nf">get_classroom_size</span><span class="p">(</span><span class="nv">ClassOne</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="c">% 2
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nn">sample</span><span class="p">:</span><span class="nf">get_student_names</span><span class="p">(</span><span class="nv">ClassOne</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="err">%</span> <span class="s">&#34;dorothy, arnold&#34;</span></span></span></code></pre></div></div>
<p>Alternatively, you could add fields to the record to hold the values that are based on other fields, and use helper function to set them along with all the other fields when the record is instantiated.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">module</span><span class="p">(</span><span class="n">sample</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">export</span><span class="p">([</span><span class="n">create_classroom</span><span class="o">/</span><span class="mi">3</span><span class="p">]).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">record</span><span class="p">(</span><span class="nl">person</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">name</span> <span class="p">::</span> <span class="n">string</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">            <span class="n">grade</span> <span class="p">::</span> <span class="n">string</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="p">}).</span>
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">record</span><span class="p">(</span><span class="nl">class</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">subject</span> <span class="p">::</span> <span class="n">string</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">            <span class="n">professor</span> <span class="p">::</span> <span class="n">string</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">            <span class="n">students</span> <span class="p">::</span> <span class="p">[</span><span class="nl">#person</span><span class="p">{}],</span>
</span></span><span class="line"><span class="cl">            <span class="nb">size</span> <span class="p">::</span> <span class="n">integer</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">            <span class="n">names</span> <span class="p">::</span> <span class="n">string</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="p">}).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">create_classroom</span><span class="p">(</span><span class="n">string</span><span class="p">(),</span> <span class="n">string</span><span class="p">(),</span> <span class="p">[</span><span class="nl">#person</span><span class="p">{}])</span> <span class="o">-&gt;</span> <span class="nl">#class</span><span class="p">{}.</span>
</span></span><span class="line"><span class="cl"><span class="nf">create_classroom</span><span class="p">(</span><span class="nv">Subject</span><span class="p">,</span> <span class="nv">Professor</span><span class="p">,</span> <span class="nv">Students</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nl">#class</span> <span class="p">{</span> <span class="n">subject</span> <span class="o">=</span> <span class="nv">Subject</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">             <span class="n">professor</span> <span class="o">=</span> <span class="nv">Professor</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">             <span class="n">students</span> <span class="o">=</span> <span class="nv">Students</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">             <span class="nb">size</span> <span class="o">=</span> <span class="nb">length</span><span class="p">(</span><span class="nv">Students</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">             <span class="n">names</span> <span class="o">=</span> <span class="nn">string</span><span class="p">:</span><span class="nf">join</span><span class="p">([</span><span class="nv">S</span><span class="nl">#person.name</span> <span class="p">||</span> <span class="nv">S</span> <span class="o">&lt;-</span> <span class="nv">Students</span><span class="p">],</span> <span class="s">&#34;, &#34;</span><span class="p">)</span> <span class="p">}.</span></span></span></code></pre></div></div>
<p>Now you can create the record and populate it with the other fields at the same time.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="nn">sample</span><span class="p">:</span><span class="nf">create_classroom</span><span class="p">(</span><span class="s">&#34;science&#34;</span><span class="p">,</span> <span class="s">&#34;ms frizzle&#34;</span><span class="p">,</span> <span class="p">[</span><span class="nl">#person</span><span class="p">{</span><span class="n">name</span><span class="o">=</span><span class="s">&#34;dorothy&#34;</span><span class="p">,</span><span class="n">grade</span><span class="o">=</span><span class="s">&#34;A&#34;</span><span class="p">},</span> 
</span></span><span class="line"><span class="cl">                                                  <span class="nl">#person</span><span class="p">{</span><span class="n">name</span><span class="o">=</span><span class="s">&#34;arnold&#34;</span><span class="p">,</span><span class="n">grade</span><span class="o">=</span><span class="s">&#34;B&#34;</span><span class="p">}]).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">% #class{subject = &#34;science&#34;, professor = &#34;ms frizzle&#34;,
</span></span></span><span class="line"><span class="cl"><span class="c">%        students = [#person{name = &#34;dorothy&#34;, grade = &#34;A&#34;},
</span></span></span><span class="line"><span class="cl"><span class="c">%                    #person{name = &#34;arnold&#34;, grade = &#34;B&#34;}],
</span></span></span><span class="line"><span class="cl"><span class="c">%        size = 2,
</span></span></span><span class="line"><span class="cl"><span class="err">%</span>        <span class="n">names</span> <span class="o">=</span> <span class="s">&#34;dorothy, arnold&#34;</span><span class="p">}</span></span></span></code></pre></div></div>

<h2 class="relative group">What can we learn from this exercise?
    <div id="what-can-we-learn-from-this-exercise" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-can-we-learn-from-this-exercise" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>At the end of the day, property accessors are kind of pointless in Erlang because the fields they would provide access to cannot be modified <em>ever</em>. Not to mention, there&rsquo;s no concept of a &ldquo;getter&rdquo; without a &ldquo;setter&rdquo;. In other words, someone could create a new instance of the record and overwrite the default value (the function) I assigned it in the record. Perhaps something would&rsquo;ve been possible if records weren&rsquo;t a tacked-on afterthought and simply tuples in disguise. 🤔</p>
<p>If what you really want is a way to populate a field during creation of a record, create a helper function like the last example in the previous section. Let the function manipulate the data and populate the fields with the values you&rsquo;d like.</p>
<p>If what you really want is a way to manipulate a field before getting its value back, create a helper function like the first example in the previous section. Let the function manipulate the data and return the values you&rsquo;re interested in.</p>
]]></content:encoded><media:content url="https://grantwinney.com/are-property-accessors-possible-in-erlang/feature.webp" medium="image" type="image/webp"/></item><item><title>Create a secure, personal instance of DokuWiki on DigitalOcean</title><link>https://grantwinney.com/creating-your-own-secure-wiki-using-dokuwiki/</link><pubDate>Sun, 01 Apr 2018 02:46:32 +0000</pubDate><guid>https://grantwinney.com/creating-your-own-secure-wiki-using-dokuwiki/</guid><description>I&amp;rsquo;ve been thinking for awhile now that I wanted to setup a wiki. I wanted something light-weight, with support for uploading images and files. And I wanted to retain control over the data and configuration, as well as encrypt access to it. Here&amp;rsquo;s how to install DokuWiki on Ubuntu with DigitalOcean.</description><content:encoded><![CDATA[<p>I&rsquo;ve been thinking for awhile now that I wanted to throw together a wiki for my personal use. Something light-weight that supported uploading images and files - nothing too fancy. And I wanted to self-host it so I have greater control over the data and installation, and can restrict and secure access to it while still accessing it from anywhere.</p>
<p>Before you tread this path, there are plenty of nice free/inexpensive tools for notetaking too - Evernote, Confluence, Dropbox Paper, docs.google.com, <a href="https://www.workzone.com/blog/evernote-alternatives/"  target="_blank" rel="noreferrer">etc</a>, but sometimes there&rsquo;s a hidden cost in &ldquo;free&rdquo; services.</p>
<p>If you want a wiki, but you need more than what I&rsquo;ve listed, check out <a href="https://www.wikimatrix.org/compare/TiddlyWiki&#43;DokuWiki&#43;MediaWiki"  target="_blank" rel="noreferrer">WikiMatrix</a> to compare what&rsquo;s out there - just try not to get overwhelmed. For what I wanted it looked like <a href="https://www.dokuwiki.org/"  target="_blank" rel="noreferrer">DokuWiki</a> would do the job, so that&rsquo;s what I went with.</p>

<h2 class="relative group">Create an Ubuntu Droplet
    <div id="create-an-ubuntu-droplet" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#create-an-ubuntu-droplet" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>First, you&rsquo;ll need a place to host your wiki. DokuWiki can be installed on Ubuntu, so we&rsquo;ll spin up an Ubuntu virtual machine using <a href="https://m.do.co/c/448f25462030"  target="_blank" rel="noreferrer">DigitalOcean</a>. I&rsquo;ve been using DO for years for this blog, and I can&rsquo;t recommend them enough. Creating new VMs is painless, <a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-digitalocean-backups"  target="_blank" rel="noreferrer">backing them up</a> and <a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-digitalocean-backups#restore-droplet"  target="_blank" rel="noreferrer">restoring them</a> is easy, they respond quickly, and their documentation is superb.</p>
<p>Create a new <a href="https://m.do.co/c/448f25462030"  target="_blank" rel="noreferrer">DigitalOcean</a> account. They call their virtual machines &ldquo;droplets&rdquo;, so click the big green &ldquo;Create&rdquo; button near the top, then choose &ldquo;Droplets&rdquo; and &ldquo;Ubuntu&rdquo;.</p>
<ul>
<li>I chose Ubuntu 17.10. There are other versions available, but you might as well choose the most recent.</li>
<li>Select the smallest droplet size. You can always upgrade later, but it should work just fine for DokuWiki and a few users.</li>
<li>Pay close attention to the SSH options. You&rsquo;ll want to be able to SSH into your new machine. Reading this may help: <a href="https://www.digitalocean.com/community/tutorials/how-to-use-ssh-keys-with-digitalocean-droplets"  target="_blank" rel="noreferrer">How To Use SSH Keys with DigitalOcean Droplets</a></li>
<li>Choose whatever data center is closest to where you live.</li>
<li>Under &ldquo;additional options&rdquo; consider selecting &ldquo;backups&rdquo;. You don&rsquo;t need to do this, but for a whopping $1 a month you gain the ability to roll back your entire droplet if anything gets messed up with it (such as an update gone bad).</li>
</ul>
<p>Give the droplet a name if you&rsquo;d like, then click &ldquo;create&rdquo; at the bottom. After a minute or so, the droplet is created and you get an email with the root password for logging in.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="create-an-ubuntu-17.10-droplet"
    src="/creating-your-own-secure-wiki-using-dokuwiki/create-an-ubuntu-17.10-droplet.png"
    width="1362"
      height="1308"></figure>

<h2 class="relative group">Configure Ubuntu
    <div id="configure-ubuntu" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#configure-ubuntu" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The email you receive from DigitalOcean has everything you need to login to your new machine.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">Your new Droplet is all set to go!
You can access it using the following credentials:

Droplet Name: my-wiki
IP Address: 111.111.111.111
Username: root
Password: some-long-hexadecimal-string</code></pre></div>
<p>Open up the terminal window or command prompt of your choice, and type something like this, replacing the IP address with whatever DO assigned to you. It&rsquo;ll prompt you for the password, then make you change it.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">ssh root@111.111.111.111</span></span></code></pre></div></div>
<p><a href="https://www.digitalocean.com/community/tutorials/how-to-create-a-sudo-user-on-ubuntu-quickstart"  target="_blank" rel="noreferrer">Create a new user with sudo privileges</a>, so you&rsquo;re not performing everything that follows as root. That&rsquo;s just good practice.</p>
<p><a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-16-04#step-seven-%E2%80%94-set-up-a-basic-firewall"  target="_blank" rel="noreferrer">Enable the built-in firewall</a> to restrict what your server allows connections to. This entire document is full of good advice on setting up your server - I suggest checking the rest of it out. I&rsquo;m not covering it here, except to say you&rsquo;ll need to add two other rules: <em>(or you won&rsquo;t even be able to view your new wiki)</em></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">sudo ufw allow http
</span></span><span class="line"><span class="cl">sudo ufw allow https</span></span></code></pre></div></div>

<h2 class="relative group">Install DokuWiki
    <div id="install-dokuwiki" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#install-dokuwiki" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><strong>Follow steps 2-9</strong> of <a href="https://www.dokuwiki.org/install:ubuntu"  target="_blank" rel="noreferrer">installing DokuWiki on Ubuntu</a>, paying attention to the following:</p>
<p>Step 7:<br>
Ignore a, b, and c, since you&rsquo;re not doing this for testing purposes.</p>
<p>Step 8:<br>
Look for the last <code>Directory</code> block:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;Directory</span> <span class="err">/var/www</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    Options Indexes FollowSymLinks
</span></span><span class="line"><span class="cl">    AllowOverride None
</span></span><span class="line"><span class="cl">    Require all granted
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/Directory&gt;</span></span></span></code></pre></div></div>
<p>Step 10:<br>
<strong>Don&rsquo;t actually do this step yet</strong>, but visit the setup link. If everything is good so far, you should see a page like this one:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="dokuwiki-installer"
    src="/creating-your-own-secure-wiki-using-dokuwiki/dokuwiki-installer.png"
    width="2020"
      height="764"></figure>

<h2 class="relative group">Secure Your Droplet
    <div id="secure-your-droplet" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#secure-your-droplet" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><em>Note: If you&rsquo;re planning on assigning a domain name to your wiki, you might consider</em> <a href="https://www.digitalocean.com/community/tutorials/how-to-secure-apache-with-let-s-encrypt-on-ubuntu-16-04"  target="_blank" rel="noreferrer"><em>securing your site with a free certificate from Let&rsquo;s Encrypt</em></a><em>. If you&rsquo;re just using the droplet&rsquo;s IP address to access your wiki like I plan on doing, or you&rsquo;re already familiar with self-signed certificates, then continue&hellip;</em></p>
<p>Follow these DigitalOcean instructions:<br>
<a href="https://www.digitalocean.com/community/tutorials/how-to-create-a-self-signed-ssl-certificate-for-apache-in-ubuntu-16-04"  target="_blank" rel="noreferrer">How To Create a Self-Signed SSL Certificate for Apache in Ubuntu 16.04</a></p>
<p>Step 1:<br>
If you don&rsquo;t care about how long the SSL certificate lives, you could change the <code>-days</code> parameter to something longer like 36500 (a hundred years).</p>
<p>You&rsquo;ll be prompted for some info. The &ldquo;Common Name&rdquo; is the IP address of your new droplet, and <em><strong>you need that</strong></em>. Everything else is optional and you can just click Enter through them.</p>
<p>Step 2:<br>
Under <em>&ldquo;Modify the Default Apache SSL Virtual Host File&rdquo;</em> you&rsquo;ll want to make one more modification to the <code>default-ssl.conf</code> file, changing <code>DocumentRoot /var/www/html</code> to <code>DocumentRoot /var/www/dokuwiki</code></p>
<p>Also, you can just ignore the suggested change to &ldquo;BrowserMatch&rdquo; unless you&rsquo;re planning on using IE 6 to access your wiki.</p>
<p>Step 3:<br>
After allowing &lsquo;Apache Full&rsquo; in the firewall, I was done. There was no separate &lsquo;Apache&rsquo; entry to delete. You probably won&rsquo;t have one either.</p>
<p>After following the rest of the document, refresh the DokuWiki install page from earlier (or reopen it). It should redirect you from the secure (https) version of the site, and (in most browsers) throw up a nice big red warning page.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="self-signed-cert-warning-1"
    src="/creating-your-own-secure-wiki-using-dokuwiki/self-signed-cert-warning.png"
    width="1224"
      height="934"></figure>
<p>Just bypass it using whatever method your browser gives you. That&rsquo;s the downside of a self-signed certificate, and why you should never use one for a production site. Your communication with the server is encrypted though, so let&rsquo;s continue with setting up the wiki.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="self-signed-cert-bypass"
    src="/creating-your-own-secure-wiki-using-dokuwiki/self-signed-cert-bypass.png"
    width="1842"
      height="728"></figure>

<h2 class="relative group">Configure DokuWiki
    <div id="configure-dokuwiki" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#configure-dokuwiki" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Now that the line to the server is encrypted, let&rsquo;s continue setting up DokuWiki. We&rsquo;ll be setting up a password and other information, and I didn&rsquo;t want to send that in plaintext.</p>
<p><strong>Follow steps 10-11</strong> of <a href="https://www.dokuwiki.org/install:ubuntu"  target="_blank" rel="noreferrer">installing DokuWiki on Ubuntu</a>, paying attention to the following:</p>
<p>Step 10:<br>
Here&rsquo;s how I recommend configuring your installation, but you may want something different. Click &ldquo;Enable ACL&rdquo; to setup the initial user, which you can then use to login and change other options. I also chose to disable sending anonymous usage data because I tend to be a tad paranoid about exactly what data is being sent, but ymmv.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="dokuwiki-initial-setup"
    src="/creating-your-own-secure-wiki-using-dokuwiki/dokuwiki-initial-setup.png"
    width="1632"
      height="1502"></figure>
<p>Step 12:<br>
I didn&rsquo;t bother setting up postfix for sending email because I don&rsquo;t plan on giving anyone else access. If I do run through it, I&rsquo;ll update these instructions. If you&rsquo;re interested in trying it, <a href="https://linode.com/docs/email/postfix/configure-postfix-to-send-mail-using-gmail-and-google-apps-on-debian-or-ubuntu/"  target="_blank" rel="noreferrer">this tutorial for using Gmail looks pretty comprehensive</a>.</p>

<h2 class="relative group">Other Things to Consider
    <div id="other-things-to-consider" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#other-things-to-consider" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There&rsquo;s <a href="https://www.dokuwiki.org/config"  target="_blank" rel="noreferrer">a lot more you can configure</a> too. Here are a few other things you might consider adjusting.</p>

<h3 class="relative group">Further Secure Your Site
    <div id="further-secure-your-site" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#further-secure-your-site" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>If you truly want a private wiki that no one else can register an account for, or view details about, you&rsquo;ll want to set some other configuration settings. For example, even if you&rsquo;re not logged in you can view the sitemap for the wiki&hellip; that lets anyone view the <em>list</em> of pages available (although not the actual content).</p>
<p>Login, go to <code>Admin / Configuration Settings</code> and look for the following panel. I&rsquo;d suggest disabling the following options, but you may want to experiment on your own and see how it affects the site when you&rsquo;re logged out.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="disable-dokuwiki-actions"
    src="/creating-your-own-secure-wiki-using-dokuwiki/disable-dokuwiki-actions.png"
    width="1726"
      height="350"></figure>

<h3 class="relative group">Increase Maximum Upload Size
    <div id="increase-maximum-upload-size" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#increase-maximum-upload-size" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>If you try to upload a file to your new wiki, you&rsquo;ll see something like this. Notice the 2MB limit on uploads. If you were setting up a server for a bunch of people to use, this might be reasonable; however, if it&rsquo;s for you then you might want to upload much larger files.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="upload-media-files"
    src="/creating-your-own-secure-wiki-using-dokuwiki/upload-media-files.png"
    width="1486"
      height="476"></figure>
<p>To fix this, let&rsquo;s make a couple changes to the <code>php.ini</code> file, which can be found here. <em>(This is one advantage of hosting our own server. You often cannot edit the php.ini file in a shared environment.)</em></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">sudo nano /etc/php/7.2/apache2/php.ini</span></span></code></pre></div></div>
<p>If you don&rsquo;t have a file there for some reason, you can search for it with <code>find</code>:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">sudo find / -name php.ini</span></span></code></pre></div></div>
<p>Open the file and look for <code>upload_max_filesize</code>. Change the &ldquo;2M&rdquo; to whatever you want, like &ldquo;100M&rdquo;. Now you&rsquo;ll be able to upload 100 MB files.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">;;;;;;;;;;;;;;;;
; File Uploads ;
;;;;;;;;;;;;;;;;

; Whether to allow HTTP file uploads.
; http://php.net/file-uploads
file_uploads = On

; Maximum allowed size for uploaded files.
; http://php.net/upload-max-filesize
upload_max_filesize = 2M</code></pre></div>
<p>Apparently you have to modify one more setting, and that&rsquo;s to <a href="https://stackoverflow.com/a/2184541/301857"  target="_blank" rel="noreferrer">make <code>post_max_size</code> greater than <code>upload_max_filesize</code></a>. Better yet, the comments in the file suggest setting it to 0 to just disable, which works fine for me. Maybe not a great idea for a public facing blog with lots of users, but if it&rsquo;s just for you then I wouldn&rsquo;t sweat it.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">;;;;;;;;;;;;;;;;;
; Data Handling ;
;;;;;;;;;;;;;;;;;

; Maximum size of POST data that PHP will accept.
; Its value may be 0 to disable the limit. It is ignored if POST data reading
; is disabled through enable_post_data_reading.
; http://php.net/post-max-size
post_max_size = 0</code></pre></div>
<p>Restart your server with <code>sudo service apache2 restart</code> and try uploading again.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="upload-media-files-larger"
    src="/creating-your-own-secure-wiki-using-dokuwiki/upload-media-files-larger.png"
    width="1492"
      height="492"></figure>

<h3 class="relative group">Add Support for Markdown
    <div id="add-support-for-markdown" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#add-support-for-markdown" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>I use markdown all over - on my blog, Stack Overflow, GitHub, etc. If you&rsquo;d like to add support for it to DokuWiki, click on the &ldquo;Admin&rdquo; link and go to &ldquo;Extension Manager&rdquo;. From there, do a search for &ldquo;markdown&rdquo;. As of this writing, <a href="https://www.dokuwiki.org/plugin:markdowku"  target="_blank" rel="noreferrer">markdowku</a> appears to be the most recently maintained plugin. Click the &ldquo;Install&rdquo; link next to it and try it out - it seems to be working for me.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="markdowku-search-results"
    src="/creating-your-own-secure-wiki-using-dokuwiki/markdowku-search-results.png"
    width="1980"
      height="692"></figure>

<h3 class="relative group">View Raw Wiki Files
    <div id="view-raw-wiki-files" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#view-raw-wiki-files" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Everything your wiki needs is stored in <code>/var/www/dokuwiki</code>. You might as well get a little familiar with that directory, in case you ever need to fix something. For example, you can view the text file representing the main page here:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">cat /var/www/dokuwiki/data/pages/wiki/welcome.txt</span></span></code></pre></div></div>
<p>Here&rsquo;s a side-by-side view of the source file and the rendered page in the browser:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="view-text-file"
    src="/creating-your-own-secure-wiki-using-dokuwiki/view-text-file.jpg"
    width="2784"
      height="1398"></figure>

<h3 class="relative group">Regarding &ldquo;Let&rsquo;s Encrypt&rdquo;&hellip;
    <div id="regarding-lets-encrypt" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#regarding-lets-encrypt" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>I already have a free certificate from Let&rsquo;s Encrypt for my blog. If you do too, you might consider <a href="https://letsencrypt.org/2017/07/06/wildcard-certificates-coming-jan-2018.html"  target="_blank" rel="noreferrer">configuring wildcard certificates</a> so you can assign your wiki to something like &ldquo;wiki.your-blog-domain.com&rdquo;. I haven&rsquo;t gone through this yet, so no clue how simple or complicated it&rsquo;ll be.</p>
<p>That&rsquo;s it! You have a personal, secure environment to store 25 GB worth of notes, images, files, etc - for only $5 a month. If you end up needing more space, you can easily add extra through the DigitalOcean admin panel. If you manage to get it working, let me know&hellip; I&rsquo;d love to hear how it goes. Good luck!</p>
]]></content:encoded><media:content url="https://grantwinney.com/creating-your-own-secure-wiki-using-dokuwiki/feature.webp" medium="image" type="image/webp"/></item><item><title>Automatically add links to all headers on a website (a Chrome extension)</title><link>https://grantwinney.com/automatically-adding-links-next-to-all-headers-on-the-page-a-chrome-extension/</link><pubDate>Sun, 25 Feb 2018 21:33:09 +0000</pubDate><guid>https://grantwinney.com/automatically-adding-links-next-to-all-headers-on-the-page-a-chrome-extension/</guid><description>Ever needed to link directly to one section of a webpage? You can, as long as there&amp;rsquo;s a header (or another element nearby like a div) with an ID assigned to it. Let&amp;rsquo;s see how.</description><content:encoded><![CDATA[<p>Ever needed to link directly to one section of a webpage? You can, as long as there&rsquo;s a header (or another element nearby like a div) with an ID assigned to it. The presence of an ID isn&rsquo;t guaranteed for every website, but a lot of personal blogs and technical doc sites use them.</p>
<p>Getting the ID isn&rsquo;t tough, but it&rsquo;s a bit of a pain. You have to view the source code for the page, find the header to get the ID, and then append the ID to the URL before sharing it.</p>
<p>It doesn&rsquo;t have to be that hard.</p>

<h2 class="relative group">A smarter way to link directly to a section
    <div id="a-smarter-way-to-link-directly-to-a-section" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#a-smarter-way-to-link-directly-to-a-section" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>This weekend I wrote an extension for the Chrome browser to make this easier. When you open a webpage, it scans the DOM for headers, and generates a link for each header that has an ID. When you hover over the header, you can see the link and click on it to copy it to your clipboard.</p>
<p><a href="https://chrome.google.com/webstore/detail/generate-links-for-header/dckfkngmahjdokkkmconmfjdmicjcmgf"  target="_blank" rel="noreferrer"><strong>Get it from the Chrome webstore</strong></a><strong>.</strong></p>

<h3 class="relative group">See it in action
    <div id="see-it-in-action" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#see-it-in-action" aria-label="Anchor">#</a>
    </span>
    
</h3>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="seeing the extension in action"
    src="/automatically-adding-links-next-to-all-headers-on-the-page-a-chrome-extension/show-header-with-links.gif"
    width="640"
      height="400"></figure>

<h2 class="relative group">Random resources and notes
    <div id="random-resources-and-notes" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#random-resources-and-notes" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>This was a true &ldquo;programming by stack overflow&rdquo; experience, as there were a myriad of things I didn&rsquo;t know how to do before writing this. But that&rsquo;s the fun part of writing something like this - you end up learning about things you didn&rsquo;t even know existed!</p>

<h3 class="relative group">Getting the bookmark (chain link) icon
    <div id="getting-the-bookmark-chain-link-icon" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#getting-the-bookmark-chain-link-icon" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>For the icon, I considered using an image first, then thought I&rsquo;d use a <a href="http://www.fileformat.info/info/unicode/char/1f517/browsertest.htm"  target="_blank" rel="noreferrer">unicode</a> character. But I was concerned the image might be difficult to inject into the page, and that the unicode character might not render correctly on some systems.</p>
<p>I ended up using the same SVG element that GitHub uses for their anchor icon. Here&rsquo;s an interesting article I found while doing research:</p>
<p><a href="http://vanseodesign.com/web-design/svg-definition-reuse/"  target="_blank" rel="noreferrer">How To Define SVG Content for Reuse — The defs, symbol, And use Elements</a></p>

<h3 class="relative group">Positioning the icon
    <div id="positioning-the-icon" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#positioning-the-icon" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>I used a few other posts to figure out how to position the icon where I did, making it seem to hover to the left of the header, and hiding it until the user hovers over the header. Gotta give credit where it&rsquo;s due:</p>
<ul>
<li><a href="https://stackoverflow.com/a/8262470/301857"  target="_blank" rel="noreferrer">How to Set a Fixed Width for a Div Element with Display Inline?</a></li>
<li><a href="https://stackoverflow.com/a/27208577/301857"  target="_blank" rel="noreferrer">Using only CSS, show div on hover over &lt;a&gt;</a></li>
<li><a href="https://stackoverflow.com/a/4502693/301857"  target="_blank" rel="noreferrer">How to affect other elements when a div is hovered</a></li>
</ul>
<p>I also made use of <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions"  target="_blank" rel="noreferrer">css transitions</a> for a nice little fade-in/fade-out effect on the icon, whereas the GitHub icon is either visible or not.</p>

<h3 class="relative group">Linking the icon to the header
    <div id="linking-the-icon-to-the-header" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#linking-the-icon-to-the-header" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>I had considered <a href="https://stackoverflow.com/a/4772817/301857"  target="_blank" rel="noreferrer">building the link in javascript</a>, which worked, but ultimately I didn&rsquo;t need that level of flexibility so I opted to just hardcode it instead.</p>
<p>As for constructing the URL itself, I found the <a href="http://www.javascriptkit.com/jsref/location.shtml"  target="_blank" rel="noreferrer">location</a> object to be immensely useful. I wasn&rsquo;t sure whether an anchor should come <em>before</em> a querystring or after, so <a href="https://stackoverflow.com/a/34772568/301857"  target="_blank" rel="noreferrer">this was useful</a> - fwiw, the querystring comes <em>before</em> the anchor.</p>

<h3 class="relative group">Copying the link to the clipboard
    <div id="copying-the-link-to-the-clipboard" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#copying-the-link-to-the-clipboard" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>I had no idea how to copy something to the clipboard, and figured it&rsquo;d be somewhat restricted because, well, can you imagine if every site could touch your clipboard? I found a great SO post on <a href="https://stackoverflow.com/a/33928558/301857"  target="_blank" rel="noreferrer">how to copy to the clipboard</a>, and it was so helpful I <a href="https://meta.stackoverflow.com/a/306183/301857"  target="_blank" rel="noreferrer">awarded a bounty</a> to it.</p>
<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand"  target="_blank" rel="noreferrer">execCommand</a> the thread suggested works on <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand#Browser_compatibility"  target="_blank" rel="noreferrer">all modern browsers</a>, including Chrome.</p>
<p>After I had the script though, I still had to figure out how to get it into the website - having it in the extension was not good enough. SO to the rescue again, which a helpful post on how to <a href="https://stackoverflow.com/a/9517879/301857"  target="_blank" rel="noreferrer">insert code into the page context</a>.</p>

<h3 class="relative group">Miscellaneous
    <div id="miscellaneous" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#miscellaneous" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>I ended up not using this, but <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle"  target="_blank" rel="noreferrer">Window.getComputedStyle()</a> looks extremely helpful if you need it.</p>
<blockquote><p>The Window.getComputedStyle() method returns an object that reports the values of all CSS properties of an element after applying active stylesheets and resolving any basic computation those values may contain. Individual CSS property values are accessed through APIs provided by the object or by simply indexing with CSS property names.</p>
</blockquote>
<h2 class="relative group">For the future&hellip;
    <div id="for-the-future" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#for-the-future" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>It&rsquo;d be nice to display a small confirmation that the text has been copied, <a href="https://www.w3schools.com/howto/howto_js_snackbar.asp"  target="_blank" rel="noreferrer">maybe like this</a>, but I don&rsquo;t think it&rsquo;s necessary.</p>
]]></content:encoded><media:content url="https://grantwinney.com/automatically-adding-links-next-to-all-headers-on-the-page-a-chrome-extension/feature.webp" medium="image" type="image/webp"/></item><item><title>Generate random passwords, numbers and GUIDs with the PasswordRandom API</title><link>https://grantwinney.com/passwordrandom-api/</link><pubDate>Tue, 06 Feb 2018 12:36:34 +0000</pubDate><guid>https://grantwinney.com/passwordrandom-api/</guid><description>The PasswordRandom API provides random values - and not just passwords as the name would seem to suggest. It also generates GUIDs, random numbers, characters, etc.</description><content:encoded><![CDATA[<p>Some of the API&rsquo;s I&rsquo;ve written about give you access to data - public data like <a href="https://grantwinney.com/what-is-iss-notify-api/"  target="_blank" rel="noreferrer">ISS sightings</a> and personal data like your <a href="https://grantwinney.com/what-is-dropbox-api/"  target="_blank" rel="noreferrer">Dropbox files</a> - but an API can return other things too. Here&rsquo;s an API that returns random numbers, GUIDs and other values, and provides an opportunity to customize what it returns.</p>
<p>Two things before you get started:</p>
<ul>
<li>If you&rsquo;re unfamiliar with APIs, you might want to <a href="https://grantwinney.com/what-is-an-api/"  target="_blank" rel="noreferrer">read this first</a> to familiarize yourself with them.</li>
<li>You may want to install <a href="https://www.getpostman.com/"  target="_blank" rel="noreferrer">Postman</a>, which allows you to access API endpoints without having to write an app, as well as save the calls you make and sync them online between your computers.</li>
</ul>

<h2 class="relative group">What does it provide?
    <div id="what-does-it-provide" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-does-it-provide" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The <a href="http://www.passwordrandom.com/api"  target="_blank" rel="noreferrer">PasswordRandom API</a> provides random values - and not <em>just</em> passwords as the name would seem to suggest. It also generates GUIDs, random numbers, characters, etc. Better yet, it has parameters for the <em>number</em> of results you get back (like 10 GUIDs at once), and other ways to configure or limit the numbers and passwords you get back too.</p>
<p>If you&rsquo;re developing an app that will, for whatever reason, generate a GUID or return such a value to the user, you have a choice. Some languages have built-in libraries that return a GUID for you. Others require downloading a third-party and taking on another dependency. If you didn&rsquo;t want that dependency for some reason, you could call this API instead. As for everything else it offers.. well, you may find a reason to use it, so let&rsquo;s check it out.</p>

<h2 class="relative group">Taking it out for a spin
    <div id="taking-it-out-for-a-spin" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#taking-it-out-for-a-spin" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>This API doesn&rsquo;t require any authentication, nor does it have documented rate limits. You can request the format (plain, xml, json) and other filters too.</p>

<h3 class="relative group">GUIDs
    <div id="guids" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#guids" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>You can specify the number of globally unique identifiers (GUIDs) you&rsquo;d like back, as well as the format in which to return them.</p>
<p><code>http://www.passwordrandom.com/query?command=guid&amp;count=5&amp;format=json</code></p>
<p>And that returns 5 GUIDs in JSON format, like this:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;char&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;f81660fd-6fb2-4172-81cc-7688ebb6c586&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;f67ff2cf-a60b-4d7b-9d21-25d28fd1628c&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;c7e019eb-dd7f-4660-9674-1109c39d394b&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;e339fc3c-4995-479b-8715-4c25837ec5cf&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;0700d1f6-4b40-459e-9d8f-1b5a34265421&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h3 class="relative group">Numbers
    <div id="numbers" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#numbers" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>You can also get random integers&hellip;</p>
<p><code>http://www.passwordrandom.com/query?command=int&amp;count=12&amp;format=xml&amp;min=-12&amp;max=18</code></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="cp">&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34; ?&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;random&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>-3<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>7<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>13<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>-12<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>-11<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>2<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>-1<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>6<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>-10<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>6<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>8<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>7<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/random&gt;</span></span></span></code></pre></div></div>
<p>&hellip; as well as random floats.</p>
<p><code>http://www.passwordrandom.com/query?command=double&amp;count=12&amp;format=xml&amp;min=-12&amp;max=18</code></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="cp">&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34; ?&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;random&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>2.8868<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>9.0036<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>-3.4164<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>9.5505<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>-0.0881<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>-10.8226<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>17.7975<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>4.9651<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>1.9549<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>2.0954<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>-4.7204<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>-6.0978<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/random&gt;</span></span></span></code></pre></div></div>
<p>Strangely, getting random floats with a range only works if the range is integers. If you try to specify a max of 18.2, it returns values all over the place&hellip; even outside the default range of 0 to 100.</p>
<p><code>http://www.passwordrandom.com/query?command=double&amp;count=6&amp;format=xml&amp;min=0.0&amp;max=18.0</code></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="cp">&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34; ?&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;random&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>136.7259<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>135.1663<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>36.1727<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>25.7964<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>80.7496<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;result&gt;</span>112.1617<span class="nt">&lt;/result&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/random&gt;</span></span></span></code></pre></div></div>

<h3 class="relative group">Passwords
    <div id="passwords" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#passwords" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>A note about generating passwords using an API like this. I probably wouldn&rsquo;t recommend it for <em>passwords,</em> but it could be useful for generating randomized strings. If you did use it for passwords, it&rsquo;s not like the site serving the API could know what account you&rsquo;ll use it for, but then it&rsquo;d help if it were at least served over SSL so no one watching the network could sniff it&hellip;</p>
<p>Here&rsquo;s 8 random passwords:</p>
<p><code>http://www.passwordrandom.com/query?command=password&amp;count=8&amp;format=json</code></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;char&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;RiuXA6.44ld&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;SyePA8)31qr&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;RyiHY8%55ss&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;MiuVE3#22vr&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;QuyNO6|26gx&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;BuuPY7`29vl&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;ZouTE5~18jv&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;ZeoJI2[03bm&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>If you click on the <a href="http://www.passwordrandom.com/pronounceable-password-generator"  target="_blank" rel="noreferrer">scheme</a> link next to &ldquo;password&rdquo; field, you&rsquo;ll find a lot more options to generate the <em>exact</em> string you need.</p>
<p>For example, if I needed a password for a system that required a length of 8, and at least one upper, one number, and one special symbol, I could request:</p>
<p><code>http://www.passwordrandom.com/query?command=password&amp;count=3&amp;scheme=LrrrNrr!</code></p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">BFal0is!
TOGY3Xa+
LnqD8Ae#</code></pre></div>
<p>Or if you just wanted to generate 10 random phone numbers for some mockup:</p>
<p><code>http://www.passwordrandom.com/query?command=password&amp;count=10&amp;scheme=%2Bn(nnn)nnn-nnnn</code></p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">+1(597)499-5436
+8(080)704-9577
+6(916)072-4624
+3(418)217-7998
+8(470)258-2650
+4(995)881-2964
+3(648)883-2888
+8(178)047-6459
+3(029)629-6601
+3(016)479-8471</code></pre></div>
]]></content:encoded><media:content url="https://grantwinney.com/passwordrandom-api/feature.webp" medium="image" type="image/webp"/></item><item><title>Creating a table of contents for your blog</title><link>https://grantwinney.com/creating-a-table-of-contents-for-your-blog/</link><pubDate>Sat, 03 Feb 2018 14:08:21 +0000</pubDate><guid>https://grantwinney.com/creating-a-table-of-contents-for-your-blog/</guid><description>A table of contents is convenient for visitors, so I wrote a script to generate one for any blog automatically!</description><content:encoded><![CDATA[<p>I wrote about <a href="https://grantwinney.com/5-quick-hacks-for-your-ghost-theme"  target="_blank" rel="noreferrer">5 quick hacks for your Ghost theme</a> last year, after switching to Ghost as a blogging platform. The last hack I mentioned was generating a &ldquo;table of contents&rdquo; using a handlebars script I&rsquo;d found. Ghost was still considered beta at the time, and when 1.0 was released, the script stopped working correctly. I never bothered going back to figure out why.</p>
<p>A table of contents is nice to have though, and convenient for your visitors, so I wrote a new script that should work for any html page (with minor adjustments). Here it is in entirety, or <a href="https://github.com/grantwinney/table-of-contents-for-html-page"  target="_blank" rel="noreferrer">check GitHub for the latest version</a>.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm"> * For displaying a table of contents - pass the entire document (DOM) to getTocMarkup
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">getHeaderLevel</span><span class="p">(</span><span class="nx">header</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">Number</span><span class="p">(</span><span class="nx">header</span><span class="p">.</span><span class="nx">nodeName</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">createTocMarkup</span><span class="p">(</span><span class="nx">headers</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">prevLevel</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">output</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">headers</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">h</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">var</span> <span class="nx">currLevel</span> <span class="o">=</span> <span class="nx">getHeaderLevel</span><span class="p">(</span><span class="nx">h</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">currLevel</span> <span class="o">&gt;</span> <span class="nx">prevLevel</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kd">var</span> <span class="nx">ranOnce</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">while</span> <span class="p">(</span><span class="nx">currLevel</span> <span class="o">&gt;</span> <span class="nx">prevLevel</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="p">(</span><span class="nx">ranOnce</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nx">output</span> <span class="o">+=</span> <span class="s2">&#34;&amp;nbsp;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="nx">output</span> <span class="o">+=</span> <span class="s2">&#34;&lt;ol style=\&#34;margin-bottom:0px\&#34;&gt;&lt;li&gt;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="nx">prevLevel</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="nx">ranOnce</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">currLevel</span> <span class="o">==</span> <span class="nx">prevLevel</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">output</span> <span class="o">+=</span> <span class="s2">&#34;&lt;/li&gt;&lt;li&gt;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">currLevel</span> <span class="o">&lt;</span> <span class="nx">prevLevel</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">while</span> <span class="p">(</span><span class="nx">currLevel</span> <span class="o">&lt;</span> <span class="nx">prevLevel</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nx">output</span> <span class="o">+=</span> <span class="s2">&#34;&lt;/li&gt;&lt;/ol&gt;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="nx">prevLevel</span> <span class="o">-=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="nx">output</span> <span class="o">+=</span> <span class="s2">&#34;&lt;li&gt;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nx">output</span> <span class="o">+=</span> <span class="sb">`&lt;a href=&#34;#</span><span class="si">${</span><span class="nx">h</span><span class="p">.</span><span class="nx">id</span><span class="si">}</span><span class="sb">&#34;&gt;</span><span class="si">${</span><span class="nx">h</span><span class="p">.</span><span class="nx">innerText</span><span class="si">}</span><span class="sb">&lt;/a&gt;`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">output</span> <span class="o">!=</span> <span class="s2">&#34;&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Change 2 to the max header level you want in the TOC; in my case, H2
</span></span></span><span class="line"><span class="cl">        <span class="k">while</span> <span class="p">(</span><span class="nx">prevLevel</span> <span class="o">&gt;=</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">output</span> <span class="o">+=</span> <span class="s2">&#34;&lt;/li&gt;&lt;/ol&gt;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="nx">prevLevel</span> <span class="o">-=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="nx">output</span> <span class="o">=</span> <span class="sb">`&lt;h2 class=&#34;widget-title&#34;&gt;Table of Contents&lt;/h2&gt;</span><span class="si">${</span><span class="nx">output</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">output</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">getTocMarkup</span><span class="p">(</span><span class="nb">document</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// I was only interested in the headers within the element that had the .post-content class,
</span></span></span><span class="line"><span class="cl">    <span class="c1">// which is specific to the Ghost blog. If you&#39;re using this elsewhere, or are interested in
</span></span></span><span class="line"><span class="cl">    <span class="c1">// the entire document, delete this line and use document.querySelectorAll(...) on the next line.
</span></span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">body</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementsByClassName</span><span class="p">(</span><span class="s1">&#39;post-content&#39;</span><span class="p">)[</span><span class="mi">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// Add or remove header tags you do (or don&#39;t) want to include in the TOC
</span></span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">headers</span> <span class="o">=</span> <span class="nx">body</span><span class="p">.</span><span class="nx">querySelectorAll</span><span class="p">(</span><span class="s1">&#39;h2, h3, h4, h5, h6&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Change the number to 1 if you want headers no matter what.
</span></span></span><span class="line"><span class="cl">    <span class="c1">// Or if you want at least 3 headers before generating a TOC, change it to 3.
</span></span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">headers</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;=</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">createTocMarkup</span><span class="p">(</span><span class="nx">headers</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>And a sample of the HTML it generates, using this post as an example:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h2</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;title bordered uppercase&#34;</span><span class="p">&gt;</span>Table of Contents<span class="p">&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;margin-left:15px; margin-bottom:20px&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">ol</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;list-style-type:disc; margin-left:10px&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;#general-usage&#34;</span><span class="p">&gt;</span>General Usage<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;#usage-in-ghost&#34;</span><span class="p">&gt;</span>Usage in Ghost<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">ol</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;list-style-type:disc; margin-left:20px&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;#wildbird&#34;</span><span class="p">&gt;</span>Wildbird<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;#casper&#34;</span><span class="p">&gt;</span>Casper<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;/</span><span class="nt">ol</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;#snapshots&#34;</span><span class="p">&gt;</span>Snapshots<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;#other-implementations&#34;</span><span class="p">&gt;</span>Other Implementations<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">ol</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">hr</span><span class="p">&gt;</span></span></span></code></pre></div></div>

<h2 class="relative group">General Usage
    <div id="general-usage" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#general-usage" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Just call the function and write the return value out to the page.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">document</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="nx">getTocMarkup</span><span class="p">(</span><span class="nb">document</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span></span></span></code></pre></div></div>

<h2 class="relative group">Usage in Ghost
    <div id="usage-in-ghost" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#usage-in-ghost" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Here&rsquo;s how I&rsquo;ve got it displayed in the side bar in Ghost.</p>
<ol>
<li>Copy the above script into a file named <code>toc.js</code>, and drop the file into the <code>assets/js</code> directory.</li>
<li>Reference the file from <code>default.hbs</code>, somewhere between the <code>&lt;head&gt;&lt;/head&gt;</code> tags so it&rsquo;s available as the page loads.<br>
<code>&lt;script type=&quot;text/javascript&quot; src=&quot;{{asset &quot;js/toc.js&quot;}}&quot;&gt;&lt;/script&gt;</code></li>
<li>Call it from wherever you want to display it.</li>
</ol>

<h3 class="relative group">Wildbird
    <div id="wildbird" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#wildbird" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>To get it to work with the Wildbird theme specifically, I modified the <code>sidebar.hbs</code> file so that, if the current page is a &ldquo;post&rdquo;, it&rsquo;ll insert a new section containing a table of contents.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">{{!-- Table of Contents --}}
</span></span><span class="line"><span class="cl">{{#is &#34;post&#34;}}
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">section</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;widget widget-text&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nb">document</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="nx">getTocMarkup</span><span class="p">(</span><span class="nb">document</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">section</span><span class="p">&gt;</span><span class="c">&lt;!-- .widget --&gt;</span>
</span></span><span class="line"><span class="cl">{{/is}}</span></span></code></pre></div></div>

<h3 class="relative group">Casper
    <div id="casper" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#casper" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>If you want it to work with the Casper theme, you&rsquo;ll need to make a couple changes.</p>
<ul>
<li>First, change this line in the <code>toc.js</code> script, so that <code>post-content</code> is <code>post-full-content</code>, since that&rsquo;s the name of the class on the element that contains the body of your post.</li>
<li>Second, you&rsquo;ll have to update the <code>post.hbs</code> file directly, since there&rsquo;s no <code>sidebar.hbs</code> file like in the Wildbird theme. Look for the section that starts with <code>&lt;section class=&quot;post-full-content&quot;&gt;</code>. After that markup, setup an empty div where you&rsquo;d like to display the table of contents. Leave <code>{{content}}</code> since that displays the body of your post. Now you can pass the document to the <code>toc.js</code> script and set the results to the div you just created. That has the effect of inserting the TOC at the top of your post, right after the image (if any).</li>
</ul>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;toc&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{{content}}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{{!-- Table of Contents --}}
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">toc</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">&#39;toc&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">toc</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">getTocMarkup</span><span class="p">(</span><span class="nb">document</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<hr>

<h2 class="relative group">Snapshots
    <div id="snapshots" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#snapshots" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Here&rsquo;s how it looks when rendered, showing a single level of headers, two levels, and multiple levels, respectively. It handles omitted headers, like going right from <code>&lt;H2&gt;</code> to <code>&lt;H5&gt;</code>, but uh.. it could be better. Heh.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/creating-a-table-of-contents-for-your-blog/multiple-level-toc.png"
    width="1019"
      height="741"></figure>
<p>While you&rsquo;re at it, you could create a <code>&lt;div id=&quot;toc&quot;&gt;&lt;/div</code> element or similar, and generate your TOC inside of that. Then use CSS styling to your heart&rsquo;s content. Here&rsquo;s what it looks like without numbers, and indenting on linewrap. If you&rsquo;re using Ghost, you could even put the CSS in the &ldquo;code injection&rdquo; section of the admin panel.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/creating-a-table-of-contents-for-your-blog/toc.png"
    width="295"
      height="697"></figure>

<h2 class="relative group">Other Implementations
    <div id="other-implementations" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#other-implementations" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I&rsquo;m happy with my solution, but it requires modifying your theme files directly, which isn&rsquo;t the most user-friendly thing. If you&rsquo;d like to see this solution re-implemented so you can paste it into the blog footer with Ghost&rsquo;s <em>&ldquo;code injection&rdquo;</em> section, check out László&rsquo;s blog post, <a href="https://kb.zensoft.hu/toc-for-your-blog/"  target="_blank" rel="noreferrer">A jQuery Table of Contents for your Ghost Blog Entries</a>.</p>
]]></content:encoded><media:content url="https://grantwinney.com/creating-a-table-of-contents-for-your-blog/feature.webp" medium="image" type="image/webp"/></item><item><title>Manage your books with the Google Books API</title><link>https://grantwinney.com/what-is-the-google-books-api/</link><pubDate>Wed, 31 Jan 2018 00:07:00 +0000</pubDate><guid>https://grantwinney.com/what-is-the-google-books-api/</guid><description>The Google Books API provides access to Google Books, which lets you search for any book and, at a bare minimium, see meta data about it. Depending on copyright status, you might also be able to see sample pages or read the entire book. You can also buy books.</description><content:encoded><![CDATA[<p>There&rsquo;s a staggering amount of data out there - and a lot of it free - but accessing it isn&rsquo;t always easy. A good API hides the complexities of accessing that data, and can save you a ton of development time too. After writing about <a href="https://grantwinney.com/tags/15-apis-in-15-days/"  target="_blank" rel="noreferrer">15 APIs in 15 days</a> over the holidays, I&rsquo;ve decided to find a different <a href="https://grantwinney.com/tags/api/"  target="_blank" rel="noreferrer">API</a> to write about every Monday <em>(okay, so I&rsquo;m a day late this week&hellip;)</em>.</p>
<p>Two things before you get started:</p>
<ul>
<li>If you&rsquo;re unfamiliar with APIs, you might want to <a href="https://grantwinney.com/what-is-an-api/"  target="_blank" rel="noreferrer">read this first</a> to familiarize yourself with them.</li>
<li>You may want to install <a href="https://www.getpostman.com/"  target="_blank" rel="noreferrer">Postman</a>, which allows you to access API endpoints without having to write an app, as well as save the calls you make and sync them online between your computers.</li>
</ul>
<hr>

<h2 class="relative group">What does it provide?
    <div id="what-does-it-provide" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-does-it-provide" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The <a href="https://developers.google.com/books/"  target="_blank" rel="noreferrer">Google Books API</a> provides access to.. well&hellip; Google Books. <a href="https://books.google.com/intl/en/googlebooks/about/index.html"  target="_blank" rel="noreferrer">Google Books</a> lets you search for any book and, at a bare minimium, see meta data about it. Depending on copyright status or permission from the author, you might also be able to see sample pages or read the entire book. You can also buy books through their service.</p>
<p>Google Books has been around for over 15 years. Although the progress has apparently slowed down a lot in the last few years, amid legal battles and newer goals, it&rsquo;s still available&hellip; for now. Here&rsquo;s an interesting article about its history: <a href="https://www.wired.com/2017/04/how-google-book-search-got-lost/"  target="_blank" rel="noreferrer">How Google Book Search Got Lost</a></p>
<hr>

<h2 class="relative group">Taking it out for a spin
    <div id="taking-it-out-for-a-spin" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#taking-it-out-for-a-spin" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The easiest way to try it out is to use the <a href="https://developers.google.com/apis-explorer/?hl=en_US#p/books/v1/"  target="_blank" rel="noreferrer">APIs Explorer</a> tool they built on top of their own API. In order to access someone&rsquo;s private data, you&rsquo;ll need to have your app request access to their account. Similarly, <em>you&rsquo;re</em> the user of the APIs Explorer, so it&rsquo;ll request access to your account.</p>
<p>But first, a few others things you&rsquo;ll need to do to get ready first.</p>

<h3 class="relative group">Create an account
    <div id="create-an-account" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#create-an-account" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Sign in to your existing Google account, or <a href="https://www.google.com/accounts/NewAccount"  target="_blank" rel="noreferrer">create a new test account</a> if you like to keep things separate. I&rsquo;m using my normal Google account and it&rsquo;s working fine. YMMV.</p>

<h3 class="relative group">Find your user id
    <div id="find-your-user-id" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#find-your-user-id" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>It&rsquo;s easy to find your ID, even if it&rsquo;s not that obvious. According to the <a href="https://developers.google.com/books/docs/v1/using#ids"  target="_blank" rel="noreferrer">documentation on IDs</a>:</p>
<blockquote><p>The only way retrieve the user ID is to extract it from the selfLink in a Bookshelf resource retrieved with an authenticated request. Users can also obtain their own user ID from the Books site.</p>
</blockquote><p>Go to the <a href="https://books.google.com/"  target="_blank" rel="noreferrer">Google Books</a> site and click on &ldquo;My Library&rdquo;. Look in the address bar. You should see a URL like the following. Just note the ID.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">https://books.google.com/books?uid=12341234123412341234</span></span></code></pre></div></div>

<h3 class="relative group">Add a few books
    <div id="add-a-few-books" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#add-a-few-books" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>You should still be in your library. If you already have books available in your library, awesome. Otherwise, search for something free like <a href="https://books.google.com/books?id=u5MNAAAAYAAJ&amp;dq=alices%20adventures%20in%20wonderland&amp;pg=PA11#v=onepage&amp;q&amp;f=false"  target="_blank" rel="noreferrer">Alice in Wonderland</a> or <a href="https://books.google.com/books?id=RqlEAAAAYAAJ&amp;dq=Twenty%20Thousand%20Leagues%20Under%20the%20Sea&amp;pg=PP1#v=onepage&amp;q&amp;f=false"  target="_blank" rel="noreferrer">Twenty Thousand Leagues Under the Sea</a>, and click the <em>&ldquo;Add to my library&rdquo;</em> button at the top of the page.</p>

<h3 class="relative group">Create a bookshelf
    <div id="create-a-bookshelf" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#create-a-bookshelf" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>In your library, look for the red &ldquo;NEW SHELF&rdquo; button on the left. Click that and create a new shelf. Name it whatever you want, then select it from the list to open it up. I chose to make it private, but whatever.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="create new bookshelf"
    src="/what-is-the-google-books-api/create-new-bookshelf.png"
    width="1020"
      height="904"></figure>
<p>Note the URL. The bookshelf has an ID too, for example 9999 in the following example, so make a note of it too for later.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">https://books.google.com/books?uid=12341234123412341234&amp;as_coll=9999&amp;source=gbs_lp_bookshelf_list</span></span></code></pre></div></div>
<p>Look for the &ldquo;Search My Library&rdquo; box on the left. Search for those titles you added earlier, one at a time. Hover over the &ldquo;Add to my library&rdquo; button in the search results and select your new shelf to add them to it.</p>

<h3 class="relative group">Authorize the APIs Explorer app to access your account
    <div id="authorize-the-apis-explorer-app-to-access-your-account" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#authorize-the-apis-explorer-app-to-access-your-account" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Like I said earlier, you need to grant the app authorization to access your data, just like you would anything else. Go back to the <a href="https://developers.google.com/apis-explorer/?hl=en_US#p/books/v1/"  target="_blank" rel="noreferrer">API endpoints</a> page.</p>
<p>Click the &ldquo;OFF&rdquo; button next to <em>&ldquo;Authorize requests using OAuth 2.0&rdquo;</em> in the upper-right corner of the page. The APIs Explorer needs access to manage your books (obviously), so select the checkbox and press &ldquo;Authorize&rdquo;. You should get a popup - select the Google account where you&rsquo;ve got your books saved, and then click &ldquo;Allow&rdquo;.</p>
<p>You should be back where you started on the APIs Explorer page, but now the gray &ldquo;OFF&rdquo; toggle button should be a blue &ldquo;ON&rdquo; button. If you wait too long, you&rsquo;ll need to go through this process again. The &ldquo;authorization&rdquo; you grant only seems to last about a day, maybe less.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-the-google-books-api/google---authorize-access-to-books.png"
    width="711"
      height="763"></figure>

<h3 class="relative group">FINALLY! Let&rsquo;s ask for metadata about our new shelf
    <div id="finally-lets-ask-for-metadata-about-our-new-shelf" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#finally-lets-ask-for-metadata-about-our-new-shelf" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Go to <a href="https://developers.google.com/apis-explorer/?hl=en_US#p/books/v1/books.bookshelves.get"  target="_blank" rel="noreferrer">books.bookshelves.get</a>, and fill in the <code>userId</code> and <code>shelf</code> fields using the two IDs you grabbed earlier. This endpoint lists metadata about the shelf you created - in my case, I had only added one book when I called it.</p>
<p>Note that they provide you with the REST endpoint call, with your values in it. You could test things out in the APIs Explorer, then copy the call into your app. Convenient.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="get-bookshelf-metadata"
    src="/what-is-the-google-books-api/get-bookshelf-metadata.png"
    width="1426"
      height="1236"></figure>

<h3 class="relative group">Now let&rsquo;s ask for metadata about ALL our shelves
    <div id="now-lets-ask-for-metadata-about-all-our-shelves" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#now-lets-ask-for-metadata-about-all-our-shelves" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Click on <a href="https://developers.google.com/apis-explorer/?hl=en_US#p/books/v1/books.bookshelves.list"  target="_blank" rel="noreferrer">books.bookshelves.list</a>, and fill in your <code>userId</code> again. This endpoint lists metadata about all of your shelves, including:</p>
<ul>
<li>Everything listed in your library on the left-hand side (the names vary slightly)</li>
<li>&ldquo;My Google eBooks&rdquo;, which seems to correspond to &ldquo;My Books on Google Play&rdquo;</li>
<li>&ldquo;Purchased&rdquo;, &ldquo;Reviewed&rdquo;, and &ldquo;Recently viewed&rdquo;, which don&rsquo;t show in the list at all</li>
</ul>
<p>Since it includes IDs for everything, you could display a list like this to a user, let them select one, then use the ID for whatever you need. If you just created an account, or haven&rsquo;t used Google Books before, you may not have some of those shelves.</p>

<h3 class="relative group">One more&hellip; detailed info on all volumes (books) in our shelf
    <div id="one-more-detailed-info-on-all-volumes-books-in-our-shelf" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#one-more-detailed-info-on-all-volumes-books-in-our-shelf" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Click on <a href="https://developers.google.com/apis-explorer/?hl=en_US#p/books/v1/books.bookshelves.volumes.list"  target="_blank" rel="noreferrer">books.bookshelves.volumes.list</a>, and fill in your <code>userId</code> and <code>shelf</code> IDs again. This endpoint lists detailed information about the volumes (books) in your shelf.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="get-all-volumes-in-bookshelf"
    src="/what-is-the-google-books-api/get-all-volumes-in-bookshelf.png"
    width="1374"
      height="1388"></figure>
<p>Note that the results include the ID for the book, like &ldquo;Y7sOAAAAIAAJ&rdquo; in the above results. You can use that ID with endpoints like <a href="https://developers.google.com/apis-explorer/?hl=en_US#p/books/v1/books.volumes.get?volumeId=Y7sOAAAAIAAJ"  target="_blank" rel="noreferrer">books.volumes.get</a> that expect a <code>volumeId</code> value.</p>
<hr>

<h2 class="relative group">Next Steps
    <div id="next-steps" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#next-steps" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>When you&rsquo;re ready to starting creating an app that uses the API, you&rsquo;ll need to create an &ldquo;application&rdquo; in Google&rsquo;s system that represents the app you&rsquo;re building. Then you&rsquo;ll use that to request authorization to your users&rsquo; accounts. Based on the sensitivity of the data you&rsquo;re requesting, you may need an OAuth 2.0 token (more secure) or an API key.</p>
<p>I don&rsquo;t intend to get into all the intricacies of that though - this took longer to setup and try out than I thought it would. Google has a decent <a href="https://developers.google.com/books/docs/v1/getting_started"  target="_blank" rel="noreferrer">Getting Started</a> guide you should check out.</p>
<p>I did play with the API key a bit <em>(I&rsquo;ll try OAuth 2.0 at some point).</em> It requires you to:</p>
<ol>
<li>Create a bookshelf like I explained above (and note the bookshelf id, aka &ldquo;as_coll&rdquo;)</li>
<li>Go to <a href="https://books.google.com/books"  target="_blank" rel="noreferrer">https://books.google.com/books</a> if you&rsquo;re not there already, find the bookshelf, click the gear and &ldquo;edit properties&rdquo;, then set the visibility to &ldquo;public&rdquo;.</li>
<li>Go to <a href="https://console.developers.google.com/apis/library/books.googleapis.com"  target="_blank" rel="noreferrer">https://console.developers.google.com/apis/library/books.googleapis.com</a> and select a project at the top (or create a new one from the same screen), and enable the API.</li>
<li>Go to <a href="https://console.developers.google.com/projectselector/apis/credentials"  target="_blank" rel="noreferrer">https://console.developers.google.com/projectselector/apis/credentials</a>, select your project from the drop-down, then &ldquo;create credentials&rdquo; and &ldquo;api key&rdquo; to create an API key for the request.</li>
<li>Open Postman and do a <code>GET https://www.googleapis.com/books/v1/users/&lt;your-user-id&gt;/bookshelves/&lt;as-coll-id&gt;?key=&lt;api-key&gt;</code></li>
</ol>
<p>If you want to read about a real-life implementation of this API, here&rsquo;s an article I came across recently: <a href="https://medium.freecodecamp.org/build-a-best-sellers-list-with-new-york-times-google-books-api-46201c30aec7"  target="_blank" rel="noreferrer">Build a Best Sellers List with New York Times and Google Books API</a></p>
<hr>

<h2 class="relative group">Observations
    <div id="observations" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#observations" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Not too much else to say, except one thing I noticed in the <a href="https://developers.google.com/books/terms"  target="_blank" rel="noreferrer">terms of service</a>. The terms dictate that you can&rsquo;t charge a fee for any app that uses their API service.</p>
<blockquote><p>You may not charge users any fee for the use of your application, unless you have entered into a separate agreement with Google or obtained Google&rsquo;s written permission.</p>
</blockquote><p>This seems silly to me, as (1) it&rsquo;s not like Google&rsquo;s motives are altruistic anyway - they <em>sell</em> books through the service, and (2) an app that uses their API ostensibly provides end-users with more features than just the API, and you should be free to charge for your time and service. Requiring you to publicize that you use the Google Books API would make more sense to me, as it&rsquo;s free advertising for them.</p>
<p>Then again, Google can&rsquo;t even keep its own store free of junk apps that most likely violate their own TOS, so the odds of them randomly catching your application seems pretty slim&hellip;</p>
]]></content:encoded><media:content url="https://grantwinney.com/what-is-the-google-books-api/feature.webp" medium="image" type="image/webp"/></item><item><title>What is an API wrapper?</title><link>https://grantwinney.com/what-is-an-api-wrapper/</link><pubDate>Thu, 25 Jan 2018 04:59:32 +0000</pubDate><guid>https://grantwinney.com/what-is-an-api-wrapper/</guid><description>When you find an API to use in your app, you&amp;rsquo;ll need to access it in a specific language - not always an easy or straightforward task. As long as you&amp;rsquo;re doing all that work, why keep it to yourself? Let&amp;rsquo;s look at creating an API wrapper that you can share with others!</description><content:encoded><![CDATA[<p>Forget about peeling back the layers - today we&rsquo;re gonna talk about <em>adding</em> layers.</p>
<p>When you find an API that looks interesting, you&rsquo;ll naturally want to try it out. I&rsquo;ve <a href="https://grantwinney.com/tags/api/"  target="_blank" rel="noreferrer">tested and written quite a bit about APIs</a>, and most of the time I start off with a tool like <a href="https://www.getpostman.com/"  target="_blank" rel="noreferrer">Postman</a>. That&rsquo;s fine for playing around, but eventually you&rsquo;ll want to use it in your app.</p>
<p>APIs come in all shapes and sizes. Some are dead simple; others are amazingly complex - even overly complicated at times. It takes time to implement it in a language - to figure out the right way to access <em>any</em> REST endpoint, then to figure out the right way to access a <em>specific</em> endpoint and get the data you&rsquo;re interested in. As long as you&rsquo;re doing all that work, why keep it to yourself?</p>
<p>You can share the work you&rsquo;ve done - maybe elaborating to cover all of an API&rsquo;s endpoints, or maybe letting others make pull requests to fill in the gaps. Hopefully the language you&rsquo;re using has some concept of a library, package, or some other way to bundle code up for easy sharing, but that&rsquo;s not necessarily necessary.</p>

<h2 class="relative group">API Wrapper in Python
    <div id="api-wrapper-in-python" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#api-wrapper-in-python" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Several weeks ago, I wrote a few one-off Python scripts to demo accessing the <a href="https://grantwinney.com/what-is-iss-notify-api/"  target="_blank" rel="noreferrer">ISS Notify API</a>. I won&rsquo;t repost them here, but go check them out before looking at how we can refactor them. Notice how much code is duplicated between them.</p>
<p>What if we wanted to make another call to the same endpoint? Or a call to a closely related endpoint? Or 10 more calls with different parameters? And what if someone else wanted to make the same calls as you but wasn&rsquo;t sure where to start? Maybe we can help them out.</p>
<p>What I call an API wrapper is really quite simple - just some nice, clean functions to access the API, published somewhere accessible like GitHub. Tada.</p>
<p>Here&rsquo;s two of the Python scripts from the other post, refactored into a single file that I named <code>iss_api_wrapper.py</code>.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">urllib2</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">json</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">datetime</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">_call_api</span><span class="p">(</span><span class="n">endpoint</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">request</span> <span class="o">=</span> <span class="n">urllib2</span><span class="o">.</span><span class="n">Request</span><span class="p">(</span><span class="s1">&#39;http://api.open-notify.org</span><span class="si">%s</span><span class="s1">&#39;</span> <span class="o">%</span><span class="p">(</span><span class="n">endpoint</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span> <span class="o">=</span> <span class="n">urllib2</span><span class="o">.</span><span class="n">urlopen</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">read</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">show_roster</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">result</span> <span class="o">=</span> <span class="n">_call_api</span><span class="p">(</span><span class="s1">&#39;/astros.json&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span> <span class="s2">&#34;There are </span><span class="si">%d</span><span class="s2"> people in space:&#34;</span> <span class="o">%</span> <span class="p">(</span><span class="n">result</span><span class="p">[</span><span class="s1">&#39;number&#39;</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">result</span><span class="p">[</span><span class="s1">&#39;number&#39;</span><span class="p">]):</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span> <span class="n">result</span><span class="p">[</span><span class="s1">&#39;people&#39;</span><span class="p">][</span><span class="n">i</span><span class="p">][</span><span class="s1">&#39;name&#39;</span><span class="p">]</span> <span class="o">+</span> <span class="s2">&#34;,&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">show_next_pass</span><span class="p">(</span><span class="n">latitude</span><span class="p">,</span> <span class="n">longitude</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">result</span> <span class="o">=</span> <span class="n">_call_api</span><span class="p">(</span><span class="s1">&#39;/iss-pass.json?lat=</span><span class="si">%s</span><span class="s1">&amp;lon=</span><span class="si">%s</span><span class="s1">&#39;</span> <span class="o">%</span><span class="p">(</span><span class="n">latitude</span><span class="p">,</span> <span class="n">longitude</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;The next ISS pass for </span><span class="si">%s</span><span class="s1"> </span><span class="si">%s</span><span class="s1"> is </span><span class="si">%s</span><span class="s1"> for </span><span class="si">%s</span><span class="s1"> seconds&#39;</span>
</span></span><span class="line"><span class="cl">          <span class="o">%</span><span class="p">(</span><span class="n">result</span><span class="p">[</span><span class="s1">&#39;request&#39;</span><span class="p">][</span><span class="s1">&#39;latitude&#39;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="n">result</span><span class="p">[</span><span class="s1">&#39;request&#39;</span><span class="p">][</span><span class="s1">&#39;longitude&#39;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">fromtimestamp</span><span class="p">(</span><span class="n">result</span><span class="p">[</span><span class="s1">&#39;response&#39;</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="s1">&#39;risetime&#39;</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl">            <span class="n">result</span><span class="p">[</span><span class="s1">&#39;response&#39;</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="s1">&#39;duration&#39;</span><span class="p">]))</span></span></span></code></pre></div></div>
<p>Place the file in a directory called <code>iss</code> along with an empty file named <code>_init_.py</code>. Create another file <em>outside</em> the directory called <code>use_iss.py</code> (or whatever you want) and call the functions in your new module: <em>(or just</em> <a href="https://github.com/grantwinney/BlogCodeSamples/tree/master/APIs/IssNotifyApiWrapper/Python"  target="_blank" rel="noreferrer"><em>download the scripts from GitHub</em></a><em>)</em></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">iss.iss_api_wrapper</span> <span class="k">as</span> <span class="nn">iss</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">iss</span><span class="o">.</span><span class="n">show_roster</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span> <span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">iss</span><span class="o">.</span><span class="n">show_next_pass</span><span class="p">(</span><span class="s1">&#39;41.4984174&#39;</span><span class="p">,</span> <span class="s1">&#39;-81.6937287&#39;</span><span class="p">)</span></span></span></code></pre></div></div>
<p>Output:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">There are 6 people in space: Alexander Misurkin, Mark Vande Hei, Joe Acaba, Anton Shkaplerov, Scott Tingle, Norishige Kanai,

The next ISS pass for 41.4984174 -81.6937287 is 2018-01-24 17:37:00 for 254 seconds</code></pre></div>
<p>Now you can share your nice module / API wrapper with the world. If the API endpoints change in the future, you can change the calls being made in your Python code, but anyone using your module will be none the wiser!</p>

<h2 class="relative group">API Wrapper in C#
    <div id="api-wrapper-in-c" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#api-wrapper-in-c" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Let&rsquo;s try the same thing one more time, in C# this time.</p>
<p>I wrapped all three examples from the <a href="https://grantwinney.com/what-is-iss-notify-api/"  target="_blank" rel="noreferrer">ISS Notify API</a> post. This code depends on the <a href="https://www.nuget.org/packages/RestSharp/"  target="_blank" rel="noreferrer">RestSharp NuGet package</a> <em>(just discovered it; made accessing the endpoint simple)</em> and some classes I had to defined but didn&rsquo;t want to paste below - you can <a href="https://github.com/grantwinney/BlogCodeSamples/tree/master/APIs/IssNotifyApiWrapper/CSharp"  target="_blank" rel="noreferrer">find everything on GitHub</a>. You can <a href="https://grantwinney.com/what-is-iss-notify-api/"  target="_blank" rel="noreferrer">see the original JSON output from the API</a> here.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">System.Collections.Generic</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">System.Linq</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">RestSharp</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">namespace</span> <span class="nn">ISS_Notify_Wrapper</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="k">class</span> <span class="nc">ISS</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">private</span> <span class="kd">static</span> <span class="n">T</span> <span class="n">GetResource</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;(</span><span class="kt">string</span> <span class="n">description</span><span class="p">,</span> <span class="kt">string</span> <span class="n">resource</span><span class="p">,</span> <span class="n">Tuple</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span><span class="kt">string</span><span class="p">&gt;[]</span> <span class="n">parameters</span> <span class="p">=</span> <span class="kc">null</span><span class="p">)</span> <span class="k">where</span> <span class="n">T</span> <span class="p">:</span> <span class="k">new</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">client</span> <span class="p">=</span> <span class="k">new</span> <span class="n">RestClient</span> <span class="p">{</span> <span class="n">BaseUrl</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Uri</span><span class="p">(</span><span class="s">&#34;http://api.open-notify.org&#34;</span><span class="p">)</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">request</span> <span class="p">=</span> <span class="k">new</span> <span class="n">RestRequest</span><span class="p">(</span><span class="n">resource</span><span class="p">,</span> <span class="n">Method</span><span class="p">.</span><span class="n">GET</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">parameters</span> <span class="p">!=</span> <span class="kc">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">param</span> <span class="k">in</span> <span class="n">parameters</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                    <span class="n">request</span><span class="p">.</span><span class="n">AddParameter</span><span class="p">(</span><span class="n">param</span><span class="p">.</span><span class="n">Item1</span><span class="p">,</span> <span class="n">param</span><span class="p">.</span><span class="n">Item2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">response</span> <span class="p">=</span> <span class="n">client</span><span class="p">.</span><span class="n">Execute</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;(</span><span class="n">request</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">content</span> <span class="p">=</span> <span class="n">response</span><span class="p">.</span><span class="n">Content</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="n">ErrorException</span> <span class="p">!=</span> <span class="kc">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="k">throw</span> <span class="k">new</span> <span class="n">ApplicationException</span><span class="p">(</span><span class="s">$&#34;Unable to retrieve {description}.&#34;</span><span class="p">,</span> <span class="n">response</span><span class="p">.</span><span class="n">ErrorException</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">response</span><span class="p">.</span><span class="n">Data</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kd">static</span> <span class="k">void</span> <span class="n">ShowRoster</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">roster</span> <span class="p">=</span> <span class="n">GetResource</span><span class="p">&lt;</span><span class="n">Roster</span><span class="p">&gt;(</span><span class="s">&#34;roster&#34;</span><span class="p">,</span> <span class="s">&#34;astros.json&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">astronautNames</span> <span class="p">=</span> <span class="n">String</span><span class="p">.</span><span class="n">Join</span><span class="p">(</span><span class="s">&#34;, &#34;</span><span class="p">,</span> <span class="n">roster</span><span class="p">.</span><span class="n">People</span><span class="p">.</span><span class="n">Select</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">Name</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">          
</span></span><span class="line"><span class="cl">            <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;There are {roster.Number} people in space: {astronautNames}&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kd">static</span> <span class="k">void</span> <span class="n">ShowUpcomingPasses</span><span class="p">(</span><span class="kt">string</span> <span class="n">latitude</span><span class="p">,</span> <span class="kt">string</span> <span class="n">longitude</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">nextPass</span> <span class="p">=</span> <span class="n">GetResource</span><span class="p">&lt;</span><span class="n">Passes</span><span class="p">&gt;(</span><span class="s">&#34;next pass&#34;</span><span class="p">,</span> <span class="s">&#34;iss-pass.json&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="k">new</span><span class="p">[]</span> <span class="p">{</span> <span class="n">Tuple</span><span class="p">.</span><span class="n">Create</span><span class="p">(</span><span class="s">&#34;lat&#34;</span><span class="p">,</span> <span class="n">latitude</span><span class="p">),</span> <span class="n">Tuple</span><span class="p">.</span><span class="n">Create</span><span class="p">(</span><span class="s">&#34;lon&#34;</span><span class="p">,</span> <span class="n">longitude</span><span class="p">)</span> <span class="p">}).</span><span class="n">Response</span><span class="p">[</span><span class="m">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;The next ISS pass for {latitude} {longitude} is &#34;</span> <span class="p">+</span>
</span></span><span class="line"><span class="cl">                              <span class="s">$&#34;{DateTimeOffset.FromUnixTimeSeconds(nextPass.Risetime)} &#34;</span> <span class="p">+</span>
</span></span><span class="line"><span class="cl">                              <span class="s">$&#34;for {nextPass.Duration} seconds.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kd">static</span> <span class="k">void</span> <span class="n">ShowCurrentLocation</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">pos</span> <span class="p">=</span> <span class="n">GetResource</span><span class="p">&lt;</span><span class="n">Position</span><span class="p">&gt;(</span><span class="s">&#34;next pass&#34;</span><span class="p">,</span> <span class="s">&#34;iss-now.json&#34;</span><span class="p">).</span><span class="n">IssPosition</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;The current position is: {pos.Latitude} {pos.Longitude}&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>And now you&rsquo;ve got a nice wrapper that anyone can download and use, without them having to know exactly how the ISS Notify API looks, or rethink the same logic you already spent time coding.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">ISS</span><span class="p">.</span><span class="n">ShowRoster</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="n">ISS</span><span class="p">.</span><span class="n">ShowUpcomingPasses</span><span class="p">(</span><span class="s">&#34;41.4984174&#34;</span><span class="p">,</span> <span class="s">&#34;-81.6937287&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">ISS</span><span class="p">.</span><span class="n">ShowCurrentLocation</span><span class="p">();</span></span></span></code></pre></div></div>
<p>Output:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">There are 6 people in space: Alexander Misurkin, Mark Vande Hei, Joe Acaba, Anton Shkaplerov, Scott Tingle, Norishige Kanai
The next ISS pass for 41.4984174 -81.6937287 is 1/25/2018 3:23:45 AM +00:00 for 563 seconds.
The current position is: -47.0603 147.0037

Press any key to continue...</code></pre></div>

<h2 class="relative group">Learning More&hellip;
    <div id="learning-more" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#learning-more" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you want to check out another example, I wrote <a href="https://grantwinney.com/ghostsharp/"  target="_blank" rel="noreferrer">GhostSharp</a> as a C# wrapper around the API that&rsquo;s built into the Ghost engine that this blog runs on.</p>
<p>What do you think? Are you working on an API wrapper, or thinking about it? I&rsquo;d love to check it out - let me know or just share your thoughts below!</p>
]]></content:encoded><media:content url="https://grantwinney.com/what-is-an-api-wrapper/feature.webp" medium="image" type="image/webp"/></item><item><title>How to autolink all images in Ghost to the full resolution</title><link>https://grantwinney.com/ghost-blog-autolink-images-to-full-resolution/</link><pubDate>Mon, 22 Jan 2018 03:41:27 +0000</pubDate><guid>https://grantwinney.com/ghost-blog-autolink-images-to-full-resolution/</guid><description/><content:encoded><![CDATA[
<h2 class="relative group">The Problem
    <div id="the-problem" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-problem" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>When you insert an image into a post using the Ghost platform, it might be displayed as much smaller, depending on your theme. Viewing the full version means either opening the image in a new tab or linking the image to itself so that clicking on it opens it up outside of the context of the page. Either way, someone&rsquo;s doing manual work, so let&rsquo;s automate it!</p>

<h2 class="relative group">The Solution
    <div id="the-solution" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-solution" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Here&rsquo;s a snippet of JavaScript code you can insert under &ldquo;Blog Header&rdquo; in the &ldquo;Code injection&rdquo; section of your Ghost blog, or in a separate file if you&rsquo;re self-hosting and have access to where the themes are uploaded.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nx">script</span> <span class="nx">type</span><span class="o">=</span><span class="s2">&#34;text/javascript&#34;</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s2">&#34;DOMContentLoaded&#34;</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">document</span><span class="p">.</span><span class="nx">querySelectorAll</span><span class="p">(</span><span class="s1">&#39;.post-content img&#39;</span><span class="p">).</span><span class="nx">forEach</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">image</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">image</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">cursor</span> <span class="o">=</span> <span class="s2">&#34;zoom-in&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">image</span><span class="p">.</span><span class="nx">title</span> <span class="o">===</span> <span class="s1">&#39;&#39;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">image</span><span class="p">.</span><span class="nx">title</span> <span class="o">+=</span> <span class="s2">&#34;(click to zoom)&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">image</span><span class="p">.</span><span class="nx">title</span> <span class="o">+=</span> <span class="s2">&#34; (click to zoom)&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="nx">image</span><span class="p">.</span><span class="nx">onclick</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">href</span> <span class="o">=</span> <span class="nx">image</span><span class="p">.</span><span class="nx">src</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="err">/script&gt;</span></span></span></code></pre></div></div>

<h2 class="relative group">What&rsquo;s it do?
    <div id="whats-it-do" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whats-it-do" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>This selects all image elements under the element that has the <code>.post-content</code> class applied to it, which is the main body of your post&rsquo;s content, and makes them clickable to display the full image. You probably don&rsquo;t want to apply this script to <em>every</em> image on your site, but you can change change the selector as needed. Read more about <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll"  target="_blank" rel="noreferrer">querySelectorAll</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors"  target="_blank" rel="noreferrer">selectors</a> at MDN.</p>
<p>It does this when the <a href="https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded"  target="_blank" rel="noreferrer">DOMContentLoaded</a> event fires, which occurs after the HTML is rendered but before any resources (images, stylesheets, etc) are downloaded, this code. That way, we can be sure the image placeholders are present, even if the images themselves are not yet.</p>
<p>It loops through each image, doing three things:</p>
<ul>
<li>The cursor is changed to &ldquo;zoom-in&rdquo;, signaling that an action can be taken.</li>
<li>The title is changed, again to signal that an action can be taken.</li>
<li>A click event is assigned, that navigates to the image&rsquo;s source.</li>
</ul>
]]></content:encoded><media:content url="https://grantwinney.com/ghost-blog-autolink-images-to-full-resolution/feature.webp" medium="image" type="image/webp"/></item><item><title>Access Game Data with the IGDB API v4</title><link>https://grantwinney.com/what-is-internet-game-database-api/</link><pubDate>Mon, 01 Jan 2018 05:00:00 +0000</pubDate><guid>https://grantwinney.com/what-is-internet-game-database-api/</guid><description>The Internet Game Database is a community-driven site that collects and shares information about games and game-related data. Let&amp;rsquo;s check out the IGDB API!</description><content:encoded><![CDATA[<p>The <a href="https://github.com/twitchtv/igdb-contribution-guidelines/wiki"  target="_blank" rel="noreferrer">Internet Game Database (IGDB)</a> is a community-driven site, now owned by Twitch, that collects and shares information about games and game-related data. They have an API to support the mission, so today let&rsquo;s check out the <a href="https://www.igdb.com/api"  target="_blank" rel="noreferrer">IGDB API</a>.</p>
<p>First though, two things to consider:</p>
<ul>
<li>If you&rsquo;re unfamiliar with APIs, you might want to <a href="https://grantwinney.com/what-is-an-api/"  target="_blank" rel="noreferrer">read this first</a>.</li>
<li>Install <a href="https://www.getpostman.com/"  target="_blank" rel="noreferrer">Postman</a>, which allows you to access API endpoints without having to write an app, as well as save the calls you make and sync them online.</li>
</ul>

<h2 class="relative group">Create an Account
    <div id="create-an-account" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#create-an-account" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There&rsquo;s a &ldquo;<a href="https://api-docs.igdb.com/#getting-started"  target="_blank" rel="noreferrer">Getting Started</a>&rdquo; guide you should read, but here&rsquo;s the basic steps:</p>
<ul>
<li><a href="https://api.igdb.com/signup"  target="_blank" rel="noreferrer">Sign up for a Twitch account</a></li>
<li>Setup 2FA in your <a href="https://www.twitch.tv/settings/security"  target="_blank" rel="noreferrer">profile</a> - you can&rsquo;t do the next step without it</li>
<li>Open the <a href="https://dev.twitch.tv/console/apps/create"  target="_blank" rel="noreferrer">Twitch Developer Portal</a> and create a new &ldquo;application&rdquo;
<ul>
<li>Redirect URL: https://localhost</li>
<li>Fill in anything for the name and category, and leave client type as-is</li>
</ul>
</li>
<li>Click the &ldquo;Manage&rdquo; button, scroll down, and write down the Client ID and Client Secret (after you create one).</li>
</ul>
<p>Create an &ldquo;application&rdquo;:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-internet-game-database-api/image-8.png"
    width="947"
      height="502"></figure>
<p>Click &ldquo;Manage&rdquo;:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-internet-game-database-api/image-9.png"
    width="963"
      height="142"></figure>
<p>Get a Client ID and Client Secret so you can use the API:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-internet-game-database-api/image-10.png"
    width="680"
      height="238"></figure>

<h2 class="relative group">Authorization
    <div id="authorization" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#authorization" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The same doc I mentioned above tells you how to authenticate too.</p>
<ul>
<li>Do a <code>POST</code> to the endpoint they provide: <em>(Postman makes this easy)</em><br>
<a href="https://id.twitch.tv/oauth2/token?client_id=43w672tdd57vyfb9hzbin46akrsfjr&amp;client_secret=44kjbao6xl57fd7lljyd04q8r2699u&amp;grant_type=client_credentials"  target="_blank" rel="noreferrer"><code>https://id.twitch.tv/oauth2/token?client_id={client_id}&amp;client_secret={client_secret}&amp;grant_type=client_credentials</code></a></li>
<li>Note the access token you get back in the little block of JSON</li>
</ul>
<p>Every request you make after this will have two headers:</p>
<ul>
<li><code>Accept: application/json</code></li>
<li><code>Client-ID: &lt;same-client-id-as-before&gt;</code></li>
</ul>
<p>And under the Authorization tab, choose &ldquo;Bearer Token&rdquo; and set the Token to whatever value you just got back in the JSON above.</p>

<h2 class="relative group">Find Games
    <div id="find-games" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#find-games" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>When I first wrote this years ago for the first version of the API, it seemed a lot easier. Somehow, since Twitch got it I guess, it&rsquo;s gotten more complicated to use. It seems like they were going for extreme flexibility, but instead it&rsquo;s just counterintuitive.</p>
<p>To &ldquo;get&rdquo; game data, for instance, we need to do a <code>POST</code> (ugh) to <code>https://api.igdb.com/v4/games</code>, then pass a query via the body (set to &ldquo;raw&rdquo; and &ldquo;text&rdquo;) like this:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">fields id,name,rating;
where id=1025;</code></pre></div>
<p>That&rsquo;ll return JSON with information about one particular game, but it&rsquo;s so odd looking.. kind of a pseudo-sql query I guess. I don&rsquo;t know how we&rsquo;re supposed to know when to use a SQL keyword and when not to, or when an <code>=</code> sign is appropriate or why it doesn&rsquo;t need to be there after &ldquo;fields&rdquo;.</p>
<p>Looking at the <a href="https://api-docs.igdb.com/?shell#game"  target="_blank" rel="noreferrer">docs for the <code>games</code> endpoint</a>, I&rsquo;m also not sure what to use if I don&rsquo;t know the exact ID and just want anything starting with Zelda.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">1025</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Zelda II: The Adventure of Link&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;rating&#34;</span><span class="p">:</span> <span class="mf">66.72145408447821</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">]</span></span></span></code></pre></div></div>
<p>Conveniently but strangely, it appears that many of the old <code>GET</code> requests from several versions ago still work. Like, I can do this to get all zelda games:</p>
<p><code>https://api.igdb.com/v4/games/?search=zelda&amp;fields=id,name,rating</code></p>
<p>And it returns a reasonable block of JSON. I can work with that, grabbing the ID from the one(s) I&rsquo;m interested in, and then querying again for the full record.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">1025</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Zelda II: The Adventure of Link&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;rating&#34;</span><span class="p">:</span> <span class="mf">66.72145408447821</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">1022</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;The Legend of Zelda&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;rating&#34;</span><span class="p">:</span> <span class="mf">80.37884373313888</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">1041</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;The Legend of Zelda: Oracle of Ages&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;rating&#34;</span><span class="p">:</span> <span class="mf">84.5419859364635</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">1032</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;The Legend of Zelda: Oracle of Seasons&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;rating&#34;</span><span class="p">:</span> <span class="mf">81.7140839079194</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">150080</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;BS The Legend of Zelda \&#34;MottZilla Patch\&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">45142</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;The Legend of Zelda: Ocarina of Time - Master Quest&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;rating&#34;</span><span class="p">:</span> <span class="mf">89.99133378448971</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">237895</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;The Legend of Zelda: Breath of the Wild and The Legend of Zelda: Breath of the Wild Expansion Pass Bundle&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">38319</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;BS Zelda no Densetsu&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">152362</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Zelda&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">1029</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;The Legend of Zelda: Ocarina of Time&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;rating&#34;</span><span class="p">:</span> <span class="mf">91.7500985899066</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">]</span></span></span></code></pre></div></div>
<p>The old endpoints don&rsquo;t seem to be documented anymore though, unless I missed them, and I don&rsquo;t see a field in the new docs that provides this same flexibility.</p>

<h2 class="relative group">Thoughts
    <div id="thoughts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#thoughts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There&rsquo;s plenty of documentation on <a href="https://api-docs.igdb.com/#endpoints"  target="_blank" rel="noreferrer">the available endpoints</a>, and I have no doubt there&rsquo;s a <em>ton</em> of data available here. It&rsquo;s just that, after spending an hour updating this post, I have the distinct feeling that the latest version of this API aren&rsquo;t necessarily an improvement over the original one.</p>
<p>If you agree with me, or you think I&rsquo;m clueless and need to give it another look, let me know below. The pseudo-query thing is kind of interesting in an academic way, but it seems like it&rsquo;d be aggravating to have to figure out if I really needed to use this API on a daily basis.</p>
]]></content:encoded><media:content url="https://grantwinney.com/what-is-internet-game-database-api/feature.webp" medium="image" type="image/webp"/></item><item><title>Access Book and Author Data with the Penguin Random House API</title><link>https://grantwinney.com/what-is-penguin-random-house-api/</link><pubDate>Sun, 31 Dec 2017 20:20:17 +0000</pubDate><guid>https://grantwinney.com/what-is-penguin-random-house-api/</guid><description>Penguin Random House is a book publisher, and their API can be used to get data about books, authors and events. Let&amp;rsquo;s check it out!</description><content:encoded><![CDATA[<p>Penguin Random House is a book publisher, and their <a href="http://www.penguinrandomhouse.biz/webservices/rest/"  target="_blank" rel="noreferrer">API</a> can be used to get data about books, authors and events. Let&rsquo;s check it out!</p>
<p>First though, two things to consider:</p>
<ul>
<li>If you&rsquo;re unfamiliar with APIs, you might want to <a href="https://grantwinney.com/what-is-an-api/"  target="_blank" rel="noreferrer">read this first</a> to familiarize yourself.</li>
<li>Install <a href="https://www.getpostman.com/"  target="_blank" rel="noreferrer">Postman</a>, which allows you to access API endpoints without having to write an app, as well as save the calls you make and sync them online.</li>
</ul>
<p>Normally the first thing you have to worry about is some form of authorization and getting an API key. This API doesn&rsquo;t require or even offer it though, so&hellip; let&rsquo;s make some requests!</p>

<h2 class="relative group">Find Authors
    <div id="find-authors" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#find-authors" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Try looking up an author such as Isaac Asimov. You can specify a first and last name, and most likely you&rsquo;ll get multiple <code>author</code> records back - especially if you search for a popular name like &ldquo;Smith&rdquo; or something.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET https://reststop.randomhouse.com/resources/authors?lastName=Asimov&amp;firstName=Isaac</span></span></code></pre></div></div>
<p>Here&rsquo;s a very small sample of the returned payload. You get the name back as well as an <code>authorid</code> (we&rsquo;ll use that in a moment), a short bio, and a list of titles (Asimov wrote a <em>lot</em> of books).</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="err">&lt;?xml</span> <span class="err">version=</span><span class="s2">&#34;1.0&#34;</span> <span class="err">encoding=</span><span class="s2">&#34;UTF-8&#34;</span> <span class="err">standalone=</span><span class="s2">&#34;yes&#34;</span><span class="err">?&gt;</span>
</span></span><span class="line"><span class="cl"><span class="err">&lt;authors</span> <span class="err">uri=</span><span class="s2">&#34;https://reststop.randomhouse.com/resources/authors?lastName=Asimov&amp;amp;firstName=Isaac&#34;</span><span class="err">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;author</span> <span class="err">uri=</span><span class="s2">&#34;https://reststop.randomhouse.com/resources/authors/947&#34;</span><span class="err">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;approved&gt;X&lt;/approved&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;authordisplay&gt;Isaac</span> <span class="err">Asimov&lt;/authordisplay&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;authorfirst&gt;Isaac&lt;/authorfirst&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;authorfirstlc&gt;isaac&lt;/authorfirstlc&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;authorid&gt;</span><span class="mi">947</span><span class="err">&lt;/authorid&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;authorlast&gt;Asimov&lt;/authorlast&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;authorlastfirst&gt;ASIMOV,</span> <span class="err">ISAAC&lt;/authorlastfirst&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;authorlastlc&gt;asimov&lt;/authorlastlc&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;titles&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="err">&lt;isbn</span> <span class="err">contributortype=</span><span class="s2">&#34;A&#34;</span><span class="err">&gt;</span><span class="mi">9780307488633</span><span class="err">&lt;/isbn&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="err">&lt;isbn</span> <span class="err">contributortype=</span><span class="s2">&#34;A&#34;</span><span class="err">&gt;</span><span class="mi">9780307490247</span><span class="err">&lt;/isbn&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="err">&lt;isbn</span> <span class="err">contributortype=</span><span class="s2">&#34;A&#34;</span><span class="err">&gt;</span><span class="mi">9780307573537</span><span class="err">&lt;/isbn&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="err">...</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;/titles&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;lastinitial&gt;a&lt;/lastinitial&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;spotlight&gt;&amp;lt;b&amp;gt;Isaac</span> <span class="err">Asimov&amp;lt;/b&amp;gt;&amp;amp;nbsp;began</span> <span class="err">his</span> <span class="err">Foundation</span> <span class="err">series</span> <span class="err">at</span> <span class="err">the</span> <span class="err">age</span> <span class="err">of</span> <span class="mi">21</span><span class="err">,</span> <span class="err">not</span> <span class="err">realizing</span> <span class="err">that</span> <span class="err">it</span> <span class="err">would</span> <span class="err">one</span> <span class="err">day</span> <span class="err">be</span> <span class="err">considered</span> <span class="err">a</span> <span class="err">cornerstone</span> <span class="err">of</span> <span class="err">science</span> <span class="err">fiction.</span> <span class="err">During</span> <span class="err">his</span> <span class="err">legendary</span> <span class="err">career,</span> <span class="err">Asimov</span> <span class="err">penned</span> <span class="err">over</span> <span class="mi">470</span> <span class="err">books</span> <span class="err">on</span> <span class="err">subjects</span> <span class="err">ranging</span> <span class="err">from</span> <span class="err">science</span> <span class="err">to</span> <span class="err">Shakespeare</span> <span class="err">to</span> <span class="err">history,</span> <span class="err">though</span> <span class="err">he</span> <span class="err">was</span> <span class="err">most</span> <span class="err">loved</span> <span class="err">for</span> <span class="err">his</span> <span class="err">award-winning</span> <span class="err">science</span> <span class="err">fiction</span> <span class="err">sagas,</span> <span class="err">which</span> <span class="err">include</span> <span class="err">the</span> <span class="err">Robot,</span> <span class="err">Empire,</span> <span class="err">and</span> <span class="err">Foundation</span> <span class="err">series.</span> <span class="err">Named</span> <span class="err">a</span> <span class="err">Grand</span> <span class="err">Master</span> <span class="err">of</span> <span class="err">Science</span> <span class="err">Fiction</span> <span class="err">by</span> <span class="err">the</span> <span class="err">Science</span> <span class="err">Fiction</span> <span class="err">and</span> <span class="err">Fantasy</span> <span class="err">Writers</span> <span class="err">of</span> <span class="err">America,</span> <span class="err">Asimov</span> <span class="err">entertained</span> <span class="err">and</span> <span class="err">educated</span> <span class="err">readers</span> <span class="err">of</span> <span class="err">all</span> <span class="err">ages</span> <span class="err">for</span> <span class="err">close</span> <span class="err">to</span> <span class="err">five</span> <span class="err">decades.</span> <span class="err">He</span> <span class="err">died,</span> <span class="err">at</span> <span class="err">the</span> <span class="err">age</span> <span class="mi">72</span><span class="err">,</span> <span class="err">in</span> <span class="err">April</span> <span class="mi">1992</span><span class="err">.&lt;/spotlight&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;works&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="err">&lt;works&gt;</span><span class="mi">5595</span><span class="err">&lt;/works&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="err">&lt;works&gt;</span><span class="mi">5596</span><span class="err">&lt;/works&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="err">&lt;works&gt;</span><span class="mi">5597</span><span class="err">&lt;/works&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="err">...</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;/works&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;/author&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">...</span>
</span></span><span class="line"><span class="cl"><span class="err">&lt;/authors&gt;</span></span></span></code></pre></div></div>

<h2 class="relative group">Get Author Details
    <div id="get-author-details" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#get-author-details" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>You can use the <code>authorid</code> value from the previous response (or just use the <code>uri</code> attribute of the <code>author</code> node) to get the details of the author you&rsquo;re interested in.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET https://reststop.randomhouse.com/resources/authors/947</span></span></code></pre></div></div>
<p>What I find interesting is that it seems to be the same data as the previous request. I&rsquo;m not sure why, in order to make it faster, the previous endpoint doesn&rsquo;t return less data&hellip; maybe a few titles and works to make sure it&rsquo;s the right author in the event of some ambiguity.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="err">&lt;?xml</span> <span class="err">version=</span><span class="s2">&#34;1.0&#34;</span> <span class="err">encoding=</span><span class="s2">&#34;UTF-8&#34;</span> <span class="err">standalone=</span><span class="s2">&#34;yes&#34;</span><span class="err">?&gt;</span>
</span></span><span class="line"><span class="cl"><span class="err">&lt;author</span> <span class="err">uri=</span><span class="s2">&#34;https://reststop.randomhouse.com/resources/authors/947&#34;</span><span class="err">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;approved&gt;X&lt;/approved&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;authordisplay&gt;Isaac</span> <span class="err">Asimov&lt;/authordisplay&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;authorfirst&gt;Isaac&lt;/authorfirst&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;authorfirstlc&gt;isaac&lt;/authorfirstlc&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;authorid&gt;</span><span class="mi">947</span><span class="err">&lt;/authorid&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;authorlast&gt;Asimov&lt;/authorlast&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;authorlastfirst&gt;ASIMOV,</span> <span class="err">ISAAC&lt;/authorlastfirst&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;authorlastlc&gt;asimov&lt;/authorlastlc&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;titles&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;isbn</span> <span class="err">contributortype=</span><span class="s2">&#34;A&#34;</span><span class="err">&gt;</span><span class="mi">9780307488633</span><span class="err">&lt;/isbn&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;isbn</span> <span class="err">contributortype=</span><span class="s2">&#34;A&#34;</span><span class="err">&gt;</span><span class="mi">9780307490247</span><span class="err">&lt;/isbn&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;isbn</span> <span class="err">contributortype=</span><span class="s2">&#34;A&#34;</span><span class="err">&gt;</span><span class="mi">9780307573537</span><span class="err">&lt;/isbn&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">...</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;/titles&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;lastinitial&gt;a&lt;/lastinitial&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;spotlight&gt;&amp;lt;b&amp;gt;Isaac</span> <span class="err">Asimov&amp;lt;/b&amp;gt;&amp;amp;nbsp;began</span> <span class="err">his</span> <span class="err">Foundation</span> <span class="err">series</span> <span class="err">at</span> <span class="err">the</span> <span class="err">age</span> <span class="err">of</span> <span class="mi">21</span><span class="err">,</span> <span class="err">not</span> <span class="err">realizing</span> <span class="err">that</span> <span class="err">it</span> <span class="err">would</span> <span class="err">one</span> <span class="err">day</span> <span class="err">be</span> <span class="err">considered</span> <span class="err">a</span> <span class="err">cornerstone</span> <span class="err">of</span> <span class="err">science</span> <span class="err">fiction.</span> <span class="err">During</span> <span class="err">his</span> <span class="err">legendary</span> <span class="err">career,</span> <span class="err">Asimov</span> <span class="err">penned</span> <span class="err">over</span> <span class="mi">470</span> <span class="err">books</span> <span class="err">on</span> <span class="err">subjects</span> <span class="err">ranging</span> <span class="err">from</span> <span class="err">science</span> <span class="err">to</span> <span class="err">Shakespeare</span> <span class="err">to</span> <span class="err">history,</span> <span class="err">though</span> <span class="err">he</span> <span class="err">was</span> <span class="err">most</span> <span class="err">loved</span> <span class="err">for</span> <span class="err">his</span> <span class="err">award-winning</span> <span class="err">science</span> <span class="err">fiction</span> <span class="err">sagas,</span> <span class="err">which</span> <span class="err">include</span> <span class="err">the</span> <span class="err">Robot,</span> <span class="err">Empire,</span> <span class="err">and</span> <span class="err">Foundation</span> <span class="err">series.</span> <span class="err">Named</span> <span class="err">a</span> <span class="err">Grand</span> <span class="err">Master</span> <span class="err">of</span> <span class="err">Science</span> <span class="err">Fiction</span> <span class="err">by</span> <span class="err">the</span> <span class="err">Science</span> <span class="err">Fiction</span> <span class="err">and</span> <span class="err">Fantasy</span> <span class="err">Writers</span> <span class="err">of</span> <span class="err">America,</span> <span class="err">Asimov</span> <span class="err">entertained</span> <span class="err">and</span> <span class="err">educated</span> <span class="err">readers</span> <span class="err">of</span> <span class="err">all</span> <span class="err">ages</span> <span class="err">for</span> <span class="err">close</span> <span class="err">to</span> <span class="err">five</span> <span class="err">decades.</span> <span class="err">He</span> <span class="err">died,</span> <span class="err">at</span> <span class="err">the</span> <span class="err">age</span> <span class="mi">72</span><span class="err">,</span> <span class="err">in</span> <span class="err">April</span> <span class="mi">1992</span><span class="err">.&lt;/spotlight&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;works&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;works&gt;</span><span class="mi">5595</span><span class="err">&lt;/works&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;works&gt;</span><span class="mi">5596</span><span class="err">&lt;/works&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;works&gt;</span><span class="mi">5597</span><span class="err">&lt;/works&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">...</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;/works&gt;</span>
</span></span><span class="line"><span class="cl"><span class="err">&lt;/author&gt;</span></span></span></code></pre></div></div>

<h2 class="relative group">Get Title / Work Details
    <div id="get-title--work-details" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#get-title--work-details" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The two responses so far have included the author&rsquo;s titles and works, of which I just showed a few (Asimov had hundreds). You can use that data with another endpoint to get more details about them. FWIW, I&rsquo;m a little fuzzy on the difference between &ldquo;titles&rdquo; and &ldquo;works&rdquo;&hellip;</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET https://reststop.randomhouse.com/resources/titles/9780307490247</span></span></code></pre></div></div>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="err">&lt;?xml</span> <span class="err">version=</span><span class="s2">&#34;1.0&#34;</span> <span class="err">encoding=</span><span class="s2">&#34;UTF-8&#34;</span> <span class="err">standalone=</span><span class="s2">&#34;yes&#34;</span><span class="err">?&gt;</span>
</span></span><span class="line"><span class="cl"><span class="err">&lt;title</span> <span class="err">uri=</span><span class="s2">&#34;https://reststop.randomhouse.com/resources/titles/9780307490247&#34;</span><span class="err">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;author&gt;ASIMOV,</span> <span class="err">ISAAC&lt;/author&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;authors&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;authorId</span> <span class="err">contributortype=</span><span class="s2">&#34;A&#34;</span><span class="err">&gt;</span><span class="mi">947</span><span class="err">&lt;/authorId&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;/authors&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;authorbio&gt;&amp;lt;b&amp;gt;Isaac</span> <span class="err">Asimov&amp;lt;/b&amp;gt;</span> <span class="err">began</span> <span class="err">his</span> <span class="err">Foundation</span> <span class="err">series</span> <span class="err">at</span> <span class="err">the</span> <span class="err">age</span> <span class="err">of</span> <span class="err">twenty-one,</span> <span class="err">not</span> <span class="err">realizing</span> <span class="err">that</span> <span class="err">it</span> <span class="err">would</span> <span class="err">one</span> <span class="err">day</span> <span class="err">be</span> <span class="err">considered</span> <span class="err">a</span> <span class="err">cornerstone</span> <span class="err">of</span> <span class="err">science</span> <span class="err">fiction.</span> <span class="err">During</span> <span class="err">his</span> <span class="err">legendary</span> <span class="err">career,</span> <span class="err">Asimov</span> <span class="err">penned</span> <span class="err">more</span> <span class="err">than</span> <span class="mi">470</span> <span class="err">books</span> <span class="err">on</span> <span class="err">subjects</span> <span class="err">ranging</span> <span class="err">from</span> <span class="err">science</span> <span class="err">to</span> <span class="err">Shakespeare</span> <span class="err">to</span> <span class="err">history,</span> <span class="err">though</span> <span class="err">he</span> <span class="err">was</span> <span class="err">most</span> <span class="err">loved</span> <span class="err">for</span> <span class="err">his</span> <span class="err">award-winning</span> <span class="err">science</span> <span class="err">fiction</span> <span class="err">sagas,</span> <span class="err">which</span> <span class="err">include</span> <span class="err">the</span> <span class="err">Robot,</span> <span class="err">Empire,</span> <span class="err">and</span> <span class="err">Foundation</span> <span class="err">series.</span> <span class="err">Named</span> <span class="err">a</span> <span class="err">Grand</span> <span class="err">Master</span> <span class="err">of</span> <span class="err">Science</span> <span class="err">Fiction</span> <span class="err">by</span> <span class="err">the</span> <span class="err">Science</span> <span class="err">Fiction</span> <span class="err">Writers</span> <span class="err">of</span> <span class="err">America,</span> <span class="err">Asimov</span> <span class="err">entertained</span> <span class="err">and</span> <span class="err">educated</span> <span class="err">readers</span> <span class="err">of</span> <span class="err">all</span> <span class="err">ages</span> <span class="err">for</span> <span class="err">close</span> <span class="err">to</span> <span class="err">five</span> <span class="err">decades.</span> <span class="err">He</span> <span class="err">died,</span> <span class="err">at</span> <span class="err">the</span> <span class="err">age</span> <span class="err">of</span> <span class="err">seventy-two,</span> <span class="err">in</span> <span class="err">April</span> <span class="mi">1992</span><span class="err">.&lt;/authorbio&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;authorweb&gt;Isaac</span> <span class="err">Asimov&lt;/authorweb&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;awards/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;characters/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;contributorfirst</span><span class="mi">1</span><span class="err">&gt;Isaac&lt;/contributorfirst</span><span class="mi">1</span><span class="err">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;contributorlast</span><span class="mi">1</span><span class="err">&gt;Asimov&lt;/contributorlast</span><span class="mi">1</span><span class="err">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;division&gt;Del</span> <span class="err">Rey&lt;/division&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;flapcopy&gt;A</span> <span class="err">millennium</span> <span class="err">into</span> <span class="err">the</span> <span class="err">future</span> <span class="err">two</span> <span class="err">advances</span> <span class="err">have</span> <span class="err">altered</span> <span class="err">the</span> <span class="err">course</span> <span class="err">of</span> <span class="err">human</span> <span class="err">history:</span> <span class="err">the</span> <span class="err">colonization</span> <span class="err">of</span> <span class="err">the</span> <span class="err">Galaxy</span> <span class="err">and</span> <span class="err">the</span> <span class="err">creation</span> <span class="err">of</span> <span class="err">the</span> <span class="err">positronic</span> <span class="err">brain.</span> <span class="err">Isaac</span> <span class="err">Asimov&#39;s</span> <span class="err">Robot</span> <span class="err">novels</span> <span class="err">chronicle</span> <span class="err">the</span> <span class="err">unlikely</span> <span class="err">partnership</span> <span class="err">between</span> <span class="err">a</span> <span class="err">New</span> <span class="err">York</span> <span class="err">City</span> <span class="err">detective</span> <span class="err">and</span> <span class="err">a</span> <span class="err">humanoid</span> <span class="err">robot</span> <span class="err">who</span> <span class="err">must</span> <span class="err">learn</span> <span class="err">to</span> <span class="err">work</span> <span class="err">together.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Detective</span> <span class="err">Elijah</span> <span class="err">Baiey</span> <span class="err">is</span> <span class="err">called</span> <span class="err">to</span> <span class="err">the</span> <span class="err">Spacer</span> <span class="err">world</span> <span class="err">Aurora</span> <span class="err">to</span> <span class="err">solve</span> <span class="err">a</span> <span class="err">bizarre</span> <span class="err">case</span> <span class="err">of</span> <span class="err">roboticide.</span> <span class="err">The</span> <span class="err">prime</span> <span class="err">suspect</span> <span class="err">is</span> <span class="err">a</span> <span class="err">gifted</span> <span class="err">roboticist</span> <span class="err">who</span> <span class="err">had</span> <span class="err">the</span> <span class="err">means,</span> <span class="err">the</span> <span class="err">motive,</span> <span class="err">and</span> <span class="err">the</span> <span class="err">opportunity</span> <span class="err">to</span> <span class="err">commit</span> <span class="err">the</span> <span class="err">crime.</span> <span class="err">There&#39;s</span> <span class="err">only</span> <span class="err">one</span> <span class="err">catch:</span> <span class="err">Baley</span> <span class="err">and</span> <span class="err">his</span> <span class="err">positronic</span> <span class="err">partner,</span> <span class="err">R.</span> <span class="err">Daneel</span> <span class="err">Olivaw,</span> <span class="err">must</span> <span class="err">prove</span> <span class="err">the</span> <span class="err">man</span> <span class="err">innocent.</span> <span class="err">For</span> <span class="err">in</span> <span class="err">a</span> <span class="err">case</span> <span class="err">of</span> <span class="err">political</span> <span class="err">intrigue</span> <span class="err">and</span> <span class="err">love</span> <span class="err">between</span> <span class="err">woman</span> <span class="err">and</span> <span class="err">robot</span> <span class="err">gone</span> <span class="err">tragically</span> <span class="err">wrong,</span> <span class="err">there&#39;s</span> <span class="err">more</span> <span class="err">at</span> <span class="err">stake</span> <span class="err">than</span> <span class="err">simple</span> <span class="err">justice.</span> <span class="err">This</span> <span class="err">time</span> <span class="err">Baley&#39;s</span> <span class="err">career,</span> <span class="err">his</span> <span class="err">life,</span> <span class="err">and</span> <span class="err">Earth&#39;s</span> <span class="err">right</span> <span class="err">to</span> <span class="err">pioneer</span> <span class="err">the</span> <span class="err">Galaxy</span> <span class="err">lie</span> <span class="err">in</span> <span class="err">the</span> <span class="err">delicate</span> <span class="err">balance.&lt;/flapcopy&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;formatcode&gt;EL&lt;/formatcode&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;formatname&gt;eBook&lt;/formatname&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;imprint&gt;Spectra&lt;/imprint&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;isbn&gt;</span><span class="mi">9780307490247</span><span class="err">&lt;/isbn&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;isbn</span><span class="mi">10</span><span class="err">&gt;</span><span class="mi">0307490246</span><span class="err">&lt;/isbn</span><span class="mi">10</span><span class="err">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;isbn</span><span class="mi">10</span><span class="err">hyphenated&gt;</span><span class="mi">0-307-49024-6</span><span class="err">&lt;/isbn</span><span class="mi">10</span><span class="err">hyphenated&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;isbn</span><span class="mi">13</span><span class="err">hyphenated&gt;</span><span class="mi">978-0-307-49024-7</span><span class="err">&lt;/isbn</span><span class="mi">13</span><span class="err">hyphenated&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;keyword&gt;The</span> <span class="err">Robots</span> <span class="err">of</span> <span class="err">Dawn</span> <span class="err">:</span>  <span class="err">:</span> <span class="err">Isaac</span> <span class="err">Asimov</span> <span class="err">:</span> <span class="err">Spectra</span> <span class="err">:</span> <span class="err">Fiction</span> <span class="err">-</span> <span class="err">Science</span> <span class="err">Fiction</span> <span class="err">-</span> <span class="err">Hard</span> <span class="err">Science</span> <span class="err">Fiction</span> <span class="err">:</span> <span class="err">Fiction</span> <span class="err">-</span> <span class="err">Classics</span> <span class="err">:</span> <span class="err">Fiction</span> <span class="err">-</span> <span class="err">Science</span> <span class="err">Fiction</span> <span class="err">-</span> <span class="err">Space</span> <span class="err">Opera</span> <span class="err">:</span> <span class="mi">0307490246</span> <span class="err">:</span> <span class="mi">0-307-49024-6</span> <span class="err">:</span> <span class="mi">9780307490247</span> <span class="err">:</span> <span class="mi">978-0-307-49024-7</span> <span class="err">:</span> <span class="err">A</span> <span class="err">millennium</span> <span class="err">into</span> <span class="err">the</span> <span class="err">future</span> <span class="err">two</span> <span class="err">advances</span> <span class="err">have</span> <span class="err">altered</span> <span class="err">the</span> <span class="err">course</span> <span class="err">of</span> <span class="err">human</span> <span class="err">history:</span> <span class="err">the</span> <span class="err">colonization</span> <span class="err">of</span> <span class="err">the</span> <span class="err">Galaxy</span> <span class="err">and</span> <span class="err">the</span> <span class="err">creation</span> <span class="err">of</span> <span class="err">the</span> <span class="err">positronic</span> <span class="err">brain.</span> <span class="err">Isaac</span> <span class="err">Asimov&#39;s</span> <span class="err">Robot</span> <span class="err">novels</span> <span class="err">chronicle</span> <span class="err">the</span> <span class="err">unlikely</span> <span class="err">partnership</span> <span class="err">between</span> <span class="err">a</span> <span class="err">New</span> <span class="err">York</span> <span class="err">City</span> <span class="err">detective</span> <span class="err">and</span> <span class="err">a</span> <span class="err">humanoid</span> <span class="err">robot</span> <span class="err">who</span> <span class="err">must</span> <span class="err">learn</span> <span class="err">to</span> <span class="err">work</span> <span class="err">together.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Detective</span> <span class="err">Elijah</span> <span class="err">Baiey</span> <span class="err">is</span> <span class="err">called</span> <span class="err">to</span> <span class="err">the</span> <span class="err">Spacer</span> <span class="err">world</span> <span class="err">Aurora</span> <span class="err">to</span> <span class="err">solve</span> <span class="err">a</span> <span class="err">bizarre</span> <span class="err">case</span> <span class="err">of</span> <span class="err">roboticide.</span> <span class="err">The</span> <span class="err">prime</span> <span class="err">suspect</span> <span class="err">is</span> <span class="err">a</span> <span class="err">gifted</span> <span class="err">roboticist</span> <span class="err">who</span> <span class="err">had</span> <span class="err">the</span> <span class="err">means,</span> <span class="err">the</span> <span class="err">motive,</span> <span class="err">and</span> <span class="err">the</span> <span class="err">opportunity</span> <span class="err">to</span> <span class="err">commit</span> <span class="err">the</span> <span class="err">crime.</span> <span class="err">There&#39;s</span> <span class="err">only</span> <span class="err">one</span> <span class="err">catch:</span> <span class="err">Baley</span> <span class="err">and</span> <span class="err">his</span> <span class="err">positronic</span> <span class="err">partner,</span> <span class="err">R.</span> <span class="err">Daneel</span> <span class="err">Olivaw,</span> <span class="err">must</span> <span class="err">prove</span> <span class="err">the</span> <span class="err">man</span> <span class="err">innocent.</span> <span class="err">For</span> <span class="err">in</span> <span class="err">a</span> <span class="err">case</span> <span class="err">of</span> <span class="err">political</span> <span class="err">intrigue</span> <span class="err">and</span> <span class="err">love</span> <span class="err">between</span> <span class="err">woman</span> <span class="err">and</span> <span class="err">robot</span> <span class="err">gone</span> <span class="err">tragically</span> <span class="err">wrong,</span> <span class="err">there&#39;s</span> <span class="err">more</span> <span class="err">at</span> <span class="err">stake</span> <span class="err">than</span> <span class="err">simple</span> <span class="err">justice.</span> <span class="err">This</span> <span class="err">time</span> <span class="err">Baley&#39;s</span> <span class="err">career,</span> <span class="err">his</span> <span class="err">life,</span> <span class="err">and</span> <span class="err">Earth&#39;s</span> <span class="err">right</span> <span class="err">to</span> <span class="err">pioneer</span> <span class="err">the</span> <span class="err">Galaxy</span> <span class="err">lie</span> <span class="err">in</span> <span class="err">the</span> <span class="err">delicate</span> <span class="err">balance.&lt;/keyword&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;onsaledate&gt;</span><span class="mi">01</span><span class="err">/</span><span class="mi">21</span><span class="err">/</span><span class="mi">2009</span><span class="err">&lt;/onsaledate&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;pages&gt;</span><span class="mi">448</span><span class="err">&lt;/pages&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;pricecanada&gt;</span><span class="mf">8.99</span><span class="err">&lt;/pricecanada&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;priceusa&gt;</span><span class="mf">7.99</span><span class="err">&lt;/priceusa&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;relatedisbns&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;isbn</span> <span class="err">formatcode=</span><span class="s2">&#34;MM&#34;</span><span class="err">&gt;</span><span class="mi">9780553299496</span><span class="err">&lt;/isbn&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;isbn</span> <span class="err">formatcode=</span><span class="s2">&#34;EL&#34;</span><span class="err">&gt;</span><span class="mi">9780307490247</span><span class="err">&lt;/isbn&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;isbn</span> <span class="err">formatcode=</span><span class="s2">&#34;DN&#34;</span><span class="err">&gt;</span><span class="mi">9780804191241</span><span class="err">&lt;/isbn&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;/relatedisbns&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;salestatus&gt;EL&lt;/salestatus&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;subformat&gt;</span><span class="mi">001</span><span class="err">&lt;/subformat&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;subjectcategory</span><span class="mi">1</span><span class="err">&gt;FIC</span><span class="mi">028020</span><span class="err">&lt;/subjectcategory</span><span class="mi">1</span><span class="err">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;subjectcategory</span><span class="mi">2</span><span class="err">&gt;FIC</span><span class="mi">004000</span><span class="err">&lt;/subjectcategory</span><span class="mi">2</span><span class="err">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;subjectcategory</span><span class="mi">3</span><span class="err">&gt;FIC</span><span class="mi">028030</span><span class="err">&lt;/subjectcategory</span><span class="mi">3</span><span class="err">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;subjectcategorydescription</span><span class="mi">1</span><span class="err">&gt;Fiction</span> <span class="err">-</span> <span class="err">Science</span> <span class="err">Fiction</span> <span class="err">-</span> <span class="err">Hard</span> <span class="err">Science</span> <span class="err">Fiction&lt;/subjectcategorydescription</span><span class="mi">1</span><span class="err">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;subjectcategorydescription</span><span class="mi">2</span><span class="err">&gt;Fiction</span> <span class="err">-</span> <span class="err">Classics&lt;/subjectcategorydescription</span><span class="mi">2</span><span class="err">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;subjectcategorydescription</span><span class="mi">3</span><span class="err">&gt;Fiction</span> <span class="err">-</span> <span class="err">Science</span> <span class="err">Fiction</span> <span class="err">-</span> <span class="err">Space</span> <span class="err">Opera&lt;/subjectcategorydescription</span><span class="mi">3</span><span class="err">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;tgpdf&gt;</span><span class="kc">false</span><span class="err">&lt;/tgpdf&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;themes/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;titleauthisbn&gt;The</span> <span class="err">Robots</span> <span class="err">of</span> <span class="err">Dawn</span> <span class="err">:</span> <span class="err">Isaac</span> <span class="err">Asimov</span> <span class="err">:</span> <span class="mi">0307490246</span> <span class="err">:</span> <span class="mi">0-307-49024-6</span> <span class="err">:</span> <span class="mi">9780307490247</span> <span class="err">:</span> <span class="mi">978-0-307-49024-7</span><span class="err">&lt;/titleauthisbn&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;titleshort&gt;ROBOTS</span> <span class="err">OF</span> <span class="err">DAWN,</span> <span class="err">THE</span> <span class="err">(EBK)&lt;/titleshort&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;titlesubtitleauthisbn&gt;The</span> <span class="err">Robots</span> <span class="err">of</span> <span class="err">Dawn</span> <span class="err">:</span>  <span class="err">:</span> <span class="err">Isaac</span> <span class="err">Asimov</span> <span class="err">:</span> <span class="mi">0307490246</span> <span class="err">:</span> <span class="mi">0-307-49024-6</span> <span class="err">:</span> <span class="mi">9780307490247</span> <span class="err">:</span> <span class="mi">978-0-307-49024-7</span><span class="err">&lt;/titlesubtitleauthisbn&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;titleweb&gt;The</span> <span class="err">Robots</span> <span class="err">of</span> <span class="err">Dawn&lt;/titleweb&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;updatedOn&gt;</span><span class="mi">2017-12-02</span><span class="err">T</span><span class="mi">01</span><span class="err">:</span><span class="mi">59</span><span class="err">:</span><span class="mf">13.000</span><span class="err">&lt;/updatedOn&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;webdomains&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">...</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;/webdomains&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;links/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;workid&gt;</span><span class="mi">5799</span><span class="err">&lt;/workid&gt;</span>
</span></span><span class="line"><span class="cl"><span class="err">&lt;/title&gt;</span></span></span></code></pre></div></div>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET https://reststop.randomhouse.com/resources/works/5596</span></span></code></pre></div></div>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="err">&lt;?xml</span> <span class="err">version=</span><span class="s2">&#34;1.0&#34;</span> <span class="err">encoding=</span><span class="s2">&#34;UTF-8&#34;</span> <span class="err">standalone=</span><span class="s2">&#34;yes&#34;</span><span class="err">?&gt;</span>
</span></span><span class="line"><span class="cl"><span class="err">&lt;work</span> <span class="err">uri=</span><span class="s2">&#34;https://reststop.randomhouse.com/resources/works/5596&#34;</span><span class="err">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;authorweb&gt;ASIMOV,</span> <span class="err">ISAAC&lt;/authorweb&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;titles&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;isbn</span> <span class="err">formatcode=</span><span class="s2">&#34;TR&#34;</span><span class="err">&gt;</span><span class="mi">9780449900482</span><span class="err">&lt;/isbn&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;/titles&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;onsaledate&gt;</span><span class="mi">1981-09-12</span><span class="err">T</span><span class="mi">00</span><span class="err">:</span><span class="mi">00</span><span class="err">:</span><span class="mi">00-04</span><span class="err">:</span><span class="mi">00</span><span class="err">&lt;/onsaledate&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;titleAuth&gt;A</span> <span class="err">Choice</span> <span class="err">of</span> <span class="err">Catastrophes</span> <span class="err">:</span> <span class="err">Isaac</span> <span class="err">Asimov&lt;/titleAuth&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;titleSubtitleAuth&gt;A</span> <span class="err">Choice</span> <span class="err">of</span> <span class="err">Catastrophes</span> <span class="err">:</span> <span class="err">The</span> <span class="err">Disasters</span> <span class="err">That</span> <span class="err">Threaten</span> <span class="err">Our</span> <span class="err">World</span> <span class="err">:</span> <span class="err">Isaac</span> <span class="err">Asimov&lt;/titleSubtitleAuth&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;titleshort&gt;CHOICE</span> <span class="err">OF</span> <span class="err">CATASTROPHES,</span> <span class="err">A&lt;/titleshort&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;titleweb&gt;A</span> <span class="err">Choice</span> <span class="err">of</span> <span class="err">Catastrophes&lt;/titleweb&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;workid&gt;</span><span class="mi">5596</span><span class="err">&lt;/workid&gt;</span>
</span></span><span class="line"><span class="cl"><span class="err">&lt;/work&gt;</span></span></span></code></pre></div></div>

<h2 class="relative group">Thoughts
    <div id="thoughts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#thoughts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There&rsquo;s not too much to say about this one. Not sure what the limitations / request throttling might be for this API. It&rsquo;s free, which is nice! The documentation is all in one place and includes examples in php and java, which is nice too.</p>
]]></content:encoded><media:content url="https://grantwinney.com/what-is-penguin-random-house-api/feature.webp" medium="image" type="image/webp"/></item><item><title>Accessing Census, Demographic, and Housing Data with the US Census Bureau API</title><link>https://grantwinney.com/what-is-us-census-bureau-api/</link><pubDate>Sat, 30 Dec 2017 23:01:30 +0000</pubDate><guid>https://grantwinney.com/what-is-us-census-bureau-api/</guid><description>The US Census Bureau APIs provide free access to geolocation data, as well as American census data, demographics, housing stats, etc. Let&amp;rsquo;s check them out!</description><content:encoded><![CDATA[<p>The <a href="https://www.census.gov/data/developers/data-sets.html"  target="_blank" rel="noreferrer">US Census Bureau APIs</a> provide free access to American census data, demographics, housing stats, etc. It&rsquo;s all anonymous aggregate data that&rsquo;s already publicly available - no personal data.</p>
<p>First though, two things to consider:</p>
<ul>
<li>If you&rsquo;re unfamiliar with APIs, you might want to <a href="https://grantwinney.com/what-is-an-api/"  target="_blank" rel="noreferrer">read this first</a> to familiarize yourself.</li>
<li>Install <a href="https://www.getpostman.com/"  target="_blank" rel="noreferrer">Postman</a>, which allows you to access API endpoints without having to write an app, as well as save the calls you make and sync them online.</li>
</ul>

<h2 class="relative group">Authorization
    <div id="authorization" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#authorization" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>As usual, you need to <a href="https://api.census.gov/data/key_signup.html"  target="_blank" rel="noreferrer">request a key</a> first. Just enter &ldquo;none&rdquo; and your email address, and hopefully you&rsquo;ll receive a success message like this one:</p>
<p>Your request for a new API key has been successfully submitted. Please check your email. In a few minutes you should receive a message with instructions on how to activate your new key.</p>
<p>I got the email in about 30 seconds, clicked the link to validate the key, and was taken to a <em>success</em> page.</p>
<p>Congratulations! Your key has been activated. You may now use it to query the Data API. Happy querying!</p>

<h2 class="relative group">Geocoding Service
    <div id="geocoding-service" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#geocoding-service" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Try your shiny new key with the <a href="https://www.census.gov/data/developers/data-sets/Geocoding-services.html"  target="_blank" rel="noreferrer">geocoding API</a>, a service for looking up addresses and getting a latitude/longitude coordinate - similar to the <a href="https://grantwinney.com/what-is-google-maps-api/#geocodingapi"  target="_blank" rel="noreferrer">Google Maps geocoding API</a>.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET https://geocoding.geo.census.gov/geocoder/locations/onelineaddress?address=50+Public+Square+Cleveland+Ohio&amp;format=json&amp;benchmark=Public_AR_Current&amp;key=&lt;your-key&gt;</span></span></code></pre></div></div>
<p>I looked up the address for the Terminal Tower in Cleveland OH, and got the address back along with its geolocation coordinates. (Using <code>x</code> and <code>y</code> as a field name in the response is a poor choice - they represent longitude and latitude respectively, and should&rsquo;ve been named likewise.)</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;result&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;input&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;address&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;address&#34;</span><span class="p">:</span> <span class="s2">&#34;50 Public Square Cleveland Ohio&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;benchmark&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;isDefault&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;benchmarkName&#34;</span><span class="p">:</span> <span class="s2">&#34;Public_AR_Current&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;benchmarkDescription&#34;</span><span class="p">:</span> <span class="s2">&#34;Public Address Ranges - Current Benchmark&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;addressMatches&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;matchedAddress&#34;</span><span class="p">:</span> <span class="s2">&#34;50 PUBLIC SQ, CLEVELAND, OH, 44113&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;coordinates&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;x&#34;</span><span class="p">:</span> <span class="mf">-81.6947</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;y&#34;</span><span class="p">:</span> <span class="mf">41.500114</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;tigerLine&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;tigerLineId&#34;</span><span class="p">:</span> <span class="s2">&#34;61109032&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;side&#34;</span><span class="p">:</span> <span class="s2">&#34;R&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;addressComponents&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;fromAddress&#34;</span><span class="p">:</span> <span class="s2">&#34;2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;toAddress&#34;</span><span class="p">:</span> <span class="s2">&#34;108&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;preQualifier&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;preDirection&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;preType&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;streetName&#34;</span><span class="p">:</span> <span class="s2">&#34;PUBLIC&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;suffixType&#34;</span><span class="p">:</span> <span class="s2">&#34;SQ&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;suffixDirection&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;suffixQualifier&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;state&#34;</span><span class="p">:</span> <span class="s2">&#34;OH&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;zip&#34;</span><span class="p">:</span> <span class="s2">&#34;44113&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;city&#34;</span><span class="p">:</span> <span class="s2">&#34;CLEVELAND&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;matchedAddress&#34;</span><span class="p">:</span> <span class="s2">&#34;50 PUBLIC SQ, CLEVELAND, OH, 44141&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;coordinates&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;x&#34;</span><span class="p">:</span> <span class="mf">-81.62801</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;y&#34;</span><span class="p">:</span> <span class="mf">41.320698</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;tigerLine&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;tigerLineId&#34;</span><span class="p">:</span> <span class="s2">&#34;61154520&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;side&#34;</span><span class="p">:</span> <span class="s2">&#34;L&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;addressComponents&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;fromAddress&#34;</span><span class="p">:</span> <span class="s2">&#34;2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;toAddress&#34;</span><span class="p">:</span> <span class="s2">&#34;98&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;preQualifier&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;preDirection&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;preType&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;streetName&#34;</span><span class="p">:</span> <span class="s2">&#34;PUBLIC&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;suffixType&#34;</span><span class="p">:</span> <span class="s2">&#34;SQ&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;suffixDirection&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;suffixQualifier&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;state&#34;</span><span class="p">:</span> <span class="s2">&#34;OH&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;zip&#34;</span><span class="p">:</span> <span class="s2">&#34;44141&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;city&#34;</span><span class="p">:</span> <span class="s2">&#34;CLEVELAND&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h2 class="relative group">Language Statistics
    <div id="language-statistics" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#language-statistics" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I have pretty much no idea what all this census lingo means&hellip; it&rsquo;s like greek to me. I&rsquo;m gonna pick one more API that seems fairly self-explanatory - <a href="https://www.census.gov/data/developers/data-sets/language-stats.html"  target="_blank" rel="noreferrer">language statistics</a>.</p>
<p>For the following calls, you&rsquo;ll need <a href="https://web.archive.org/web/20180807144506/https://www.census.gov/geo/reference/codes/cou.html"  target="_blank" rel="noreferrer">FIPS codes for states/counties</a> and <a href="https://www.census.gov/hhes/socdemo/language/about/02_Primary_list.pdf"  target="_blank" rel="noreferrer">language codes</a>, both available on census.gov. In the text files, the first number (02 or 39 below) is the state code, and 020, 035, etc are the county codes.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">AK,02,013,Aleutians East Borough,H1
</span></span><span class="line"><span class="cl">AK,02,016,Aleutians West Census Area,H5
</span></span><span class="line"><span class="cl">AK,02,020,Anchorage Municipality,H6
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">OH,39,033,Crawford County,H1
</span></span><span class="line"><span class="cl">OH,39,035,Cuyahoga County,H1
</span></span><span class="line"><span class="cl">OH,39,037,Darke County,H1</span></span></code></pre></div></div>
<p><em>The numbers reported by the following calls seem exceptionally low. Ohio has 11.5 million people, but the results say only a quarter million speak Spanish - there&rsquo;s no way it&rsquo;s that few. I&rsquo;m not sure if it means primary language, or only language, or something else. Additionally, it probably depends on whether residents were even asked, and whether or not they chose to tick the box indicating they spoke Spanish. In other words, I don&rsquo;t think I&rsquo;d trust this data without knowing more about its limitations.</em></p>

<h3 class="relative group">Alaska
    <div id="alaska" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#alaska" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Find how many people in Anchorage, AK speak Spanish at home: (12635 people)</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET https://api.census.gov/data/2013/language?get=NAME,EST,LANLABEL&amp;for=county:020&amp;in=state:02&amp;LAN=625&amp;key=&lt;your-key&gt;</span></span></code></pre></div></div>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;NAME&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;EST&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;LANLABEL&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;LAN&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;state&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;county&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Anchorage Municipality, AK&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;12635&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Spanish&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;625&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;02&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;020&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">]</span></span></span></code></pre></div></div>
<p>And how many speak French: (1035 people)</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET https://api.census.gov/data/2013/language?get=NAME,EST,LANLABEL&amp;for=county:020&amp;in=state:02&amp;LAN=620&amp;key=&lt;your-key&gt;</span></span></code></pre></div></div>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;NAME&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;EST&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;LANLABEL&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;LAN&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;state&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;county&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Anchorage Municipality, AK&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;1035&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;French&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;620&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;02&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;020&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">]</span></span></span></code></pre></div></div>

<h3 class="relative group">Ohio
    <div id="ohio" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#ohio" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>There are 490 Portuguese speakers in Cuyahoga County OH, and 241650 Spanish speakers in Ohio.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET https://api.census.gov/data/2013/language?get=NAME,EST,LANLABEL&amp;for=state:39&amp;LAN=625&amp;key=&lt;your-key&gt;</span></span></code></pre></div></div>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;NAME&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;EST&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;LANLABEL&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;LAN&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;state&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Ohio&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;241650&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Spanish&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;625&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;39&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">]</span></span></span></code></pre></div></div>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET https://api.census.gov/data/2013/language?get=NAME,EST,LANLABEL&amp;for=county:035&amp;in=state:39&amp;LAN=629&amp;key=&lt;your-key&gt;</span></span></code></pre></div></div>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;NAME&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;EST&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;LANLABEL&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;LAN&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;state&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;county&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Cuyahoga County, OH&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;490&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Portuguese&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;629&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;39&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;035&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">]</span></span></span></code></pre></div></div>

<h2 class="relative group">Thoughts
    <div id="thoughts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#thoughts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The website is a little tough on the eyes - at least mine - but the information is all there. Once you pick an API, you can drill down into the documentation and find allowed parameters, sample responses and even sample usages.</p>
<p>The JSON responses are a little weird, and makes it more difficult than necessary to extract a particular field from the results. I&rsquo;d expect to see the previous results to be returned in a format like this instead, so (for example) querying on &ldquo;EST&rdquo; would return &ldquo;12635&rdquo;.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;NAME&#34;</span><span class="p">:</span> <span class="s2">&#34;Anchorage Municipality, AK&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;EST&#34;</span><span class="p">:</span> <span class="s2">&#34;12635&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;LANLABEL&#34;</span><span class="p">:</span> <span class="s2">&#34;Spanish&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;LAN&#34;</span><span class="p">:</span> <span class="s2">&#34;625&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;state&#34;</span><span class="p">:</span> <span class="s2">&#34;02&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;county&#34;</span><span class="p">:</span> <span class="s2">&#34;020&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
]]></content:encoded><media:content url="https://grantwinney.com/what-is-us-census-bureau-api/feature.webp" medium="image" type="image/webp"/></item><item><title>Access Current and Historical Weather Data with the OpenWeatherMap API</title><link>https://grantwinney.com/what-is-openweathermap-api/</link><pubDate>Fri, 29 Dec 2017 15:33:04 +0000</pubDate><guid>https://grantwinney.com/what-is-openweathermap-api/</guid><description>OpenWeatherMap provides free access to extensive weather data - current conditions, 5-day forecast, uv index, weather alerts, etc. Let&amp;rsquo;s check out their API!</description><content:encoded><![CDATA[<p>OpenWeatherMap provides free access to current weather conditions, 5-day forecast, uv index, alerts, etc. Let&rsquo;s check out the <a href="https://openweathermap.org/api"  target="_blank" rel="noreferrer">OpenWeatherMap API</a>.</p>
<p>First though, two things to consider:</p>
<ul>
<li>If you&rsquo;re unfamiliar with APIs, you might want to <a href="https://grantwinney.com/what-is-an-api/"  target="_blank" rel="noreferrer">read this first</a> to familiarize yourself.</li>
<li>Install <a href="https://www.getpostman.com/"  target="_blank" rel="noreferrer">Postman</a>, which allows you to access API endpoints without having to write an app, as well as save the calls you make and sync them online.</li>
</ul>

<h2 class="relative group">Authorization
    <div id="authorization" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#authorization" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="http://openweathermap.org/appid"  target="_blank" rel="noreferrer">Sign up</a> to request an API key. You should end up in a user settings area where you can select the &ldquo;API keys&rdquo; tab. It showed a message about taking 10 minutes to generate keys but then it already had a key immediately generated and ready to go, so&hellip; I don&rsquo;t know.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="openweathermap-api&mdash;api-key"
    src="/what-is-openweathermap-api/openweathermap-api---api-key.png"
    width="993"
      height="362"></figure>

<h2 class="relative group">Get Current Weather
    <div id="get-current-weather" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#get-current-weather" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There are a number of ways to <a href="http://openweathermap.org/current"  target="_blank" rel="noreferrer">request current weather data</a> for your area, but the two most accurate ones seem to be using zip code, and using latitude/longitude:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET http://api.openweathermap.org/data/2.5/weather?lat=41.4984174&amp;lon=-81.69372869999999&amp;APPID=&lt;your-app-key&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">GET http://api.openweathermap.org/data/2.5/weather?zip=44113,US&amp;APPID=&lt;your-app-key&gt;</span></span></code></pre></div></div>
<p>It returns an abundance of data for the location - you can <a href="http://openweathermap.org/current#parameter"  target="_blank" rel="noreferrer">read about the result values here</a>. Here&rsquo;s the result for Cleveland OH, where it&rsquo;s snowing lightly. There&rsquo;s also a block with other current conditions, such as:</p>
<ul>
<li>Tempature, in Kelvin (261.02 K is about 10 Fahrenheit)</li>
<li>Barometric Pressure, in mm (1038 mm is about 40 inches)</li>
<li>Humidity, percentage (it&rsquo;s currently snowing so the humidity is high)</li>
<li>Min and Max temps, in Kelvin (deviation for large geographical areas)</li>
<li>Visibility, in meters (about 9 miles)</li>
<li>Wind speed and direction, in m/sec (about 6 mph, due South)</li>
<li>Cloud cover (currently 90%)</li>
<li>Timestamp of request (which you can <a href="https://www.epochconverter.com/"  target="_blank" rel="noreferrer">convert to normal time</a> here)</li>
<li>Timestamps of sunset and sunrise (currently 7:52:43 AM and 5:05:01 PM, respectively)</li>
</ul>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JSON" data-lang="JSON"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;coord&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;lon&#34;</span><span class="p">:</span> <span class="mf">-81.69</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.5</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;weather&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">600</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;main&#34;</span><span class="p">:</span> <span class="s2">&#34;Snow&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;description&#34;</span><span class="p">:</span> <span class="s2">&#34;light snow&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;icon&#34;</span><span class="p">:</span> <span class="s2">&#34;13d&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;base&#34;</span><span class="p">:</span> <span class="s2">&#34;stations&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;main&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;temp&#34;</span><span class="p">:</span> <span class="mf">261.02</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;pressure&#34;</span><span class="p">:</span> <span class="mi">1038</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;humidity&#34;</span><span class="p">:</span> <span class="mi">66</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;temp_min&#34;</span><span class="p">:</span> <span class="mf">259.15</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;temp_max&#34;</span><span class="p">:</span> <span class="mf">262.15</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;visibility&#34;</span><span class="p">:</span> <span class="mi">14484</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;wind&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;speed&#34;</span><span class="p">:</span> <span class="mf">2.6</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;deg&#34;</span><span class="p">:</span> <span class="mi">180</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;clouds&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;all&#34;</span><span class="p">:</span> <span class="mi">90</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;dt&#34;</span><span class="p">:</span> <span class="mi">1514472900</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;sys&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">2166</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;message&#34;</span><span class="p">:</span> <span class="mf">0.0045</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;country&#34;</span><span class="p">:</span> <span class="s2">&#34;US&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;sunrise&#34;</span><span class="p">:</span> <span class="mi">1514465563</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;sunset&#34;</span><span class="p">:</span> <span class="mi">1514498701</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">5150529</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Cleveland&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;cod&#34;</span><span class="p">:</span> <span class="mi">200</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h3 class="relative group">Finding Latitude/Longitude
    <div id="finding-latitudelongitude" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#finding-latitudelongitude" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>If you need to lookup the coordinates of a location, <a href="https://grantwinney.com/what-is-google-maps-api/"  target="_blank" rel="noreferrer">check out the Google Maps API</a> - they have an endpoint for just that purpose. You can parse out the geometry/location values and use those in the weather request.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET https://maps.googleapis.com/maps/api/geocode/json?address=50 Public Square Cleveland, Ohio&amp;key=&lt;your-key&gt;</span></span></code></pre></div></div>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;results&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;formatted_address&#34;</span><span class="p">:</span> <span class="s2">&#34;50 Public Square, Cleveland, OH 44113, USA&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;geometry&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4984174</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.69372869999999</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="err">...</span>
</span></span><span class="line"><span class="cl">            <span class="err">...</span></span></span></code></pre></div></div>

<h2 class="relative group">Get 5-Day Forecast
    <div id="get-5-day-forecast" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#get-5-day-forecast" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The process for <a href="http://openweathermap.org/forecast5"  target="_blank" rel="noreferrer">getting the 5-day forecast</a> is pretty much the same as current weather, except you get a lot more data - every 3 hours worth, in fact.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET api.openweathermap.org/data/2.5/forecast?lat=41.4984174&amp;lon=-81.69372869999999&amp;APPID=&lt;your-app-key&gt;</span></span></code></pre></div></div>
<p>Here&rsquo;s a small portion of the results - there&rsquo;s a <code>dt_txt</code> field that clearly shows that each &ldquo;block&rdquo; of JSON data is for a 3-hour interval.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;cod&#34;</span><span class="p">:</span> <span class="s2">&#34;200&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;message&#34;</span><span class="p">:</span> <span class="mf">0.0044</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;cnt&#34;</span><span class="p">:</span> <span class="mi">40</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;list&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;dt&#34;</span><span class="p">:</span> <span class="mi">1514570400</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;main&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;temp&#34;</span><span class="p">:</span> <span class="mf">264.96</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;temp_min&#34;</span><span class="p">:</span> <span class="mf">263.056</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;temp_max&#34;</span><span class="p">:</span> <span class="mf">264.96</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;pressure&#34;</span><span class="p">:</span> <span class="mf">1006.4</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;sea_level&#34;</span><span class="p">:</span> <span class="mf">1041.63</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;grnd_level&#34;</span><span class="p">:</span> <span class="mf">1006.4</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;humidity&#34;</span><span class="p">:</span> <span class="mi">100</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;temp_kf&#34;</span><span class="p">:</span> <span class="mf">1.9</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="err">...</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;dt_txt&#34;</span><span class="p">:</span> <span class="s2">&#34;2017-12-29 18:00:00&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;dt&#34;</span><span class="p">:</span> <span class="mi">1514581200</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;main&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;temp&#34;</span><span class="p">:</span> <span class="mf">264.29</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;temp_min&#34;</span><span class="p">:</span> <span class="mf">263.016</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;temp_max&#34;</span><span class="p">:</span> <span class="mf">264.29</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;pressure&#34;</span><span class="p">:</span> <span class="mf">1005.31</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;sea_level&#34;</span><span class="p">:</span> <span class="mf">1040.53</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;grnd_level&#34;</span><span class="p">:</span> <span class="mf">1005.31</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;humidity&#34;</span><span class="p">:</span> <span class="mi">100</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;temp_kf&#34;</span><span class="p">:</span> <span class="mf">1.27</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="err">...</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;dt_txt&#34;</span><span class="p">:</span> <span class="s2">&#34;2017-12-29 21:00:00&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;dt&#34;</span><span class="p">:</span> <span class="mi">1514592000</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;main&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;temp&#34;</span><span class="p">:</span> <span class="mf">261.97</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;temp_min&#34;</span><span class="p">:</span> <span class="mf">261.333</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;temp_max&#34;</span><span class="p">:</span> <span class="mf">261.97</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;pressure&#34;</span><span class="p">:</span> <span class="mf">1005.29</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;sea_level&#34;</span><span class="p">:</span> <span class="mf">1040.7</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;grnd_level&#34;</span><span class="p">:</span> <span class="mf">1005.29</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;humidity&#34;</span><span class="p">:</span> <span class="mi">100</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;temp_kf&#34;</span><span class="p">:</span> <span class="mf">0.63</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="err">...</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;dt_txt&#34;</span><span class="p">:</span> <span class="s2">&#34;2017-12-30 00:00:00&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="err">...</span>
</span></span><span class="line"><span class="cl">        <span class="err">...</span></span></span></code></pre></div></div>

<h2 class="relative group">Historical Data
    <div id="historical-data" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#historical-data" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><em>Update:</em> Someone asked me about historical data, so I figured I&rsquo;d post what I found here. If you&rsquo;d like to <a href="https://openweathermap.org/history"  target="_blank" rel="noreferrer">get historical data</a>, such as the weather in a certain location for all of 2017, the endpoint changes from <code>api</code> to <code>history</code>:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET history.openweathermap.org/data/2.5/find?q=Cleveland&amp;type=accurate&amp;units=metric&amp;mode=xml&amp;start=1483228800&amp;end=1485820800&amp;APPID=&lt;your-app-key&gt;</span></span></code></pre></div></div>
<p>Unfortunately (but understandably), this data is not free. If you try to use the free token, it&rsquo;ll return a 401 error with the message &ldquo;Invalid API key.&rdquo; You can <a href="http://openweathermap.org/price#history"  target="_blank" rel="noreferrer">find out more about pricing</a> on their site.</p>

<h2 class="relative group">Thoughts
    <div id="thoughts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#thoughts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>It&rsquo;s unclear what the usage limits are. The page where you get an app key warns against sending requests &ldquo;more than 1 time per 10 minutes from one device/one API key&rdquo;, which seems like an extreme limitation. Yet the page that compares price and packages says &ldquo;no more than 60 calls per minute&rdquo; for a free account, and thousands or even hundreds of thousands per minute for paid accounts; that seems more reasonable.</p>
<p>There are several ways to get weather data. It&rsquo;s odd that the method they encourage is to use a &ldquo;city id&rdquo; - a value you get from a JSON file they provide, but the file is not organized in any particular order and has well over a million lines in it. They also provide a way that uses city and country, but that&rsquo;s inaccurate - I live by Cleveland, OH but there&rsquo;s also a Cleveland, GA. Guess there could be a use-case, and it&rsquo;s there if you need it, I just don&rsquo;t think I&rsquo;d use it.</p>
<p>The other APIs available for use with a free account are <a href="http://openweathermap.org/api/weathermaps"  target="_blank" rel="noreferrer">Weather Maps</a>, <a href="http://openweathermap.org/api/uvi"  target="_blank" rel="noreferrer">UV Index</a>, <a href="http://openweathermap.org/api/pollution/co"  target="_blank" rel="noreferrer">Air Pollution</a>, and <a href="http://openweathermap.org/triggers"  target="_blank" rel="noreferrer">Weather Alerts</a>. The last three are still in beta, whatever that means - not sure if that means they&rsquo;re not quite reliable yet? Still would be interesting to try. I could see looking up alerts and then having a Raspberry Pi or similar light an LED or sound a siren for certain conditions.</p>
]]></content:encoded><media:content url="https://grantwinney.com/what-is-openweathermap-api/feature.webp" medium="image" type="image/webp"/></item><item><title>Learn About the ISS and its Crew with the ISS Notify API</title><link>https://grantwinney.com/what-is-iss-notify-api/</link><pubDate>Thu, 28 Dec 2017 12:03:00 +0000</pubDate><guid>https://grantwinney.com/what-is-iss-notify-api/</guid><description>The ISS Notify API (or is it the Open Notify API?) was written by Nathan Bergey for a Science Hack Day competition, then released to the public. You can use it to find the location of the ISS, or to find when it&amp;rsquo;ll pass over a location! Check it out.</description><content:encoded><![CDATA[<p><a href="https://github.com/natronics"  target="_blank" rel="noreferrer">Nathan Bergey</a> wrote the <a href="http://open-notify.org/Open-Notify-API/"  target="_blank" rel="noreferrer">ISS Notify API</a> (or is it the Open Notify API?) for a competition called Science Hack Day. You can <a href="http://open-notify.org/about.html"  target="_blank" rel="noreferrer">learn more here</a>, or read on to try it out!</p>
<p>First though, two things to consider:</p>
<ul>
<li>If you&rsquo;re unfamiliar with APIs, you might want to <a href="https://grantwinney.com/what-is-an-api/"  target="_blank" rel="noreferrer">read this first</a> to familiarize yourself.</li>
<li>Install <a href="https://www.getpostman.com/"  target="_blank" rel="noreferrer">Postman</a>, which allows you to access API endpoints without having to write an app, as well as save the calls you make and sync them online.</li>
</ul>
<hr>

<h2 class="relative group">Where is the ISS?
    <div id="where-is-the-iss" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#where-is-the-iss" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>This is great. Could there be an easier way to <a href="http://open-notify.org/Open-Notify-API/ISS-Location-Now/"  target="_blank" rel="noreferrer">find the location of the ISS</a>?</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">GET http://api.open-notify.org/iss-now.json</code></pre></div>
<p>And here&rsquo;s the result:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;timestamp&#34;</span><span class="p">:</span> <span class="mi">1514428436</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;message&#34;</span><span class="p">:</span> <span class="s2">&#34;success&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;iss_position&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;longitude&#34;</span><span class="p">:</span> <span class="s2">&#34;63.8579&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;latitude&#34;</span><span class="p">:</span> <span class="s2">&#34;42.9473&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>If you&rsquo;d like to try it out in a script, here it is in Python2&hellip; but you can use it with any language you&rsquo;d like as long as you know how to parse the JSON response.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">urllib2</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">json</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">datetime</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">req</span> <span class="o">=</span> <span class="n">urllib2</span><span class="o">.</span><span class="n">Request</span><span class="p">(</span><span class="s2">&#34;http://api.open-notify.org/iss-now.json&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">response</span> <span class="o">=</span> <span class="n">urllib2</span><span class="o">.</span><span class="n">urlopen</span><span class="p">(</span><span class="n">req</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">payload</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">read</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">timestamp</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">fromtimestamp</span><span class="p">(</span><span class="n">payload</span><span class="p">[</span><span class="s1">&#39;timestamp&#39;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="n">date</span> <span class="o">=</span> <span class="n">timestamp</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s1">&#39;%Y-%m-</span><span class="si">%d</span><span class="s1">&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">time</span> <span class="o">=</span> <span class="n">timestamp</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s1">&#39;%H:%M:%S&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">latitude</span> <span class="o">=</span> <span class="n">payload</span><span class="p">[</span><span class="s1">&#39;iss_position&#39;</span><span class="p">][</span><span class="s1">&#39;latitude&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="n">longitude</span> <span class="o">=</span> <span class="n">payload</span><span class="p">[</span><span class="s1">&#39;iss_position&#39;</span><span class="p">][</span><span class="s1">&#39;longitude&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span> <span class="s2">&#34;On </span><span class="si">%s</span><span class="s2"> at </span><span class="si">%s</span><span class="s2">, the lat/long of the ISS was: </span><span class="si">%s</span><span class="s2"> </span><span class="si">%s</span><span class="s2">&#34;</span> <span class="o">%</span> <span class="p">(</span><span class="n">date</span><span class="p">,</span> <span class="n">time</span><span class="p">,</span> <span class="n">latitude</span><span class="p">,</span> <span class="n">longitude</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># sample output:</span>
</span></span><span class="line"><span class="cl"><span class="c1"># On 2017-12-27 at 22:03:14 the lat/long of the ISS was 4.4077, -174.5883</span>
</span></span><span class="line"><span class="cl"><span class="c1"># On 2017-12-27 at 22:03:20 the lat/long of the ISS was 4.0774, -174.3523</span>
</span></span><span class="line"><span class="cl"><span class="c1"># On 2017-12-27 at 22:04:23 the lat/long of the ISS was 0.8722 , -172.0740</span>
</span></span><span class="line"><span class="cl"><span class="c1"># On 2017-12-27 at 22:04:36 the lat/long of the ISS was 0.2359 -171.6230</span>
</span></span><span class="line"><span class="cl"><span class="c1"># On 2017-12-27 at 22:05:06 the lat/long of the ISS was: -1.2658 -170.5585</span>
</span></span><span class="line"><span class="cl"><span class="c1"># On 2017-12-27 at 22:05:11 the lat/long of the ISS was: -1.5203 -170.3780</span></span></span></code></pre></div></div>

<h2 class="relative group">When is the ISS overhead?
    <div id="when-is-the-iss-overhead" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#when-is-the-iss-overhead" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><strong>This endpoint no longer exists, it seems.</strong></p>
<p>The other day, I used the <a href="https://grantwinney.com/what-is-google-maps-api/"  target="_blank" rel="noreferrer">Google Maps API</a> to get the location of the Terminal Tower in Cleveland - the lat/long is 41.4984174, -81.6937287 respectively. We can use another endpoint to <a href="http://open-notify.org/Open-Notify-API/ISS-Pass-Times/"  target="_blank" rel="noreferrer">calculate when the ISS will fly over</a> the Terminal Tower (or any other location of your choice).</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET http://api.open-notify.org/iss-pass.json?lat=41.4984174&amp;lon=-81.6937287</span></span></code></pre></div></div>
<p>In the response, you get the location you requested back, a unix timestamp, and an array of upcoming locations. (You&rsquo;re supposed to be able to specify the number of passes, but it didn&rsquo;t seem to work.)</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;message&#34;</span><span class="p">:</span> <span class="s2">&#34;success&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;request&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;altitude&#34;</span><span class="p">:</span> <span class="mi">100</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;datetime&#34;</span><span class="p">:</span> <span class="mi">1514432297</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;latitude&#34;</span><span class="p">:</span> <span class="mf">41.4984174</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;longitude&#34;</span><span class="p">:</span> <span class="mf">-81.6937287</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;passes&#34;</span><span class="p">:</span> <span class="mi">5</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;response&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;duration&#34;</span><span class="p">:</span> <span class="mi">489</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;risetime&#34;</span><span class="p">:</span> <span class="mi">1514455711</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;duration&#34;</span><span class="p">:</span> <span class="mi">642</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;risetime&#34;</span><span class="p">:</span> <span class="mi">1514461391</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;duration&#34;</span><span class="p">:</span> <span class="mi">600</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;risetime&#34;</span><span class="p">:</span> <span class="mi">1514467219</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;duration&#34;</span><span class="p">:</span> <span class="mi">562</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;risetime&#34;</span><span class="p">:</span> <span class="mi">1514473081</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;duration&#34;</span><span class="p">:</span> <span class="mi">613</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;risetime&#34;</span><span class="p">:</span> <span class="mi">1514478895</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Again, here&rsquo;s a Python2 script if you&rsquo;d like to try it out:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">urllib2</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">json</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">datetime</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">req</span> <span class="o">=</span> <span class="n">urllib2</span><span class="o">.</span><span class="n">Request</span><span class="p">(</span><span class="s2">&#34;http://api.open-notify.org/iss-pass.json?lat=41.4984174&amp;lon=-81.6937287&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">response</span> <span class="o">=</span> <span class="n">urllib2</span><span class="o">.</span><span class="n">urlopen</span><span class="p">(</span><span class="n">req</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">payload</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">read</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">latitude</span> <span class="o">=</span> <span class="n">payload</span><span class="p">[</span><span class="s1">&#39;request&#39;</span><span class="p">][</span><span class="s1">&#39;latitude&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="n">longitude</span> <span class="o">=</span> <span class="n">payload</span><span class="p">[</span><span class="s1">&#39;request&#39;</span><span class="p">][</span><span class="s1">&#39;longitude&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="n">risetime</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">fromtimestamp</span><span class="p">(</span><span class="n">payload</span><span class="p">[</span><span class="s1">&#39;response&#39;</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="s1">&#39;risetime&#39;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="n">duration</span> <span class="o">=</span> <span class="n">payload</span><span class="p">[</span><span class="s1">&#39;response&#39;</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="s1">&#39;duration&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span> <span class="s2">&#34;The next ISS pass for </span><span class="si">%s</span><span class="s2"> </span><span class="si">%s</span><span class="s2"> is </span><span class="si">%s</span><span class="s2"> for </span><span class="si">%s</span><span class="s2"> seconds&#34;</span> <span class="o">%</span> <span class="p">(</span><span class="n">latitude</span><span class="p">,</span> <span class="n">longitude</span><span class="p">,</span> <span class="n">risetime</span><span class="p">,</span> <span class="n">duration</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># sample output:</span>
</span></span><span class="line"><span class="cl"><span class="c1"># The next ISS pass for 41.4984174 -81.6937287 is 2017-12-28 05:08:31 for 489 seconds</span></span></span></code></pre></div></div>

<h2 class="relative group">Who&rsquo;s in Space?
    <div id="whos-in-space" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whos-in-space" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>He also threw in an endpoint for getting the names of astronauts currently in space, but his docs say he has to manually update it, so it <em>may</em> not always up-to-date&hellip; although <a href="https://www.nasa.gov/mission_pages/station/expeditions/expedition54/index.html"  target="_blank" rel="noreferrer">it currently is (expedition 54)</a>.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET http://api.open-notify.org/astros.json</span></span></code></pre></div></div>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;number&#34;</span><span class="p">:</span> <span class="mi">6</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;people&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;craft&#34;</span><span class="p">:</span> <span class="s2">&#34;ISS&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Alexander Misurkin&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;craft&#34;</span><span class="p">:</span> <span class="s2">&#34;ISS&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Mark Vande Hei&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;craft&#34;</span><span class="p">:</span> <span class="s2">&#34;ISS&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Joe Acaba&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;craft&#34;</span><span class="p">:</span> <span class="s2">&#34;ISS&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Anton Shkaplerov&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;craft&#34;</span><span class="p">:</span> <span class="s2">&#34;ISS&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Scott Tingle&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;craft&#34;</span><span class="p">:</span> <span class="s2">&#34;ISS&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Norishige Kanai&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;message&#34;</span><span class="p">:</span> <span class="s2">&#34;success&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Here&rsquo;s one last script, because why not:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">urllib2</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">json</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">datetime</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">req</span> <span class="o">=</span> <span class="n">urllib2</span><span class="o">.</span><span class="n">Request</span><span class="p">(</span><span class="s2">&#34;http://api.open-notify.org/astros.json&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">response</span> <span class="o">=</span> <span class="n">urllib2</span><span class="o">.</span><span class="n">urlopen</span><span class="p">(</span><span class="n">req</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">payload</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">read</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span> <span class="s2">&#34;There are </span><span class="si">%d</span><span class="s2"> people in space:&#34;</span> <span class="o">%</span> <span class="p">(</span><span class="n">payload</span><span class="p">[</span><span class="s1">&#39;number&#39;</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">payload</span><span class="p">[</span><span class="s1">&#39;number&#39;</span><span class="p">]):</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span> <span class="n">payload</span><span class="p">[</span><span class="s1">&#39;people&#39;</span><span class="p">][</span><span class="n">i</span><span class="p">][</span><span class="s1">&#39;name&#39;</span><span class="p">]</span> <span class="o">+</span> <span class="s2">&#34;,&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># sample output:</span>
</span></span><span class="line"><span class="cl"><span class="c1"># There are 6 people in space: Alexander Misurkin, Mark Vande Hei, Joe Acaba, Anton Shkaplerov, Scott Tingle, Norishige Kanai,</span></span></span></code></pre></div></div>

<h2 class="relative group">Thoughts
    <div id="thoughts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#thoughts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>This is a clean API, and really awesome if you want to find and easily consume the location of the ISS for your own purpose, or display a list of upcoming passes on a website or something.</p>
<p>If you want to see some code that uses it, <a href="https://github.com/grantwinney/BlogCodeSamples/tree/master/APIs/IssNotifyApiWrapper"  target="_blank" rel="noreferrer">I wrote a thing</a> and put it on GitHub.</p>
<p>It looks like he opensourced everything too, so if you want to investigate how he did what he did, check out <a href="https://github.com/open-notify"  target="_blank" rel="noreferrer">Open Notify</a> on GitHub. I haven&rsquo;t dug into it yet - I&rsquo;m not sure if he&rsquo;s collecting raw data periodically, or making calculations and caching the result, or just hitting some other API at NASA and passing the results along. It&rsquo;s cool of him to put in the work and make it publicly accessible though.</p>
]]></content:encoded><media:content url="https://grantwinney.com/what-is-iss-notify-api/feature.webp" medium="image" type="image/webp"/></item><item><title>Manage Boards and Cards With the Trello API</title><link>https://grantwinney.com/what-is-trello-api/</link><pubDate>Wed, 27 Dec 2017 20:45:59 +0000</pubDate><guid>https://grantwinney.com/what-is-trello-api/</guid><description>Trello is a virtual kanban board&amp;hellip; or a nice to-do list if you&amp;rsquo;re going solo. I like it, maybe you will too, and their API makes nearly all areas accessible to devs.</description><content:encoded><![CDATA[<p>If you&rsquo;re unfamiliar with Trello, it&rsquo;s a nice service that&rsquo;s similar to a kanban board&hellip; or a to-do list if you&rsquo;re just using it personally like I do. Anyway, I like it - maybe you will too. Let&rsquo;s check out the <a href="https://developers.trello.com/v1.0/reference"  target="_blank" rel="noreferrer">Trello API</a>.</p>
<p>First though, two things to consider:</p>
<ul>
<li>If you&rsquo;re unfamiliar with APIs, you might want to <a href="https://grantwinney.com/what-is-an-api/"  target="_blank" rel="noreferrer">read this first</a> to familiarize yourself.</li>
<li>Install <a href="https://www.getpostman.com/"  target="_blank" rel="noreferrer">Postman</a>, which allows you to access API endpoints without having to write an app, as well as save the calls you make and sync them online.</li>
</ul>
<hr>

<h2 class="relative group">Authorization
    <div id="authorization" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#authorization" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The process for trying things out is nice and easy. Go to the <a href="https://trello.com/app-key"  target="_blank" rel="noreferrer">Developer API Keys</a> screen and copy the &ldquo;Key&rdquo; at the top of the page. Then click where it says &ldquo;generate a Token&rdquo; (it requests access to everything so you can try everything) and copy the token it generates too.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-trello-api/trello-api---create-token.png"
    width="1098"
      height="1168"></figure>

<h2 class="relative group">Trying It Out
    <div id="trying-it-out" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#trying-it-out" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Every request needs to have the key and auth token you copied above. To try it out, create a new board, and then throw a few lists in there, and a few cards in each list. You might want to add a couple descriptions and attachments too. Doing that will make it easier to see how the data is returned to you.</p>

<h3 class="relative group">Get Board Metadata
    <div id="get-board-metadata" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#get-board-metadata" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Open your sample board and check out the URL. Copy the unique 8-character ID and <a href="https://developers.trello.com/v1.0/reference#boardsboardid-1"  target="_blank" rel="noreferrer">retrieve your board&rsquo;s metadata</a>.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET https://api.trello.com/1/boards/Ii6vhIyq?fields=name,url&amp;key=&lt;your-key&gt;&amp;token=&lt;your-token&gt;</span></span></code></pre></div></div>
<p>The ID is returned, but apparently isn&rsquo;t necessary since the 8-character code seems to work just fine for identifying a board too (after all, that still allows for 218 <em>trillion</em> combos).</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;5a43c77676a7de01ddf92550&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Just a test board&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;https://trello.com/b/Ii6vhIyq/just-a-test-board&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h3 class="relative group">Get Cards for a Board
    <div id="get-cards-for-a-board" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#get-cards-for-a-board" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>What if you want to <a href="https://developers.trello.com/v1.0/reference#boardsboardidtest"  target="_blank" rel="noreferrer">get the contents of a board</a>, not just metadata? You can request that too.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">https://api.trello.com/1/boards/&lt;board-id&gt;/cards?key=&lt;your-key&gt;&amp;token=&lt;your-token&gt;</span></span></code></pre></div></div>
<p>Here&rsquo;s part of the result from my sample board. I&rsquo;ve actually got 5 cards, but I&rsquo;m only showing data for 2 of them. There&rsquo;s a lot of unique IDs we can use to look up more information. The first card below has an image attached - see where it has a value for <code>idAttachmentCover</code> where the second card has null? The second card has a description (see <code>desc</code>), checklist (see <code>idChecklists</code>), and an attachment (a link) which doesn&rsquo;t seem to show in the response. Am I missing something?</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;5a43c8080110176567dbd1d5&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;checkItemStates&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;closed&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;dateLastActivity&#34;</span><span class="p">:</span> <span class="s2">&#34;2017-12-27T16:20:39.902Z&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;desc&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;descData&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;idBoard&#34;</span><span class="p">:</span> <span class="s2">&#34;5a43c77676a7de01ddf92550&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;idList&#34;</span><span class="p">:</span> <span class="s2">&#34;5a43c7fb728185304d89c4ac&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;idMembersVoted&#34;</span><span class="p">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;idShort&#34;</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;idAttachmentCover&#34;</span><span class="p">:</span> <span class="s2">&#34;5a43c85796edd0379fa357ce&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;idLabels&#34;</span><span class="p">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;manualCoverAttachment&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;first list, card 2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;pos&#34;</span><span class="p">:</span> <span class="mi">131071</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;shortLink&#34;</span><span class="p">:</span> <span class="s2">&#34;QSWX0iug&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;badges&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;votes&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;attachmentsByType&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;trello&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;board&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;card&#34;</span><span class="p">:</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;viewingMemberVoted&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;subscribed&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;fogbugz&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;checkItems&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;checkItemsChecked&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;comments&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;attachments&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;description&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;due&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;dueComplete&#34;</span><span class="p">:</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;dueComplete&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;due&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;idChecklists&#34;</span><span class="p">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;idMembers&#34;</span><span class="p">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;labels&#34;</span><span class="p">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;shortUrl&#34;</span><span class="p">:</span> <span class="s2">&#34;https://trello.com/c/QSWX0iug&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;subscribed&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;https://trello.com/c/QSWX0iug/2-first-list-card-2&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;5a43c80fa2cc50186c1f8df9&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;checkItemStates&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;closed&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;dateLastActivity&#34;</span><span class="p">:</span> <span class="s2">&#34;2017-12-27T17:10:06.844Z&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;desc&#34;</span><span class="p">:</span> <span class="s2">&#34;some brief description&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;descData&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;emoji&#34;</span><span class="p">:</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;idBoard&#34;</span><span class="p">:</span> <span class="s2">&#34;5a43c77676a7de01ddf92550&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;idList&#34;</span><span class="p">:</span> <span class="s2">&#34;5a43c7feffff9c77011ab8c0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;idMembersVoted&#34;</span><span class="p">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;idShort&#34;</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;idAttachmentCover&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;idLabels&#34;</span><span class="p">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;manualCoverAttachment&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;second list, card 2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;pos&#34;</span><span class="p">:</span> <span class="mi">131071</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;shortLink&#34;</span><span class="p">:</span> <span class="s2">&#34;Ko9IqGHj&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;badges&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;votes&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;attachmentsByType&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;trello&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;board&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;card&#34;</span><span class="p">:</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;viewingMemberVoted&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;subscribed&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;fogbugz&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;checkItems&#34;</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;checkItemsChecked&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;comments&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;attachments&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;description&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;due&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;dueComplete&#34;</span><span class="p">:</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;dueComplete&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;due&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;idChecklists&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;5a43d3805d56ec37f5780b4e&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;idMembers&#34;</span><span class="p">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;labels&#34;</span><span class="p">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;shortUrl&#34;</span><span class="p">:</span> <span class="s2">&#34;https://trello.com/c/Ko9IqGHj&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;subscribed&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;https://trello.com/c/Ko9IqGHj/4-second-list-card-2&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">]</span></span></span></code></pre></div></div>

<h3 class="relative group">Get Details for a Card
    <div id="get-details-for-a-card" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#get-details-for-a-card" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>In Trello, boards have lists, lists have cards, and cards have &hellip; stuff. Like <a href="https://developers.trello.com/v1.0/reference#checklistsid"  target="_blank" rel="noreferrer">checklists</a>, <a href="https://developers.trello.com/v1.0/reference#cardsidattachments"  target="_blank" rel="noreferrer">attachments</a>, etc. Here&rsquo;s what the card I referenced above, with the checklists and whatnot, looks like:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-trello-api/trello-api---card-1.png"
    width="1150"
      height="1414"></figure>
<p>There are endpoints to query all of that data. Let&rsquo;s query the checklist.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET https://api.trello.com/1/checklists/5a43d3805d56ec37f5780b4e?key=&lt;your-key&gt;&amp;token=&lt;your-token&gt;</span></span></code></pre></div></div>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;5a43d3805d56ec37f5780b4e&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Vegetables&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;idBoard&#34;</span><span class="p">:</span> <span class="s2">&#34;5a43c77676a7de01ddf92550&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;idCard&#34;</span><span class="p">:</span> <span class="s2">&#34;5a43c80fa2cc50186c1f8df9&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;pos&#34;</span><span class="p">:</span> <span class="mi">16384</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;checkItems&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;state&#34;</span><span class="p">:</span> <span class="s2">&#34;complete&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;idChecklist&#34;</span><span class="p">:</span> <span class="s2">&#34;5a43d3805d56ec37f5780b4e&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;5a43d387c33b6b72c540fcaa&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Carrots&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;nameData&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;emoji&#34;</span><span class="p">:</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;pos&#34;</span><span class="p">:</span> <span class="mi">17286</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;state&#34;</span><span class="p">:</span> <span class="s2">&#34;incomplete&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;idChecklist&#34;</span><span class="p">:</span> <span class="s2">&#34;5a43d3805d56ec37f5780b4e&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;5a43d3895da5b8803a9ebf80&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Beans&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;nameData&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;emoji&#34;</span><span class="p">:</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;pos&#34;</span><span class="p">:</span> <span class="mi">34431</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;state&#34;</span><span class="p">:</span> <span class="s2">&#34;incomplete&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;idChecklist&#34;</span><span class="p">:</span> <span class="s2">&#34;5a43d3805d56ec37f5780b4e&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;5a43d3e261265ba4b7c9ff0d&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Tomatoes?&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;nameData&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;pos&#34;</span><span class="p">:</span> <span class="mi">51275</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h3 class="relative group">Update a Card
    <div id="update-a-card" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#update-a-card" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>We can also <a href="https://developers.trello.com/v1.0/reference#cardsid-1"  target="_blank" rel="noreferrer">update cards</a>. Here&rsquo;s a <code>PUT</code> request that updates the above card&rsquo;s name and description:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">PUT https://api.trello.com/1/cards/5a43c80fa2cc50186c1f8df9?name=second%20list,%20card%20TOOOO&amp;desc=a%20less%20briefer%20description&amp;key=&lt;your-key&gt;&amp;token=&lt;your-token&gt;</span></span></code></pre></div></div>
<p>The response returns the entire updated record, which is pretty typical for a REST endpoint (but not required&hellip; in fact, the suggestions and recommendations for REST APIs are endless, but there are very few hard and fast rules). Here&rsquo;s what the card looks like after the update:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-trello-api/trello-api---card-2.png"
    width="1150"
      height="328"></figure>

<h2 class="relative group">Thoughts
    <div id="thoughts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#thoughts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The <a href="https://developers.trello.com/v1.0/reference"  target="_blank" rel="noreferrer">documentation</a> is pretty easy to read. There are tons of endpoints for getting as much or as little data as you need, and you can even try most of them out right from their website.</p>
<p>I like that they make it so easy to get an auth token with access to everything, for the purposes of playing around, but that they provide fine-grained access for when you get around to creating a real application. When you&rsquo;re designing an app, it should only request access to what it absolutely needs.</p>
<p>There&rsquo;s way more available than I could possibly even touch the tip of the iceberg with here. There are enough calls to allow you to write some pretty cool apps / browser extensions / whatever. The devs behind it did a really nice job. Now that <a href="https://techcrunch.com/2017/01/09/atlassian-acquires-trello/"  target="_blank" rel="noreferrer">Atlassian purchased Trello</a>, I&rsquo;m hoping it continues to improve where needed - and remain the same where it already works perfectly well!</p>
]]></content:encoded><media:content url="https://grantwinney.com/what-is-trello-api/feature.webp" medium="image" type="image/webp"/></item><item><title>View the Mars Rover, Landsat Images, and More with the NASA API</title><link>https://grantwinney.com/what-is-nasa-api/</link><pubDate>Sun, 24 Dec 2017 19:06:14 +0000</pubDate><guid>https://grantwinney.com/what-is-nasa-api/</guid><description>NASA&amp;rsquo;s API makes their data (such as Mars rover photos) available to anyone who wants to consume it. It&amp;rsquo;s an unprecedented wealth of knowledge, so let&amp;rsquo;s dig in!</description><content:encoded><![CDATA[<p><a href="https://api.nasa.gov/"  target="_blank" rel="noreferrer">NASA&rsquo;s API</a> makes their data (such as Mars rover photos) available to anyone who wants to consume it. It&rsquo;s an unprecedented wealth of knowledge, so let&rsquo;s dig in!</p>
<p>First though, two things to consider:</p>
<ul>
<li>If you&rsquo;re unfamiliar with APIs, you might want to <a href="https://grantwinney.com/what-is-an-api/"  target="_blank" rel="noreferrer">read this first</a> to familiarize yourself.</li>
<li>Install <a href="https://www.getpostman.com/"  target="_blank" rel="noreferrer">Postman</a>, which allows you to access API endpoints without having to write an app, as well as save the calls you make and sync them online.</li>
</ul>
<hr>

<h2 class="relative group">Authorization
    <div id="authorization" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#authorization" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>You aren&rsquo;t required to get an API key <em>(you can just use the string &ldquo;DEMO_KEY&rdquo; instead of an actual key),</em> but you might as well. Without one, the limit is 50 requests per day - with one, it&rsquo;s (usually) 1000 requests per hour. <a href="https://api.nasa.gov/#signUp"  target="_blank" rel="noreferrer">Fill out the form</a>, and it immediately displays an API key on the same page.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="nasa-api&mdash;get-api-key"
    src="/what-is-nasa-api/nasa-api---get-api-key.png"
    width="876"
      height="348"></figure>
<p>Interestingly, they also refer to this as your api.data.gov API key &hellip; not sure where else this key is supposed to work, or whether they just have plans for the future. But at the very least, it seems to work across the various APIs published by NASA.</p>

<h2 class="relative group">Requesting Data
    <div id="requesting-data" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#requesting-data" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Just append your API key as a parameter to any of the <a href="https://api.nasa.gov/#browseAPI"  target="_blank" rel="noreferrer">available API requests</a> <em>(scroll down towards the bottom of the page if the link doesn&rsquo;t take you there)</em>. Try a simple one first, to make sure your API key works.</p>

<h3 class="relative group">Photo of the Day
    <div id="photo-of-the-day" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#photo-of-the-day" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>There&rsquo;s one that returns <a href="https://api.nasa.gov/#apod"  target="_blank" rel="noreferrer">metadata about the photo of the day</a>.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">GET https://api.nasa.gov/planetary/apod?api_key=&lt;your-api-key&gt;</code></pre></div>
<p>You could parse the result and display the photo on your personal site, for example.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;copyright&#34;</span><span class="p">:</span> <span class="s2">&#34;Craig Bobchin&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;date&#34;</span><span class="p">:</span> <span class="s2">&#34;2017-12-24&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;explanation&#34;</span><span class="p">:</span> <span class="s2">&#34;What&#39;s happened to the sky? On Friday, the photogenic launch plume from a SpaceX rocket launch created quite a spectacle over parts of southern California and Arizona.  Looking at times like a giant space fish, the impressive rocket launch from Vandenberg Air Force Base near Lompoc, California, was so bright because it was backlit by the setting Sun. Lifting off during a minuscule one-second launch window, the Falcon 9 Heavy rocket successfully delivered to low Earth orbit ten Iridium NEXT satellites that are part of a developing global communications network. The plume from the first stage is seen on the right, while the soaring upper stage rocket is seen at the apex of the plume toward the left. Several good videos of the launch were taken.  The featured image was captured from Orange County, California, in a 2.5 second duration exposure.   Gallery: More images of the SpaceX launch&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;hdurl&#34;</span><span class="p">:</span> <span class="s2">&#34;https://apod.nasa.gov/apod/image/1712/SpaceXLaunch_Bobchin_5407.jpg&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;media_type&#34;</span><span class="p">:</span> <span class="s2">&#34;image&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;service_version&#34;</span><span class="p">:</span> <span class="s2">&#34;v1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;title&#34;</span><span class="p">:</span> <span class="s2">&#34;SpaceX Rocket Launch Plume over California&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;https://apod.nasa.gov/apod/image/1712/SpaceXLaunch_Bobchin_960.jpg&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="https://apod.nasa.gov/apod/image/1712/SpaceXLaunch_Bobchin_960.jpg"
    ></figure>

<h3 class="relative group">Mars Rover Photos (Spirit)
    <div id="mars-rover-photos-spirit" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#mars-rover-photos-spirit" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>More photos! There&rsquo;s an API someone created just for retrieving photos from the various cameras on the various Mars rovers, going back 15 years.</p>
<p>Here&rsquo;s a request for archived photos from May 9, 2009 (1900th &ldquo;day&rdquo; since landing), from the navigational camera aboard the <a href="https://www.jpl.nasa.gov/missions/mars-exploration-rover-spirit-mer-spirit/"  target="_blank" rel="noreferrer">Mars Spirit Rover</a> (its mission ran from 2003 to 2011).</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">GET https://api.nasa.gov/mars-photos/api/v1/rovers/spirit/photos?sol=1900&amp;camera=NAVCAM&amp;api_key=&lt;your-api-key&gt;</code></pre></div>
<p>And here&rsquo;s the result. I had absolutely no idea all of this data was out there. Imagine, tens of thousands of photos spanning years and years, and it&rsquo;s all available for the querying.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;photos&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">292443</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;sol&#34;</span><span class="p">:</span> <span class="mi">1900</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;camera&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">29</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;NAVCAM&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;rover_id&#34;</span><span class="p">:</span> <span class="mi">7</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;full_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Navigation Camera&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;img_src&#34;</span><span class="p">:</span> <span class="s2">&#34;http://mars.nasa.gov/mer/gallery/all/2/n/1900/2N295043846EFFB1DNP1979L0M1-BR.JPG&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;earth_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2009-05-09&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;rover&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">7</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Spirit&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;landing_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2004-01-04&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;launch_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2003-06-10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;complete&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;max_sol&#34;</span><span class="p">:</span> <span class="mi">2208</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;max_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2010-03-21&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;total_photos&#34;</span><span class="p">:</span> <span class="mi">124550</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;cameras&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                    <span class="err">...</span>
</span></span><span class="line"><span class="cl">                <span class="p">]</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">292444</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;sol&#34;</span><span class="p">:</span> <span class="mi">1900</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;camera&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">29</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;NAVCAM&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;rover_id&#34;</span><span class="p">:</span> <span class="mi">7</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;full_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Navigation Camera&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;img_src&#34;</span><span class="p">:</span> <span class="s2">&#34;http://mars.nasa.gov/mer/gallery/all/2/n/1900/2N295043846EFFB1DNP1979R0M1-BR.JPG&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;earth_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2009-05-09&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;rover&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">7</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Spirit&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;landing_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2004-01-04&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;launch_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2003-06-10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;complete&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;max_sol&#34;</span><span class="p">:</span> <span class="mi">2208</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;max_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2010-03-21&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;total_photos&#34;</span><span class="p">:</span> <span class="mi">124550</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;cameras&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                    <span class="err">...</span>
</span></span><span class="line"><span class="cl">                <span class="p">]</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">292445</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;sol&#34;</span><span class="p">:</span> <span class="mi">1900</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;camera&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">29</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;NAVCAM&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;rover_id&#34;</span><span class="p">:</span> <span class="mi">7</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;full_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Navigation Camera&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;img_src&#34;</span><span class="p">:</span> <span class="s2">&#34;http://mars.nasa.gov/mer/gallery/all/2/n/1900/2N295043924EFFB1DNP1979L0M1-BR.JPG&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;earth_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2009-05-09&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;rover&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">7</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Spirit&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;landing_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2004-01-04&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;launch_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2003-06-10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;complete&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;max_sol&#34;</span><span class="p">:</span> <span class="mi">2208</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;max_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2010-03-21&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;total_photos&#34;</span><span class="p">:</span> <span class="mi">124550</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;cameras&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                    <span class="err">...</span>
</span></span><span class="line"><span class="cl">                <span class="p">]</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">292446</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;sol&#34;</span><span class="p">:</span> <span class="mi">1900</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;camera&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">29</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;NAVCAM&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;rover_id&#34;</span><span class="p">:</span> <span class="mi">7</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;full_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Navigation Camera&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;img_src&#34;</span><span class="p">:</span> <span class="s2">&#34;http://mars.nasa.gov/mer/gallery/all/2/n/1900/2N295043924EFFB1DNP1979R0M1-BR.JPG&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;earth_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2009-05-09&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;rover&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">7</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Spirit&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;landing_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2004-01-04&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;launch_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2003-06-10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;complete&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;max_sol&#34;</span><span class="p">:</span> <span class="mi">2208</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;max_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2010-03-21&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;total_photos&#34;</span><span class="p">:</span> <span class="mi">124550</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;cameras&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                    <span class="err">...</span>
</span></span><span class="line"><span class="cl">                <span class="p">]</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h3 class="relative group">Mars Rover Photos (Curiosity)
    <div id="mars-rover-photos-curiosity" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#mars-rover-photos-curiosity" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Let&rsquo;s check the cameras on the Curiosity, which is currently still running. To get photos from earlier this year (Feb 4, the 1600th &ldquo;day&rdquo; since landing):</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">GET https://api.nasa.gov/mars-photos/api/v1/rovers/curiosity/photos?sol=1600&amp;api_key=&lt;your-api-key&gt;</code></pre></div>
<p>The returned dataset includes over 13000 lines of JSON, so&rsquo;s here&rsquo;s a couple photos worth - and a few actual photos again.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;photos&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">612417</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;sol&#34;</span><span class="p">:</span> <span class="mi">1600</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;camera&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">20</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;FHAZ&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;rover_id&#34;</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;full_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Front Hazard Avoidance Camera&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;img_src&#34;</span><span class="p">:</span> <span class="s2">&#34;http://mars.jpl.nasa.gov/msl-raw-images/proj/msl/redops/ods/surface/sol/01600/opgs/edr/fcam/FLB_539548779EDR_F0602928FHAZ00337M_.JPG&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;earth_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2017-02-04&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;rover&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Curiosity&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;landing_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2012-08-06&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;launch_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2011-11-26&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;active&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;max_sol&#34;</span><span class="p">:</span> <span class="mi">1912</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;max_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2017-12-22&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;total_photos&#34;</span><span class="p">:</span> <span class="mi">328169</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;cameras&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                    <span class="err">...</span>
</span></span><span class="line"><span class="cl">                <span class="p">]</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">612418</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;sol&#34;</span><span class="p">:</span> <span class="mi">1600</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;camera&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">20</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;FHAZ&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;rover_id&#34;</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;full_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Front Hazard Avoidance Camera&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;img_src&#34;</span><span class="p">:</span> <span class="s2">&#34;http://mars.jpl.nasa.gov/msl-raw-images/proj/msl/redops/ods/surface/sol/01600/opgs/edr/fcam/FRB_539548779EDR_F0602928FHAZ00337M_.JPG&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;earth_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2017-02-04&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;rover&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Curiosity&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;landing_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2012-08-06&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;launch_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2011-11-26&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;active&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;max_sol&#34;</span><span class="p">:</span> <span class="mi">1912</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;max_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2017-12-22&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;total_photos&#34;</span><span class="p">:</span> <span class="mi">328169</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;cameras&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                    <span class="err">...</span>
</span></span><span class="line"><span class="cl">                <span class="p">]</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="err">...</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-nasa-api/1600MR0081460100800609E02_DXXX.jpg"
    width="1328"
      height="1184"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-nasa-api/1600MR0081460240800623E01_DXXX.jpg"
    width="1328"
      height="1184"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-nasa-api/NRB_539546160EDR_F0602928NCAM00207M_.jpg"
    width="1024"
      height="1024"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-nasa-api/NRB_539547976EDR_F0602928NCAM00207M_.jpg"
    width="1024"
      height="1024"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-nasa-api/NLB_539546160EDR_F0602928NCAM00207M_.jpg"
    width="1024"
      height="1024"></figure>

<h2 class="relative group">Thoughts
    <div id="thoughts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#thoughts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There are lots of other APIs to experiment with too, but it&rsquo;s Christmas Eve and I can&rsquo;t sit around playing forever. ;) This was a nice set of APIs to discover though, and I&rsquo;m excited to discover more about them in the future.</p>
<p>It&rsquo;s great that NASA has made such an effort to publicize the data it&rsquo;s amassed over the years. Even more amazing is that this data - which could&rsquo;ve been kept on a server somewhere inaccessible - is available to anyone in the <em>world</em> who requests it!</p>
]]></content:encoded><media:content url="https://grantwinney.com/what-is-nasa-api/feature.webp" medium="image" type="image/webp"/></item><item><title>Access Climate Data With the NOAA API</title><link>https://grantwinney.com/what-is-noaa-api/</link><pubDate>Sun, 24 Dec 2017 03:44:02 +0000</pubDate><guid>https://grantwinney.com/what-is-noaa-api/</guid><description>The NOAA API lets us query weather and climate data from NOAA, an agency that studies and charts conditions in the oceans and atmosphere. Let&amp;rsquo;s check it out!</description><content:encoded><![CDATA[<p><a href="http://www.noaa.gov/"  target="_blank" rel="noreferrer">NOAA</a> is an American agency that studies and charts various conditions in the oceans and atmosphere, and today we&rsquo;re going to check out the <a href="https://www.ncdc.noaa.gov/cdo-web/webservices/v2"  target="_blank" rel="noreferrer">NOAA API</a>.</p>
<p>First though, two things to consider:</p>
<ul>
<li>If you&rsquo;re unfamiliar with APIs, you might want to <a href="https://grantwinney.com/what-is-an-api/"  target="_blank" rel="noreferrer">read this first</a> to familiarize yourself.</li>
<li>Install <a href="https://www.getpostman.com/"  target="_blank" rel="noreferrer">Postman</a>, which allows you to access API endpoints without having to write an app, as well as save the calls you make and sync them online.</li>
</ul>
<hr>

<h2 class="relative group">Authorization
    <div id="authorization" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#authorization" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>First things first&hellip; we need an access token. NOAA uses a slightly differently method than the other ones I&rsquo;ve seen so far, but it&rsquo;s minor. You need to provide an email address and they&rsquo;ll email you a unique token. You can <a href="https://www.ncdc.noaa.gov/cdo-web/token"  target="_blank" rel="noreferrer">request an API token</a> here. I got the email within seconds.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="noaa-api&mdash;request-token-1"
    src="/what-is-noaa-api/noaa-api---request-token-1.png"
    width="619"
      height="337"></figure>
<p>The limits are very generous if you&rsquo;re using it for a small project for yourself or a team - <em>&ldquo;each token will be limited to five requests per second and 10,000 requests per day&rdquo;</em>.</p>
<p>For any request you make, include the access token as a header named &ldquo;token&rdquo;.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="noaa-api&mdash;token-header"
    src="/what-is-noaa-api/noaa-api---token-header.png"
    width="1368"
      height="208"></figure>

<h2 class="relative group">Requesting Data
    <div id="requesting-data" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#requesting-data" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Their <a href="https://www.ncdc.noaa.gov/cdo-web/webservices/v2"  target="_blank" rel="noreferrer">web services documentation</a> is pretty straight-forward. It follows a simple format, and each endpoint you can hit is in a tab along the top of that page.</p>

<h3 class="relative group">Get All Stations
    <div id="get-all-stations" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#get-all-stations" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Try requesting all stations using:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET https://www.ncdc.noaa.gov/cdo-web/api/v2/stations</span></span></code></pre></div></div>
<p>You&rsquo;ll get the first 25 results. Check out the metadata section that tells you the offset, the number of records returned (limit), and the total records (count). You can adjust the results using &ldquo;offset&rdquo; and &ldquo;limit&rdquo; parameters.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;metadata&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;resultset&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;offset&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;count&#34;</span><span class="p">:</span> <span class="mi">128495</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;limit&#34;</span><span class="p">:</span> <span class="mi">25</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;results&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;elevation&#34;</span><span class="p">:</span> <span class="mi">139</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;mindate&#34;</span><span class="p">:</span> <span class="s2">&#34;1948-01-01&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;maxdate&#34;</span><span class="p">:</span> <span class="s2">&#34;2014-01-01&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;latitude&#34;</span><span class="p">:</span> <span class="mf">31.5702</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;ABBEVILLE, AL US&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datacoverage&#34;</span><span class="p">:</span> <span class="mf">0.8813</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;COOP:010008&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;elevationUnit&#34;</span><span class="p">:</span> <span class="s2">&#34;METERS&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;longitude&#34;</span><span class="p">:</span> <span class="mf">-85.2482</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;elevation&#34;</span><span class="p">:</span> <span class="mf">249.3</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;mindate&#34;</span><span class="p">:</span> <span class="s2">&#34;1938-01-01&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;maxdate&#34;</span><span class="p">:</span> <span class="s2">&#34;2015-11-01&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;latitude&#34;</span><span class="p">:</span> <span class="mf">34.2553</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;ADDISON, AL US&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datacoverage&#34;</span><span class="p">:</span> <span class="mf">0.5059</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;COOP:010063&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;elevationUnit&#34;</span><span class="p">:</span> <span class="s2">&#34;METERS&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;longitude&#34;</span><span class="p">:</span> <span class="mf">-87.1814</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="err">...</span>
</span></span><span class="line"><span class="cl">        <span class="err">...</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h3 class="relative group">Get a Subset of Stations
    <div id="get-a-subset-of-stations" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#get-a-subset-of-stations" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>There are certain filters that can be applied. You can limit stations to a certain geographical location using a FIPS (Federal Information Processing System) code. I don&rsquo;t know if there&rsquo;s a central source for these codes, but <a href="https://census.gov/geographies/reference-files/2016/demo/popest/2016-fips.html"  target="_blank" rel="noreferrer">here are some</a>.</p>
<p>Since it&rsquo;s an excel sheet and not everyone can open it, here&rsquo;s a portion of it reproduced:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">09 - Connecticut
</span></span><span class="line"><span class="cl">23 - Maine
</span></span><span class="line"><span class="cl">25 - Massachusetts
</span></span><span class="line"><span class="cl">33 - New Hampshire
</span></span><span class="line"><span class="cl">44 - Rhode Island
</span></span><span class="line"><span class="cl">50 - Vermont
</span></span><span class="line"><span class="cl">34 - New Jersey
</span></span><span class="line"><span class="cl">36 - New York
</span></span><span class="line"><span class="cl">42 - Pennsylvania
</span></span><span class="line"><span class="cl">17 - Illinois
</span></span><span class="line"><span class="cl">18 - Indiana
</span></span><span class="line"><span class="cl">26 - Michigan
</span></span><span class="line"><span class="cl">39 - Ohio
</span></span><span class="line"><span class="cl">55 - Wisconsin
</span></span><span class="line"><span class="cl">19 - Iowa
</span></span><span class="line"><span class="cl">20 - Kansas
</span></span><span class="line"><span class="cl">27 - Minnesota
</span></span><span class="line"><span class="cl">29 - Missouri
</span></span><span class="line"><span class="cl">31 - Nebraska
</span></span><span class="line"><span class="cl">38 - North Dakota
</span></span><span class="line"><span class="cl">46 - South Dakota
</span></span><span class="line"><span class="cl">10 - Delaware
</span></span><span class="line"><span class="cl">11 - District of Columbia
</span></span><span class="line"><span class="cl">12 - Florida
</span></span><span class="line"><span class="cl">13 - Georgia
</span></span><span class="line"><span class="cl">24 - Maryland
</span></span><span class="line"><span class="cl">37 - North Carolina
</span></span><span class="line"><span class="cl">45 - South Carolina
</span></span><span class="line"><span class="cl">51 - Virginia
</span></span><span class="line"><span class="cl">54 - West Virginia
</span></span><span class="line"><span class="cl">01 - Alabama
</span></span><span class="line"><span class="cl">21 - Kentucky
</span></span><span class="line"><span class="cl">28 - Mississippi
</span></span><span class="line"><span class="cl">47 - Tennessee
</span></span><span class="line"><span class="cl">05 - Arkansas
</span></span><span class="line"><span class="cl">22 - Louisiana
</span></span><span class="line"><span class="cl">40 - Oklahoma
</span></span><span class="line"><span class="cl">48 - Texas
</span></span><span class="line"><span class="cl">04 - Arizona
</span></span><span class="line"><span class="cl">08 - Colorado
</span></span><span class="line"><span class="cl">16 - Idaho
</span></span><span class="line"><span class="cl">30 - Montana
</span></span><span class="line"><span class="cl">32 - Nevada
</span></span><span class="line"><span class="cl">35 - New Mexico
</span></span><span class="line"><span class="cl">49 - Utah
</span></span><span class="line"><span class="cl">56 - Wyoming
</span></span><span class="line"><span class="cl">02 - Alaska
</span></span><span class="line"><span class="cl">06 - California
</span></span><span class="line"><span class="cl">15 - Hawaii
</span></span><span class="line"><span class="cl">41 - Oregon
</span></span><span class="line"><span class="cl">53 - Washington</span></span></code></pre></div></div>
<p>I made the same call as previously, but used the FIPS code for Maine, limited the result to 5 records, and sorted by oldest <code>mindate</code> first:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET https://www.ncdc.noaa.gov/cdo-web/api/v2/stations?locationid=FIPS:23&amp;limit=5&amp;sortfield=mindate</span></span></code></pre></div></div>
<p>Here are the results. Not sure what the dates mean.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;metadata&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;resultset&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;offset&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;count&#34;</span><span class="p">:</span> <span class="mi">670</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;limit&#34;</span><span class="p">:</span> <span class="mi">5</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;results&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;elevation&#34;</span><span class="p">:</span> <span class="mf">88.1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;mindate&#34;</span><span class="p">:</span> <span class="s2">&#34;1885-05-01&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;maxdate&#34;</span><span class="p">:</span> <span class="s2">&#34;1886-08-31&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;latitude&#34;</span><span class="p">:</span> <span class="mf">43.760911</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;SEBAGO LAKE, ME US&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datacoverage&#34;</span><span class="p">:</span> <span class="mf">0.998</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;GHCND:USC00177630&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;elevationUnit&#34;</span><span class="p">:</span> <span class="s2">&#34;METERS&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;longitude&#34;</span><span class="p">:</span> <span class="mf">-70.52561</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;elevation&#34;</span><span class="p">:</span> <span class="mf">304.8</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;mindate&#34;</span><span class="p">:</span> <span class="s2">&#34;1885-06-01&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;maxdate&#34;</span><span class="p">:</span> <span class="s2">&#34;1908-01-31&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;latitude&#34;</span><span class="p">:</span> <span class="mf">45.133333</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;MAYFIELD, ME US&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datacoverage&#34;</span><span class="p">:</span> <span class="mf">0.933</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;GHCND:USC00175070&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;elevationUnit&#34;</span><span class="p">:</span> <span class="s2">&#34;METERS&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;longitude&#34;</span><span class="p">:</span> <span class="mf">-69.683333</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;elevation&#34;</span><span class="p">:</span> <span class="mf">152.4</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;mindate&#34;</span><span class="p">:</span> <span class="s2">&#34;1885-10-01&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;maxdate&#34;</span><span class="p">:</span> <span class="s2">&#34;1894-02-28&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;latitude&#34;</span><span class="p">:</span> <span class="mf">44.416667</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;KENTS HILL, ME US&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datacoverage&#34;</span><span class="p">:</span> <span class="mf">0.8617</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;GHCND:USC00174230&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;elevationUnit&#34;</span><span class="p">:</span> <span class="s2">&#34;METERS&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;longitude&#34;</span><span class="p">:</span> <span class="mf">-70.083333</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;elevation&#34;</span><span class="p">:</span> <span class="mf">27.4</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;mindate&#34;</span><span class="p">:</span> <span class="s2">&#34;1886-02-01&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;maxdate&#34;</span><span class="p">:</span> <span class="s2">&#34;1914-12-31&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;latitude&#34;</span><span class="p">:</span> <span class="mf">44.583333</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;FAIRFIELD, ME US&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datacoverage&#34;</span><span class="p">:</span> <span class="mf">0.9829</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;GHCND:USC00172740&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;elevationUnit&#34;</span><span class="p">:</span> <span class="s2">&#34;METERS&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;longitude&#34;</span><span class="p">:</span> <span class="mf">-69.583333</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;elevation&#34;</span><span class="p">:</span> <span class="mf">40.5</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;mindate&#34;</span><span class="p">:</span> <span class="s2">&#34;1886-09-01&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;maxdate&#34;</span><span class="p">:</span> <span class="s2">&#34;2017-10-31&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;latitude&#34;</span><span class="p">:</span> <span class="mf">44.2202</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;GARDINER, ME US&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datacoverage&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;GHCND:USC00173046&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;elevationUnit&#34;</span><span class="p">:</span> <span class="s2">&#34;METERS&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;longitude&#34;</span><span class="p">:</span> <span class="mf">-69.789</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h3 class="relative group">Get Available Datasets for a Station
    <div id="get-available-datasets-for-a-station" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#get-available-datasets-for-a-station" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Once you&rsquo;ve got a list of stations, you can get data for a station. But what set of data do you want? In order to determine that, you&rsquo;ll need to query to see what datasets are available. I selected the last one from the results of the previous query.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET https://www.ncdc.noaa.gov/cdo-web/api/v2/datasets?stationid=GHCND:USC00173046</span></span></code></pre></div></div>
<p>At first glance, it appears I can choose from daily, monthly, and yearly summaries.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;metadata&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;resultset&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;offset&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;count&#34;</span><span class="p">:</span> <span class="mi">6</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;limit&#34;</span><span class="p">:</span> <span class="mi">25</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;results&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;uid&#34;</span><span class="p">:</span> <span class="s2">&#34;gov.noaa.ncdc:C00861&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;mindate&#34;</span><span class="p">:</span> <span class="s2">&#34;1763-01-01&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;maxdate&#34;</span><span class="p">:</span> <span class="s2">&#34;2017-12-20&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Daily Summaries&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datacoverage&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;GHCND&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;uid&#34;</span><span class="p">:</span> <span class="s2">&#34;gov.noaa.ncdc:C00946&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;mindate&#34;</span><span class="p">:</span> <span class="s2">&#34;1763-01-01&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;maxdate&#34;</span><span class="p">:</span> <span class="s2">&#34;2017-11-01&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Global Summary of the Month&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datacoverage&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;GSOM&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;uid&#34;</span><span class="p">:</span> <span class="s2">&#34;gov.noaa.ncdc:C00947&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;mindate&#34;</span><span class="p">:</span> <span class="s2">&#34;1763-01-01&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;maxdate&#34;</span><span class="p">:</span> <span class="s2">&#34;2017-01-01&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Global Summary of the Year&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datacoverage&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;GSOY&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;uid&#34;</span><span class="p">:</span> <span class="s2">&#34;gov.noaa.ncdc:C00821&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;mindate&#34;</span><span class="p">:</span> <span class="s2">&#34;2010-01-01&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;maxdate&#34;</span><span class="p">:</span> <span class="s2">&#34;2010-01-01&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Normals Annual/Seasonal&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datacoverage&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;NORMAL_ANN&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;uid&#34;</span><span class="p">:</span> <span class="s2">&#34;gov.noaa.ncdc:C00823&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;mindate&#34;</span><span class="p">:</span> <span class="s2">&#34;2010-01-01&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;maxdate&#34;</span><span class="p">:</span> <span class="s2">&#34;2010-12-31&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Normals Daily&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datacoverage&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;NORMAL_DLY&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;uid&#34;</span><span class="p">:</span> <span class="s2">&#34;gov.noaa.ncdc:C00822&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;mindate&#34;</span><span class="p">:</span> <span class="s2">&#34;2010-01-01&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;maxdate&#34;</span><span class="p">:</span> <span class="s2">&#34;2010-12-01&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Normals Monthly&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datacoverage&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;NORMAL_MLY&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h2 class="relative group">Get Data for a Station
    <div id="get-data-for-a-station" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#get-data-for-a-station" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>You&rsquo;ve got a station you&rsquo;re interested in, and the dataset for that station, so all that&rsquo;s left is querying for the actual data. Unfortunately, I have no clue what the returned data <em>means</em>.</p>
<p>Oh well, let&rsquo;s get the data first, then try figuring out what it means.</p>

<h3 class="relative group">Get Daily Summary
    <div id="get-daily-summary" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#get-daily-summary" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>This request gets the daily summary for the same station we looked up previously. I limited it to the month of January because trying to get the entire year didn&rsquo;t finish after waiting several minutes.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET https://www.ncdc.noaa.gov/cdo-web/api/v2/data?stationid=GHCND:USC00173046&amp;datasetid=GHCND&amp;startdate=2017-01-01&amp;enddate=2017-01-31</span></span></code></pre></div></div>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;metadata&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;resultset&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;offset&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;count&#34;</span><span class="p">:</span> <span class="mi">186</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;limit&#34;</span><span class="p">:</span> <span class="mi">25</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;results&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;date&#34;</span><span class="p">:</span> <span class="s2">&#34;2017-01-01T00:00:00&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datatype&#34;</span><span class="p">:</span> <span class="s2">&#34;PRCP&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;station&#34;</span><span class="p">:</span> <span class="s2">&#34;GHCND:USC00173046&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;attributes&#34;</span><span class="p">:</span> <span class="s2">&#34;,,7,0700&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">61</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;date&#34;</span><span class="p">:</span> <span class="s2">&#34;2017-01-01T00:00:00&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datatype&#34;</span><span class="p">:</span> <span class="s2">&#34;SNOW&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;station&#34;</span><span class="p">:</span> <span class="s2">&#34;GHCND:USC00173046&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;attributes&#34;</span><span class="p">:</span> <span class="s2">&#34;,,7,&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">76</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;date&#34;</span><span class="p">:</span> <span class="s2">&#34;2017-01-01T00:00:00&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datatype&#34;</span><span class="p">:</span> <span class="s2">&#34;SNWD&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;station&#34;</span><span class="p">:</span> <span class="s2">&#34;GHCND:USC00173046&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;attributes&#34;</span><span class="p">:</span> <span class="s2">&#34;,,7,&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">279</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;date&#34;</span><span class="p">:</span> <span class="s2">&#34;2017-01-01T00:00:00&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datatype&#34;</span><span class="p">:</span> <span class="s2">&#34;TMAX&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;station&#34;</span><span class="p">:</span> <span class="s2">&#34;GHCND:USC00173046&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;attributes&#34;</span><span class="p">:</span> <span class="s2">&#34;,,7,0700&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">6</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;date&#34;</span><span class="p">:</span> <span class="s2">&#34;2017-01-01T00:00:00&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datatype&#34;</span><span class="p">:</span> <span class="s2">&#34;TMIN&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;station&#34;</span><span class="p">:</span> <span class="s2">&#34;GHCND:USC00173046&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;attributes&#34;</span><span class="p">:</span> <span class="s2">&#34;,,7,0700&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">-133</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;date&#34;</span><span class="p">:</span> <span class="s2">&#34;2017-01-01T00:00:00&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datatype&#34;</span><span class="p">:</span> <span class="s2">&#34;TOBS&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;station&#34;</span><span class="p">:</span> <span class="s2">&#34;GHCND:USC00173046&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;attributes&#34;</span><span class="p">:</span> <span class="s2">&#34;,,7,0700&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">6</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="err">...</span>
</span></span><span class="line"><span class="cl">        <span class="err">...</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The above is just a portion of the results. The &ldquo;station&rdquo; is the one I specified, and the &ldquo;date&rdquo; falls within the range I specified. The &ldquo;datatype&rdquo; is a code that indicates what the record refers to.</p>
<p>I don&rsquo;t feel like going into codes too deeply - you can <a href="undefined" >find more here</a>, under the header &ldquo;III. FORMAT OF DATA FILES&rdquo; - but here are a few to match the results above:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">PRCP = Precipitation (tenths of mm)
</span></span><span class="line"><span class="cl">SNOW = Snowfall (mm)
</span></span><span class="line"><span class="cl">SNWD = Snow depth (mm)
</span></span><span class="line"><span class="cl">TMAX = Maximum temperature (tenths of degrees C)
</span></span><span class="line"><span class="cl">TMIN = Minimum temperature (tenths of degrees C)
</span></span><span class="line"><span class="cl">TOBS = Temperature at the time of observation (tenths of degrees C)</span></span></code></pre></div></div>
<p>I&rsquo;m guessing that &ldquo;value&rdquo; is self-explanatory - like 279 for SNWD is 279mm or about 11&quot; of snow; and 6 for TOBS means .6°C, or about 33°F. The &ldquo;attributes&rdquo; though - not sure what those mean. <em>(update: thanks to</em> <a href="https://disqus.com/by/disqus_oHegIKlHsZ/"  target="_blank" rel="noreferrer"><em>Tim Erickson</em></a> <em>for finding a reference to</em> <a href="https://web.archive.org/web/20170718224700/https://cran.r-project.org/web/packages/rnoaa/vignettes/ncdc_attributes.html"  target="_blank" rel="noreferrer"><em>NOAA NCDC dataset attributes</em></a><em>!)</em></p>

<h3 class="relative group">Get Yearly Summary
    <div id="get-yearly-summary" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#get-yearly-summary" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Here&rsquo;s one last one - same station as before, but using the &ldquo;yearly&rdquo; dataset filtered to about a 7 year period.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET https://www.ncdc.noaa.gov/cdo-web/api/v2/data?stationid=GHCND:USC00173046&amp;datasetid=GSOY&amp;startdate=2010-01-01&amp;enddate=2017-01-31</span></span></code></pre></div></div>
<p>The results contain different codes than before, but I couldn&rsquo;t find definitions for them this time&hellip; so I&rsquo;m not even sure what to make of this but here it is anyway.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;metadata&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;resultset&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;offset&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;count&#34;</span><span class="p">:</span> <span class="mi">214</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;limit&#34;</span><span class="p">:</span> <span class="mi">25</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;results&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;date&#34;</span><span class="p">:</span> <span class="s2">&#34;2010-01-01T00:00:00&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datatype&#34;</span><span class="p">:</span> <span class="s2">&#34;CDSD&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;station&#34;</span><span class="p">:</span> <span class="s2">&#34;GHCND:USC00173046&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mf">248.6</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;date&#34;</span><span class="p">:</span> <span class="s2">&#34;2010-01-01T00:00:00&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datatype&#34;</span><span class="p">:</span> <span class="s2">&#34;CLDD&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;station&#34;</span><span class="p">:</span> <span class="s2">&#34;GHCND:USC00173046&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;attributes&#34;</span><span class="p">:</span> <span class="s2">&#34;0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mf">248.6</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;date&#34;</span><span class="p">:</span> <span class="s2">&#34;2010-01-01T00:00:00&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datatype&#34;</span><span class="p">:</span> <span class="s2">&#34;DP01&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;station&#34;</span><span class="p">:</span> <span class="s2">&#34;GHCND:USC00173046&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;attributes&#34;</span><span class="p">:</span> <span class="s2">&#34;0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">118</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;date&#34;</span><span class="p">:</span> <span class="s2">&#34;2010-01-01T00:00:00&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datatype&#34;</span><span class="p">:</span> <span class="s2">&#34;DP10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;station&#34;</span><span class="p">:</span> <span class="s2">&#34;GHCND:USC00173046&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;attributes&#34;</span><span class="p">:</span> <span class="s2">&#34;0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">90</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;date&#34;</span><span class="p">:</span> <span class="s2">&#34;2010-01-01T00:00:00&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datatype&#34;</span><span class="p">:</span> <span class="s2">&#34;DP1X&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;station&#34;</span><span class="p">:</span> <span class="s2">&#34;GHCND:USC00173046&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;attributes&#34;</span><span class="p">:</span> <span class="s2">&#34;0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">19</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;date&#34;</span><span class="p">:</span> <span class="s2">&#34;2010-01-01T00:00:00&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datatype&#34;</span><span class="p">:</span> <span class="s2">&#34;DSND&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;station&#34;</span><span class="p">:</span> <span class="s2">&#34;GHCND:USC00173046&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">78</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;date&#34;</span><span class="p">:</span> <span class="s2">&#34;2010-01-01T00:00:00&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datatype&#34;</span><span class="p">:</span> <span class="s2">&#34;DSNW&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;station&#34;</span><span class="p">:</span> <span class="s2">&#34;GHCND:USC00173046&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;attributes&#34;</span><span class="p">:</span> <span class="s2">&#34;0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">16</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;date&#34;</span><span class="p">:</span> <span class="s2">&#34;2010-01-01T00:00:00&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;datatype&#34;</span><span class="p">:</span> <span class="s2">&#34;DT00&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;station&#34;</span><span class="p">:</span> <span class="s2">&#34;GHCND:USC00173046&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;attributes&#34;</span><span class="p">:</span> <span class="s2">&#34;0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">9</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="err">...</span>
</span></span><span class="line"><span class="cl">        <span class="err">...</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h2 class="relative group">Thoughts
    <div id="thoughts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#thoughts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Emailing an API token, with no apparent way to reset it (maybe there is and I didn&rsquo;t see it), is a little like emailing a password. It wouldn&rsquo;t be my first choice. I wish they did like many other APIs and either let you generate it through the secure website itself, or make a separate API call to return the token.</p>
<p>Each of the endpoints has some examples you can try online without an API token, which is nice for verifying they work, but it&rsquo;s an incredibly limited tool since you can&rsquo;t modify them through the website itself.</p>
<p>I wish there were more detail about what the codes in the results mean. When it got to the point of returning data, there are a lot of codes and numbers that don&rsquo;t seem to be defined at all. Maybe this is only meant to be used by someone with a meteorological background?</p>
<p>I&rsquo;d love to use this in a project, maybe on the Raspberry Pi. I was thinking maybe query the station closest to my house and have a simple LED light up depending on the weather - green for rain, white for snow, blue for sleet, unlit if no precipitation. It seems like it&rsquo;d take more research though.</p>
]]></content:encoded><media:content url="https://grantwinney.com/what-is-noaa-api/feature.webp" medium="image" type="image/webp"/></item><item><title>Finding Directions, Coordinates and More With the Google Maps API</title><link>https://grantwinney.com/what-is-google-maps-api/</link><pubDate>Sat, 23 Dec 2017 04:10:01 +0000</pubDate><guid>https://grantwinney.com/what-is-google-maps-api/</guid><description>The Google Maps API is a series of APIs for multiple platforms, each focused on a small set of tasks. At first it all seems a bit overwhelming, but each of them is pretty easy to use. Let&amp;rsquo;s check a few out!</description><content:encoded><![CDATA[<p>Google is a service that&hellip; a provider who&hellip;. they run the Internet. They have this little mapping service too, so let&rsquo;s check out the <a href="https://developers.google.com/maps/"  target="_blank" rel="noreferrer">Google Maps API</a>.</p>
<p>First though, two things to consider:</p>
<ul>
<li>If you&rsquo;re unfamiliar with APIs, you might want to <a href="https://grantwinney.com/what-is-an-api/"  target="_blank" rel="noreferrer">read this first</a> to familiarize yourself.</li>
<li>Install <a href="https://www.getpostman.com/"  target="_blank" rel="noreferrer">Postman</a>, which allows you to access API endpoints without having to write an app, as well as save the calls you make and sync them online.</li>
</ul>
<hr>
<p>The Google Maps API isn&rsquo;t so much an API but a <a href="https://developers.google.com/maps/get-started/"  target="_blank" rel="noreferrer">series of APIs</a> - for Android, iOS, web, etc - and each is focused on a small set of tasks. At first this seemed a bit overwhelming, to see a large set of APIs, but each of them is easy to use. I&rsquo;ll pick a few out to play around with. <em>(does that count as three days then?)</em></p>

<h2 class="relative group">Geocoding API
    <div id="geocoding-api" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#geocoding-api" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The first one I thought looked interesting was the <a href="https://developers.google.com/maps/documentation/geocoding/start"  target="_blank" rel="noreferrer">Geocoding API</a>, which allows you to convert addresses to geolocation coordinates, and vice versa.</p>

<h3 class="relative group">Authorization
    <div id="authorization" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#authorization" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Oddly enough, you don&rsquo;t <em>need</em> an API key to experiment with these calls - they worked fine for me without it. I&rsquo;m not sure how they rate limit requests then, but you&rsquo;d still need one for a real product, so if you&rsquo;re up for it then <a href="https://developers.google.com/maps/documentation/geocoding/get-api-key"  target="_blank" rel="noreferrer">request an API key</a> before doing anything else. Google&rsquo;s docs are pretty straightforward so there&rsquo;s not much else to say, except that you can just select &ldquo;Create a new project&rdquo;.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="google-maps-geocoding-api&mdash;create-project-for-api-key"
    src="/what-is-google-maps-api/google-maps-geocoding-api---create-project-for-api-key.png"
    width="1526"
      height="630"></figure>
<p>Be sure to copy the key it produces, because I can&rsquo;t find where to view it after the modal dialog closes, other than clicking &ldquo;Get a key&rdquo; again. If you figure it out, I&rsquo;d like to know! As they state, this automatically activates the Google Maps Geocoding API, and generates an unrestricted key.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="google-api&mdash;get-api-key"
    src="/what-is-google-maps-api/google-api---get-api-key.png"
    width="1344"
      height="732"></figure>

<h3 class="relative group">Trying it out
    <div id="trying-it-out" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#trying-it-out" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Whether or not you generated a key, you should be able to try a couple things out.</p>

<h4 class="relative group">Getting coordinates from an address
    <div id="getting-coordinates-from-an-address" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#getting-coordinates-from-an-address" aria-label="Anchor">#</a>
    </span>
    
</h4>
<p>Try looking up an address to get information about it, including its coordinates.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET https://maps.googleapis.com/maps/api/geocode/json?address=50 Public Square Cleveland, Ohio</span></span></code></pre></div></div>
<p>I decided to lookup the Terminal Tower, a landmark in Cleveland. What I got back included:</p>
<ul>
<li>the parsed address, with information I didn&rsquo;t even provide like county and zip</li>
<li>a nicely, fully formatted string with the address</li>
<li>the geolocation coordinates</li>
</ul>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;results&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;address_components&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;long_name&#34;</span><span class="p">:</span> <span class="s2">&#34;50&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;short_name&#34;</span><span class="p">:</span> <span class="s2">&#34;50&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;street_number&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">]</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;long_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Public Square&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;short_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Public Square&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;route&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">]</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;long_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Downtown&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;short_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Downtown&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;neighborhood&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;political&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">]</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;long_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Cleveland&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;short_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Cleveland&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;locality&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;political&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">]</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;long_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Cuyahoga County&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;short_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Cuyahoga County&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;administrative_area_level_2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;political&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">]</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;long_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Ohio&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;short_name&#34;</span><span class="p">:</span> <span class="s2">&#34;OH&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;administrative_area_level_1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;political&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">]</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;long_name&#34;</span><span class="p">:</span> <span class="s2">&#34;United States&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;short_name&#34;</span><span class="p">:</span> <span class="s2">&#34;US&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;country&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;political&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">]</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;long_name&#34;</span><span class="p">:</span> <span class="s2">&#34;44113&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;short_name&#34;</span><span class="p">:</span> <span class="s2">&#34;44113&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;postal_code&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">]</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;formatted_address&#34;</span><span class="p">:</span> <span class="s2">&#34;50 Public Square, Cleveland, OH 44113, USA&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;geometry&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4984174</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.69372869999999</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;location_type&#34;</span><span class="p">:</span> <span class="s2">&#34;ROOFTOP&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;viewport&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;northeast&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4997663802915</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.6923797197085</span>
</span></span><span class="line"><span class="cl">                    <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;southwest&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4970684197085</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.6950776802915</span>
</span></span><span class="line"><span class="cl">                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;place_id&#34;</span><span class="p">:</span> <span class="s2">&#34;ChIJI9jXsn_wMIgRRyf46mR97eY&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;street_address&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;OK&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h4 class="relative group">Getting an address from coordinates
    <div id="getting-an-address-from-coordinates" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#getting-an-address-from-coordinates" aria-label="Anchor">#</a>
    </span>
    
</h4>
<p>How about trying the reverse, and getting the address again from the coordinates?</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET https://maps.googleapis.com/maps/api/geocode/json?latlng=41.4984174,-81.69372869999999</span></span></code></pre></div></div>
<p>I was suprised to find the results (a portion of which is shown below) included far more than I had expected (such as a bus station), only because the coordinates seem <em>so</em> specific.</p>
<p>I searched for my home address and had similar results with the reverse search - the lat/long coords returned my house (cool), an address range on the street that intersects mine (we&rsquo;re a corner lot, so okay), and a few addresses at the county level and the closest major city (weird and vague). It&rsquo;s possible the list that gets returned is most to least specific, but why there are multiple hits at all I don&rsquo;t really get.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;results&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;address_components&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;long_name&#34;</span><span class="p">:</span> <span class="s2">&#34;50&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;short_name&#34;</span><span class="p">:</span> <span class="s2">&#34;50&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;street_number&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">]</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;long_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Public Square&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;short_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Public Square&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;route&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">]</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;long_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Downtown&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;short_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Downtown&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;neighborhood&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;political&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">]</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;long_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Cleveland&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;short_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Cleveland&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;locality&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;political&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">]</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;long_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Cuyahoga County&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;short_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Cuyahoga County&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;administrative_area_level_2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;political&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">]</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;long_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Ohio&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;short_name&#34;</span><span class="p">:</span> <span class="s2">&#34;OH&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;administrative_area_level_1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;political&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">]</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;long_name&#34;</span><span class="p">:</span> <span class="s2">&#34;United States&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;short_name&#34;</span><span class="p">:</span> <span class="s2">&#34;US&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;country&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;political&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">]</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;long_name&#34;</span><span class="p">:</span> <span class="s2">&#34;44113&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;short_name&#34;</span><span class="p">:</span> <span class="s2">&#34;44113&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;postal_code&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">]</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;formatted_address&#34;</span><span class="p">:</span> <span class="s2">&#34;50 Public Square, Cleveland, OH 44113, USA&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;geometry&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4984174</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.69372869999999</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;location_type&#34;</span><span class="p">:</span> <span class="s2">&#34;ROOFTOP&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;viewport&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;northeast&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4997663802915</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.6923797197085</span>
</span></span><span class="line"><span class="cl">                    <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;southwest&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4970684197085</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.6950776802915</span>
</span></span><span class="line"><span class="cl">                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;place_id&#34;</span><span class="p">:</span> <span class="s2">&#34;ChIJI9jXsn_wMIgRRyf46mR97eY&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;street_address&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;address_components&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;long_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Euclid Av &amp; Ontario St Station&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;short_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Euclid Av &amp; Ontario St Station&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;bus_station&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;establishment&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;point_of_interest&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;transit_station&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">]</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;long_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Downtown&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;short_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Downtown&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;neighborhood&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;political&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">]</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;long_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Cleveland&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;short_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Cleveland&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;locality&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;political&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">]</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;long_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Cuyahoga County&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;short_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Cuyahoga County&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;administrative_area_level_2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;political&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">]</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;long_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Ohio&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;short_name&#34;</span><span class="p">:</span> <span class="s2">&#34;OH&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;administrative_area_level_1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;political&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">]</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;long_name&#34;</span><span class="p">:</span> <span class="s2">&#34;United States&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;short_name&#34;</span><span class="p">:</span> <span class="s2">&#34;US&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;country&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;political&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">]</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;long_name&#34;</span><span class="p">:</span> <span class="s2">&#34;44113&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;short_name&#34;</span><span class="p">:</span> <span class="s2">&#34;44113&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;postal_code&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">]</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;formatted_address&#34;</span><span class="p">:</span> <span class="s2">&#34;Euclid Av &amp; Ontario St Station, Cleveland, OH 44113, USA&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;geometry&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.498881</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.69354199999999</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;location_type&#34;</span><span class="p">:</span> <span class="s2">&#34;GEOMETRIC_CENTER&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;viewport&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;northeast&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.50022998029149</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.69219301970848</span>
</span></span><span class="line"><span class="cl">                    <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;southwest&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4975320197085</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.6948909802915</span>
</span></span><span class="line"><span class="cl">                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;place_id&#34;</span><span class="p">:</span> <span class="s2">&#34;ChIJk7Gntn_wMIgR67oq_t6hmaM&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;bus_station&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;establishment&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;point_of_interest&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;transit_station&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="err">...</span>
</span></span><span class="line"><span class="cl">        <span class="err">...</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;OK&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h2 class="relative group">Directions API
    <div id="directions-api" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#directions-api" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>That last API was straight-forward. Let&rsquo;s try looking up some directions with the <a href="https://developers.google.com/maps/documentation/directions/intro"  target="_blank" rel="noreferrer">Directions API</a> next. You don&rsquo;t need an API token to test this one either, although certain parameters seem to require it. <a href="https://developers.google.com/maps/documentation/directions/get-api-key"  target="_blank" rel="noreferrer">Create a new key</a> if you want, and append it with <code>&amp;key=&lt;your-api-key&gt;</code>.</p>
<p>Pick an origin and destination. I chose the Terminal Tower (again) as the origin, and a Starbucks around the corner from it as the destination (I know, there&rsquo;s probably 2 Starbucks <em>inside</em> it), so the results wouldn&rsquo;t be too large. Note the section in the results called &ldquo;steps&rdquo;, each of which have a start and end, total distance and duration, and even a one-line instruction in English.</p>
<p>There are lots of parameters for specifying how the directions should be calculated. In the following request, I&rsquo;ve chosen &ldquo;walking&rdquo; as the travel mode, told it to return alternative routes, and set the language to French.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET https://maps.googleapis.com/maps/api/directions/json?origin=50+Public+Square+Cleveland,+Ohio&amp;destination=200+Public+Square+130,+Cleveland,+OH+44114&amp;language=fr&amp;mode=walking&amp;alternatives=true</span></span></code></pre></div></div>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;geocoded_waypoints&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;geocoder_status&#34;</span><span class="p">:</span> <span class="s2">&#34;OK&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;place_id&#34;</span><span class="p">:</span> <span class="s2">&#34;ChIJI9jXsn_wMIgRRyf46mR97eY&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;street_address&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;geocoder_status&#34;</span><span class="p">:</span> <span class="s2">&#34;OK&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;partial_match&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;place_id&#34;</span><span class="p">:</span> <span class="s2">&#34;ChIJL83I43_wMIgRdZlcIZMKIJ4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;premise&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;routes&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;bounds&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;northeast&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4998051</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.6923484</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;southwest&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4986974</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.6937592</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;copyrights&#34;</span><span class="p">:</span> <span class="s2">&#34;Données cartographiques ©2017 Google&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;legs&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;distance&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;0,1 miles&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">192</span>
</span></span><span class="line"><span class="cl">                    <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;duration&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;3 minutes&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">167</span>
</span></span><span class="line"><span class="cl">                    <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;end_address&#34;</span><span class="p">:</span> <span class="s2">&#34;200 Public Square, 200 Public Square, Cleveland, OH 44114, États-Unis&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;end_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4998051</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.69261209999999</span>
</span></span><span class="line"><span class="cl">                    <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;start_address&#34;</span><span class="p">:</span> <span class="s2">&#34;50 Public Square, Cleveland, OH 44113, États-Unis&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;start_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4986974</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.6937592</span>
</span></span><span class="line"><span class="cl">                    <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;steps&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                        <span class="p">{</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;distance&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;0,1 miles&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">192</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;duration&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;3 minutes&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">167</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;end_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4998051</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.69261209999999</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;html_instructions&#34;</span><span class="p">:</span> <span class="s2">&#34;Prendre la direction &lt;b&gt;nord-est&lt;/b&gt; sur &lt;b&gt;S Roadway&lt;/b&gt; vers &lt;b&gt;Ontario St&lt;/b&gt;&lt;div style=\&#34;font-size:0.9em\&#34;&gt;Votre destination se trouvera sur la droite.&lt;/div&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;polyline&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;points&#34;</span><span class="p">:</span> <span class="s2">&#34;{eh|F~xrqN?AAEAGAEc@eASg@Se@c@gAQa@AACACAAAC?C?C@C@CBk@f@ID&#34;</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;start_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4986974</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.6937592</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;travel_mode&#34;</span><span class="p">:</span> <span class="s2">&#34;WALKING&#34;</span>
</span></span><span class="line"><span class="cl">                        <span class="p">}</span>
</span></span><span class="line"><span class="cl">                    <span class="p">],</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;traffic_speed_entry&#34;</span><span class="p">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;via_waypoint&#34;</span><span class="p">:</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;overview_polyline&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;points&#34;</span><span class="p">:</span> <span class="s2">&#34;{eh|F~xrqNEUoB{ESc@GCM?}@r@&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;summary&#34;</span><span class="p">:</span> <span class="s2">&#34;S Roadway&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;warnings&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Le calcul d&#39;itinéraires piétons est en bêta. Faites attention – Cet itinéraire n&#39;est peut-être pas complètement aménagé pour les piétons.&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;waypoint_order&#34;</span><span class="p">:</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;bounds&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;northeast&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4998051</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.6923484</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;southwest&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4986913</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.69384509999999</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;copyrights&#34;</span><span class="p">:</span> <span class="s2">&#34;Données cartographiques ©2017 Google&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;legs&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;distance&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;0,1 miles&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">221</span>
</span></span><span class="line"><span class="cl">                    <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;duration&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;3 minutes&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">177</span>
</span></span><span class="line"><span class="cl">                    <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;end_address&#34;</span><span class="p">:</span> <span class="s2">&#34;200 Public Square, 200 Public Square, Cleveland, OH 44114, États-Unis&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;end_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4998051</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.69261209999999</span>
</span></span><span class="line"><span class="cl">                    <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;start_address&#34;</span><span class="p">:</span> <span class="s2">&#34;50 Public Square, Cleveland, OH 44113, États-Unis&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;start_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4986974</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.6937592</span>
</span></span><span class="line"><span class="cl">                    <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;steps&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                        <span class="p">{</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;distance&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;23 pieds&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">7</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;duration&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;1 minute&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">5</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;end_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.49869229999999</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.69384509999999</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;html_instructions&#34;</span><span class="p">:</span> <span class="s2">&#34;Prendre la direction &lt;b&gt;ouest&lt;/b&gt; sur &lt;b&gt;S Roadway&lt;/b&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;polyline&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;points&#34;</span><span class="p">:</span> <span class="s2">&#34;{eh|F~xrqN@B?D?F&#34;</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;start_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4986974</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.6937592</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;travel_mode&#34;</span><span class="p">:</span> <span class="s2">&#34;WALKING&#34;</span>
</span></span><span class="line"><span class="cl">                        <span class="p">},</span>
</span></span><span class="line"><span class="cl">                        <span class="p">{</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;distance&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;46 pieds&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">14</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;duration&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;1 minute&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">20</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;end_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4988192</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.69381389999999</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;html_instructions&#34;</span><span class="p">:</span> <span class="s2">&#34;Tourner à &lt;b&gt;droite&lt;/b&gt; vers &lt;b&gt;E Roadway&lt;/b&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;maneuver&#34;</span><span class="p">:</span> <span class="s2">&#34;turn-right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;polyline&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;points&#34;</span><span class="p">:</span> <span class="s2">&#34;yeh|FpyrqNYG&#34;</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;start_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.49869229999999</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.69384509999999</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;travel_mode&#34;</span><span class="p">:</span> <span class="s2">&#34;WALKING&#34;</span>
</span></span><span class="line"><span class="cl">                        <span class="p">},</span>
</span></span><span class="line"><span class="cl">                        <span class="p">{</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;distance&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;430 pieds&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">131</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;duration&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;2 minutes&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">93</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;end_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4994528</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.6925217</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;html_instructions&#34;</span><span class="p">:</span> <span class="s2">&#34;Tourner à &lt;b&gt;droite&lt;/b&gt; vers &lt;b&gt;E Roadway&lt;/b&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;maneuver&#34;</span><span class="p">:</span> <span class="s2">&#34;turn-right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;polyline&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;points&#34;</span><span class="p">:</span> <span class="s2">&#34;sfh|FhyrqN@CAEAIEM[m@MUGQKWKYSq@EMEICGECAA&#34;</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;start_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4988192</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.69381389999999</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;travel_mode&#34;</span><span class="p">:</span> <span class="s2">&#34;WALKING&#34;</span>
</span></span><span class="line"><span class="cl">                        <span class="p">},</span>
</span></span><span class="line"><span class="cl">                        <span class="p">{</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;distance&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;46 pieds&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">14</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;duration&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;1 minute&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">11</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;end_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4993763</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.6923833</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;html_instructions&#34;</span><span class="p">:</span> <span class="s2">&#34;Tourner à &lt;b&gt;droite&lt;/b&gt; vers &lt;b&gt;E Roadway&lt;/b&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;maneuver&#34;</span><span class="p">:</span> <span class="s2">&#34;turn-right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;polyline&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;points&#34;</span><span class="p">:</span> <span class="s2">&#34;qjh|FfqrqN@EJU&#34;</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;start_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4994528</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.6925217</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;travel_mode&#34;</span><span class="p">:</span> <span class="s2">&#34;WALKING&#34;</span>
</span></span><span class="line"><span class="cl">                        <span class="p">},</span>
</span></span><span class="line"><span class="cl">                        <span class="p">{</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;distance&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;180 pieds&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">55</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;duration&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;1 minute&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">48</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;end_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4998051</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.69261209999999</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;html_instructions&#34;</span><span class="p">:</span> <span class="s2">&#34;Prendre &lt;b&gt;à gauche&lt;/b&gt; sur &lt;b&gt;E Roadway&lt;/b&gt;&lt;div style=\&#34;font-size:0.9em\&#34;&gt;Votre destination se trouvera sur la droite.&lt;/div&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;maneuver&#34;</span><span class="p">:</span> <span class="s2">&#34;turn-left&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;polyline&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;points&#34;</span><span class="p">:</span> <span class="s2">&#34;cjh|FjprqNAACACAAAC?C?C@C@CBk@f@ID&#34;</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;start_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4993763</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.6923833</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;travel_mode&#34;</span><span class="p">:</span> <span class="s2">&#34;WALKING&#34;</span>
</span></span><span class="line"><span class="cl">                        <span class="p">}</span>
</span></span><span class="line"><span class="cl">                    <span class="p">],</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;traffic_speed_entry&#34;</span><span class="p">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;via_waypoint&#34;</span><span class="p">:</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;overview_polyline&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;points&#34;</span><span class="p">:</span> <span class="s2">&#34;{eh|F~xrqN@H?FYG?IGWi@cASi@k@cBIK?GJUAAGCM?}@r@&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;summary&#34;</span><span class="p">:</span> <span class="s2">&#34;E Roadway&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;warnings&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Le calcul d&#39;itinéraires piétons est en bêta. Faites attention – Cet itinéraire n&#39;est peut-être pas complètement aménagé pour les piétons.&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;waypoint_order&#34;</span><span class="p">:</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;bounds&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;northeast&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.50007040000001</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.69261209999999</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;southwest&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4986913</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.69389630000001</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;copyrights&#34;</span><span class="p">:</span> <span class="s2">&#34;Données cartographiques ©2017 Google&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;legs&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;distance&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;0,2 miles&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">244</span>
</span></span><span class="line"><span class="cl">                    <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;duration&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;3 minutes&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">193</span>
</span></span><span class="line"><span class="cl">                    <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;end_address&#34;</span><span class="p">:</span> <span class="s2">&#34;200 Public Square, 200 Public Square, Cleveland, OH 44114, États-Unis&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;end_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4998051</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.69261209999999</span>
</span></span><span class="line"><span class="cl">                    <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;start_address&#34;</span><span class="p">:</span> <span class="s2">&#34;50 Public Square, Cleveland, OH 44113, États-Unis&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;start_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4986974</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.6937592</span>
</span></span><span class="line"><span class="cl">                    <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;steps&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                        <span class="p">{</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;distance&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;23 pieds&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">7</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;duration&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;1 minute&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">5</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;end_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.49869229999999</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.69384509999999</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;html_instructions&#34;</span><span class="p">:</span> <span class="s2">&#34;Prendre la direction &lt;b&gt;ouest&lt;/b&gt; sur &lt;b&gt;S Roadway&lt;/b&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;polyline&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;points&#34;</span><span class="p">:</span> <span class="s2">&#34;{eh|F~xrqN@B?D?F&#34;</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;start_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4986974</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.6937592</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;travel_mode&#34;</span><span class="p">:</span> <span class="s2">&#34;WALKING&#34;</span>
</span></span><span class="line"><span class="cl">                        <span class="p">},</span>
</span></span><span class="line"><span class="cl">                        <span class="p">{</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;distance&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;72 pieds&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">22</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;duration&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;1 minute&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">25</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;end_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4988885</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.6937969</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;html_instructions&#34;</span><span class="p">:</span> <span class="s2">&#34;Tourner à &lt;b&gt;droite&lt;/b&gt; vers &lt;b&gt;E Roadway&lt;/b&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;maneuver&#34;</span><span class="p">:</span> <span class="s2">&#34;turn-right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;polyline&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;points&#34;</span><span class="p">:</span> <span class="s2">&#34;yeh|FpyrqNYGMA&#34;</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;start_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.49869229999999</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.69384509999999</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;travel_mode&#34;</span><span class="p">:</span> <span class="s2">&#34;WALKING&#34;</span>
</span></span><span class="line"><span class="cl">                        <span class="p">},</span>
</span></span><span class="line"><span class="cl">                        <span class="p">{</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;distance&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;259 pieds&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">79</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;duration&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;1 minute&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">54</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;end_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4995628</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.6938514</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;html_instructions&#34;</span><span class="p">:</span> <span class="s2">&#34;Tourner à &lt;b&gt;gauche&lt;/b&gt; vers &lt;b&gt;E Roadway&lt;/b&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;maneuver&#34;</span><span class="p">:</span> <span class="s2">&#34;turn-left&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;polyline&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;points&#34;</span><span class="p">:</span> <span class="s2">&#34;agh|FfyrqNCFEDGBK@KAy@GKAEAE?C?A@A?A@&#34;</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;start_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4988885</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.6937969</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;travel_mode&#34;</span><span class="p">:</span> <span class="s2">&#34;WALKING&#34;</span>
</span></span><span class="line"><span class="cl">                        <span class="p">},</span>
</span></span><span class="line"><span class="cl">                        <span class="p">{</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;distance&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;328 pieds&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">100</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;duration&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;1 minute&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">70</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;end_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.50007040000001</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.6928607</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;html_instructions&#34;</span><span class="p">:</span> <span class="s2">&#34;Tourner à &lt;b&gt;droite&lt;/b&gt; vers &lt;b&gt;E Roadway&lt;/b&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;maneuver&#34;</span><span class="p">:</span> <span class="s2">&#34;turn-right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;polyline&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;points&#34;</span><span class="p">:</span> <span class="s2">&#34;gkh|FpyrqNc@eAw@kBIS&#34;</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;start_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4995628</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.6938514</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;travel_mode&#34;</span><span class="p">:</span> <span class="s2">&#34;WALKING&#34;</span>
</span></span><span class="line"><span class="cl">                        <span class="p">},</span>
</span></span><span class="line"><span class="cl">                        <span class="p">{</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;distance&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;118 pieds&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">36</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;duration&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;1 minute&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">39</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;end_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.4998051</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.69261209999999</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;html_instructions&#34;</span><span class="p">:</span> <span class="s2">&#34;Prendre &lt;b&gt;à droite&lt;/b&gt; sur &lt;b&gt;E Roadway&lt;/b&gt;&lt;div style=\&#34;font-size:0.9em\&#34;&gt;Votre destination se trouvera sur la gauche.&lt;/div&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;maneuver&#34;</span><span class="p">:</span> <span class="s2">&#34;turn-right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;polyline&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;points&#34;</span><span class="p">:</span> <span class="s2">&#34;mnh|FjsrqN@A^]PQ&#34;</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;start_location&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lat&#34;</span><span class="p">:</span> <span class="mf">41.50007040000001</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="nt">&#34;lng&#34;</span><span class="p">:</span> <span class="mf">-81.6928607</span>
</span></span><span class="line"><span class="cl">                            <span class="p">},</span>
</span></span><span class="line"><span class="cl">                            <span class="nt">&#34;travel_mode&#34;</span><span class="p">:</span> <span class="s2">&#34;WALKING&#34;</span>
</span></span><span class="line"><span class="cl">                        <span class="p">}</span>
</span></span><span class="line"><span class="cl">                    <span class="p">],</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;traffic_speed_entry&#34;</span><span class="p">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;via_waypoint&#34;</span><span class="p">:</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;overview_polyline&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;points&#34;</span><span class="p">:</span> <span class="s2">&#34;{eh|F~xrqN@H?FYGMAILSDeAI[CC@e@cAaA_C`@_@PQ&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;summary&#34;</span><span class="p">:</span> <span class="s2">&#34;S Roadway et E Roadway&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;warnings&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;Le calcul d&#39;itinéraires piétons est en bêta. Faites attention – Cet itinéraire n&#39;est peut-être pas complètement aménagé pour les piétons.&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;waypoint_order&#34;</span><span class="p">:</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;OK&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h2 class="relative group">Time Zone API
    <div id="time-zone-api" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#time-zone-api" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>We&rsquo;ll try one more. The Time Zone API presents time zone information for a given set of coordinates. Again, the key doesn&rsquo;t seem to be required for testing, but you can <a href="https://developers.google.com/maps/documentation/timezone/start#get-a-key"  target="_blank" rel="noreferrer">get a key</a> anyway if you&rsquo;d like. FWIW, when I tried to re-use a <em>previous</em> auth key, it didn&rsquo;t like that - I got an error:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;errorMessage&#34;</span><span class="p">:</span> <span class="s2">&#34;This API project is not authorized to use this API. Please ensure this API is activated in the Google Developers Console: https://console.developers.google.com/apis/api/timezone_backend?project=_&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;REQUEST_DENIED&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>So for now, just omit the API key. To calculate the required timestamp parameter, which represents the number of seconds since the epoch, use the <a href="https://www.epochconverter.com/"  target="_blank" rel="noreferrer">Epoch Converter</a>&hellip; assuming you can&rsquo;t just figure it out in your head. ;p Just copy the value where it says &ldquo;The current Unix epoch time is &hellip;&rdquo;</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET https://maps.googleapis.com/maps/api/timezone/json?location=41.4984174,-81.69372869999999&amp;timestamp=1513992742</span></span></code></pre></div></div>
<p>The results include the Time Zone ID and Name for the location you specified. Since I used a location in Cleveland OH, and daylight savings time ended in November, I get EST back.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;dstOffset&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;rawOffset&#34;</span><span class="p">:</span> <span class="mi">-18000</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;OK&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;timeZoneId&#34;</span><span class="p">:</span> <span class="s2">&#34;America/New_York&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;timeZoneName&#34;</span><span class="p">:</span> <span class="s2">&#34;Eastern Standard Time&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h2 class="relative group">Thoughts
    <div id="thoughts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#thoughts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I&rsquo;m not sure why these calls didn&rsquo;t require an API key, since I&rsquo;m not sure how they can enforce limits.</p>
<p>Speaking of limits, the limits for a free API key are very generous, typically thousands of requests per day for each API - certainly enough for personal use or even something small for a team at work - so that&rsquo;s nice.</p>
<p>Even though there are a lot of APIs, they seem to be very focused in purpose. It&rsquo;s interesting though, since the other APIs I&rsquo;ve looked at are usually few (or one) with lots of endpoints that do different things. By breaking things up, it&rsquo;s probably easier to control access to one thing or another.</p>
<p>I&rsquo;ll definitely be looking for opportunites to try these out on some other projects - maybe on the Raspberry Pi sometime. :)</p>
]]></content:encoded><media:content url="https://grantwinney.com/what-is-google-maps-api/feature.webp" medium="image" type="image/webp"/></item><item><title>Managing Workspaces and Channels With the Slack API</title><link>https://grantwinney.com/what-is-slack-api/</link><pubDate>Thu, 21 Dec 2017 18:09:09 +0000</pubDate><guid>https://grantwinney.com/what-is-slack-api/</guid><description>Slack is a popular communication and collaboration tool, and their API gives us access to channels, messages, and more. Let&amp;rsquo;s check it out!</description><content:encoded><![CDATA[<p>Slack is a popular communication and collaboration tool, and the <a href="https://api.slack.com/"  target="_blank" rel="noreferrer">Slack API</a> gives us access to workspaces, channels, messages, and more.</p>
<p>First though, two things to consider:</p>
<ul>
<li>If you&rsquo;re unfamiliar with APIs, <a href="https://grantwinney.com/what-is-an-api/"  target="_blank" rel="noreferrer">read this first</a> to familiarize yourself with the concept.</li>
<li>Install <a href="https://www.getpostman.com/"  target="_blank" rel="noreferrer">Postman</a>, which allows you to access API endpoints without having to write an app, as well as save the calls you make and sync them online.</li>
</ul>
<hr>

<h2 class="relative group">Getting Started
    <div id="getting-started" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#getting-started" aria-label="Anchor">#</a>
    </span>
    
</h2>

<h3 class="relative group">Create an account or sign in
    <div id="create-an-account-or-sign-in" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#create-an-account-or-sign-in" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>First, you&rsquo;ll need to be signed in. <a href="https://slack.com/create"  target="_blank" rel="noreferrer">Create an account</a> if you don&rsquo;t already have one. They&rsquo;ll ask for a few pieces of information. Well, we&rsquo;re just playing around so&hellip;</p>
<ul>
<li>About your &ldquo;team&rdquo; - <em>select &ldquo;other&rdquo; and &ldquo;1-10 people&rdquo;</em></li>
<li>The name of your group - <em>use &ldquo;Sandbox&rdquo; or whatever you&rsquo;d like</em></li>
<li>The workspace url - <em>unique among everyone, so use your full name or a random phrase</em></li>
<li>Send invitations - <em>skip it</em></li>
</ul>

<h3 class="relative group">Create an app
    <div id="create-an-app" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#create-an-app" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Then, you&rsquo;ll need to <a href="https://api.slack.com/apps/new"  target="_blank" rel="noreferrer">create an app</a>. Give it whatever name you like, and choose the name of your group/workspace from the dropdown. Click &ldquo;Create App&rdquo;. If all goes well, you should end up on a dashboard like this one:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="slack-api&mdash;app-dashboard"
    src="/what-is-slack-api/slack-api---app-dashboard.png"
    width="807"
      height="497"></figure>

<h3 class="relative group">Choose some permissions
    <div id="choose-some-permissions" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#choose-some-permissions" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Now that you&rsquo;ve got an app, you need to decide what you want it to do. I&rsquo;m glad they have apps scoped like this - it&rsquo;s very similar to <a href="https://grantwinney.com/making-your-first-chrome-extension/"  target="_blank" rel="noreferrer">creating browser extensions</a> and phone apps, where not everything has access to everything, and the user must explicitly &ldquo;grant&rdquo; permissions.</p>
<p>Click the &ldquo;OAuth &amp; Permissions&rdquo; link on the left, and then scroll down to &ldquo;Scopes&rdquo;. Choose a few permissions that sound interesting and then &ldquo;Save Changes&rdquo;. I chose to access channel information and alter pinned messages. <em>(The incoming-webhook one got added, and can&rsquo;t be removed - guess it&rsquo;s required by the other two?)</em></p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="slack-api&mdash;request-permissions"
    src="/what-is-slack-api/slack-api---request-permissions.png"
    width="608"
      height="648"></figure>

<h3 class="relative group">Install your new app in your workspace
    <div id="install-your-new-app-in-your-workspace" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#install-your-new-app-in-your-workspace" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>On the same page, scroll back to the top and press the &ldquo;Install App to Workspace&rdquo; button. You&rsquo;ll be notified about what permissions the app needs - just click &ldquo;Authorize&rdquo;. I also created a new channel named &ldquo;testing&rdquo;, so that I could select it here, but you can just select a built-in channel from the dropdown if you&rsquo;d like.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="slack-api&mdash;authorize-app"
    src="/what-is-slack-api/slack-api---authorize-app.png"
    width="412"
      height="452"></figure>

<h3 class="relative group">Auth Token (finally!)
    <div id="auth-token-finally" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#auth-token-finally" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>It should&rsquo;ve redirected back to the &ldquo;OAuth &amp; Permissions&rdquo; section. This is the auth token you&rsquo;ll need when you make requests to the Slack API.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="slack-api&mdash;auth-token"
    src="/what-is-slack-api/slack-api---auth-token.png"
    width="603"
      height="268"></figure>

<h2 class="relative group">Try it out
    <div id="try-it-out" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#try-it-out" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Now you can actually try some <a href="https://api.slack.com/methods"  target="_blank" rel="noreferrer">API methods</a>. For most of these, I think you need at least the <code>Authorization</code> and <code>Content-Type</code> headers - although the latter isn&rsquo;t <em>always</em> required, it won&rsquo;t hurt to include it.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="slack-api&mdash;post-headers"
    src="/what-is-slack-api/slack-api---post-headers.png"
    width="688"
      height="123"></figure>

<h3 class="relative group">List Channels
    <div id="list-channels" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#list-channels" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The first thing we&rsquo;ll try is <a href="https://api.slack.com/methods/channels.list"  target="_blank" rel="noreferrer">listing channels</a>. In Postman, do a <code>POST</code> and include the headers above. The JSON body only needs to have your auth token.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="slack-api&mdash;list-channels-request"
    src="/what-is-slack-api/slack-api---list-channels-request.png"
    width="682"
      height="180"></figure>
<p>You should get a result similar to this. My results include three channels - the default &ldquo;general&rdquo; and &ldquo;random&rdquo; ones, and also my &ldquo;testing&rdquo; one. Note how each channel has an &ldquo;id&rdquo; too. It appears that many (most? all?) of the requests that operate on channels require an id, <em>not a name.</em></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;ok&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;channels&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;C8HLHBQLS&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;general&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;is_channel&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;created&#34;</span><span class="p">:</span> <span class="mi">1513804198</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;is_archived&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;is_general&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;unlinked&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;creator&#34;</span><span class="p">:</span> <span class="s2">&#34;U8HQN49LM&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name_normalized&#34;</span><span class="p">:</span> <span class="s2">&#34;general&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;is_shared&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;is_org_shared&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;is_member&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;is_private&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;is_mpim&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;members&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;U8HQN49LM&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;topic&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="s2">&#34;Company-wide announcements and work-based matters&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;creator&#34;</span><span class="p">:</span> <span class="s2">&#34;U8HQN49LM&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;last_set&#34;</span><span class="p">:</span> <span class="mi">1513804198</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;purpose&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="s2">&#34;This channel is for workspace-wide communication and announcements. All members are in this channel.&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;creator&#34;</span><span class="p">:</span> <span class="s2">&#34;U8HQN49LM&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;last_set&#34;</span><span class="p">:</span> <span class="mi">1513804198</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;previous_names&#34;</span><span class="p">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;num_members&#34;</span><span class="p">:</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;C8HLHBR3L&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;random&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;is_channel&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;created&#34;</span><span class="p">:</span> <span class="mi">1513804198</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;is_archived&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;is_general&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;unlinked&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;creator&#34;</span><span class="p">:</span> <span class="s2">&#34;U8HQN49LM&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name_normalized&#34;</span><span class="p">:</span> <span class="s2">&#34;random&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;is_shared&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;is_org_shared&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;is_member&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;is_private&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;is_mpim&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;members&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;U8HQN49LM&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;topic&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="s2">&#34;Non-work banter and water cooler conversation&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;creator&#34;</span><span class="p">:</span> <span class="s2">&#34;U8HQN49LM&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;last_set&#34;</span><span class="p">:</span> <span class="mi">1513804198</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;purpose&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="s2">&#34;A place for non-work-related flimflam, faffing, hodge-podge or jibber-jabber you&#39;d prefer to keep out of more focused work-related channels.&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;creator&#34;</span><span class="p">:</span> <span class="s2">&#34;U8HQN49LM&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;last_set&#34;</span><span class="p">:</span> <span class="mi">1513804198</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;previous_names&#34;</span><span class="p">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;num_members&#34;</span><span class="p">:</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;C8HN58XQU&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;testing&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;is_channel&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;created&#34;</span><span class="p">:</span> <span class="mi">1513806864</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;is_archived&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;is_general&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;unlinked&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;creator&#34;</span><span class="p">:</span> <span class="s2">&#34;U8HQN49LM&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name_normalized&#34;</span><span class="p">:</span> <span class="s2">&#34;testing&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;is_shared&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;is_org_shared&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;is_member&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;is_private&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;is_mpim&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;members&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;U8HQN49LM&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;topic&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;creator&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;last_set&#34;</span><span class="p">:</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;purpose&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;creator&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;last_set&#34;</span><span class="p">:</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;previous_names&#34;</span><span class="p">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;num_members&#34;</span><span class="p">:</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h3 class="relative group">List Messages
    <div id="list-messages" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#list-messages" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Now that you&rsquo;ve got your channels, switch over to Slack and type a message into the channel you&rsquo;re interested in. Anything is fine - I typed <em>&ldquo;Just a test message!&rdquo;</em> into my &ldquo;testing&rdquo; channel.</p>
<p>Try retrieving the messages from your channel, including the message you just added. Most of the API calls can be made in two ways - either with a JSON body like I did above, or as query string parameters, which seems to be what we <em>have</em> to use with this one, as the JSON body doesn&rsquo;t work. Oddly, either a <code>POST</code> or <code>GET</code> works&hellip; a <code>GET</code> seems more intuitive for requesting information, so that&rsquo;s what I used.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GET https://slack.com/api/channels.history?token=&lt;your-auth-token&gt;&amp;channel=C8HN58XQU</span></span></code></pre></div></div>
<p>Here&rsquo;s the result I got back. See my test message? See the timestamp on it? If you&rsquo;re following along, grab the timestamp for your message too - you&rsquo;ll need it.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;ok&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;added an integration to this channel: &lt;https://grantwinney.slack.com/services/B8J1BF0AE|My First Slack App&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;bot_id&#34;</span><span class="p">:</span> <span class="s2">&#34;B8J1BF0AE&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;bot_link&#34;</span><span class="p">:</span> <span class="s2">&#34;&lt;https://grantwinney.slack.com/services/B8J1BF0AE|My First Slack App&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;message&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;subtype&#34;</span><span class="p">:</span> <span class="s2">&#34;bot_add&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;user&#34;</span><span class="p">:</span> <span class="s2">&#34;U8HQN49LM&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;ts&#34;</span><span class="p">:</span> <span class="s2">&#34;1513868109.000605&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;message&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;user&#34;</span><span class="p">:</span> <span class="s2">&#34;U8HQN49LM&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;Just a test message!&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;ts&#34;</span><span class="p">:</span> <span class="s2">&#34;1513859401.000295&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;added an integration to this channel: &lt;https://grantwinney.slack.com/services/B8K17AGEB|My First Slack App&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;bot_id&#34;</span><span class="p">:</span> <span class="s2">&#34;B8K17AGEB&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;bot_link&#34;</span><span class="p">:</span> <span class="s2">&#34;&lt;https://grantwinney.slack.com/services/B8K17AGEB|My First Slack App&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;message&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;subtype&#34;</span><span class="p">:</span> <span class="s2">&#34;bot_add&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;user&#34;</span><span class="p">:</span> <span class="s2">&#34;U8HQN49LM&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;ts&#34;</span><span class="p">:</span> <span class="s2">&#34;1513859275.000243&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;added an integration to this channel: &lt;https://grantwinney.slack.com/services/B8HN70LHJ|My First Slack App&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;bot_id&#34;</span><span class="p">:</span> <span class="s2">&#34;B8HN70LHJ&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;bot_link&#34;</span><span class="p">:</span> <span class="s2">&#34;&lt;https://grantwinney.slack.com/services/B8HN70LHJ|My First Slack App&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;message&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;subtype&#34;</span><span class="p">:</span> <span class="s2">&#34;bot_add&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;user&#34;</span><span class="p">:</span> <span class="s2">&#34;U8HQN49LM&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;ts&#34;</span><span class="p">:</span> <span class="s2">&#34;1513807083.000104&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;user&#34;</span><span class="p">:</span> <span class="s2">&#34;U8HQN49LM&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;&lt;@U8HQN49LM&gt; has joined the channel&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;message&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;subtype&#34;</span><span class="p">:</span> <span class="s2">&#34;channel_join&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;ts&#34;</span><span class="p">:</span> <span class="s2">&#34;1513806864.000379&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;has_more&#34;</span><span class="p">:</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h3 class="relative group">Add a pinned message
    <div id="add-a-pinned-message" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#add-a-pinned-message" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Now that you&rsquo;ve got the id for your channel, and the timestamp for your message, you can use one of the <a href="https://api.slack.com/methods#pins"  target="_blank" rel="noreferrer">pin methods</a> - the <a href="https://api.slack.com/methods/pins.add"  target="_blank" rel="noreferrer">pins.add</a> API call - to pin the message to the channel.</p>
<p>Here&rsquo;s the request:</p>
<p><code>POST</code> <a href="https://slack.com/api/pins.add"  target="_blank" rel="noreferrer">https://slack.com/api/pins.add</a></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nt">&#34;channel&#34;</span><span class="p">:</span> <span class="s2">&#34;C8HN58XQU&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	<span class="nt">&#34;timestamp&#34;</span><span class="p">:</span> <span class="s2">&#34;1513859401.000295&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The result doesn&rsquo;t tell us much, other than it hopefully succeeded.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;ok&#34;</span><span class="p">:</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Checking the channel though, you should be able to confirm the message is pinned on the right side. Success!</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="slack-api&mdash;pinned-message-1"
    src="/what-is-slack-api/slack-api---pinned-message.png"
    width="714"
      height="540"></figure>

<h2 class="relative group">Thoughts
    <div id="thoughts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#thoughts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Similar to the <a href="https://grantwinney.com/what-is-dropbox-api/"  target="_blank" rel="noreferrer">Dropbox API</a> and its API Explorer, the Slack API lets you explore it online too, generating the code for you to copy when you&rsquo;re satisfied. This is super convenient for trying things out. If you have an auth token already, there doesn&rsquo;t seem to be a place to paste it in here - just click the button and follow the prompts to generate another one.</p>
<p>Then check out the <a href="https://api.slack.com/methods/pins.add/test"  target="_blank" rel="noreferrer">pins.add</a> method again. Here&rsquo;s what I got when I entered the same values that I put in Postman. (It&rsquo;s whining because the message is already pinned, but it works.)</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="slack-api&mdash;online-api-tester"
    src="/what-is-slack-api/slack-api---online-api-tester.png"
    width="513"
      height="818"></figure>
]]></content:encoded><media:content url="https://grantwinney.com/what-is-slack-api/feature.webp" medium="image" type="image/webp"/></item><item><title>Managing Files and Folders Using the Dropbox API</title><link>https://grantwinney.com/what-is-dropbox-api/</link><pubDate>Wed, 20 Dec 2017 04:59:25 +0000</pubDate><guid>https://grantwinney.com/what-is-dropbox-api/</guid><description>Dropbox provides file storage that syncs between your devices, and their API gives you access to that. Let&amp;rsquo;s check it out!</description><content:encoded><![CDATA[<p>Dropbox provides file storage that syncs between your devices, but also appears to provide for synchronized collaboration of individual files on teams. Neato. Let&rsquo;s check out the <a href="https://www.dropbox.com/developers/"  target="_blank" rel="noreferrer">Dropbox API</a>, or DBX as they apparently call it.</p>
<p>First though, two things to consider:</p>
<ul>
<li>If you&rsquo;re unfamiliar with APIs, <a href="https://grantwinney.com/what-is-an-api/"  target="_blank" rel="noreferrer">read this first</a> to familiarize yourself with the concept.</li>
<li>Install <a href="https://www.getpostman.com/"  target="_blank" rel="noreferrer">Postman</a>, which allows you to access API endpoints without having to write an app, as well as save the calls you make and sync them online.</li>
</ul>
<hr>

<h2 class="relative group">API Explorer
    <div id="api-explorer" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#api-explorer" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>This is pretty cool, and something I didn&rsquo;t see with the other APIs I&rsquo;ve looked at. Dropbox developed an <a href="https://dropbox.github.io/dropbox-api-v2-explorer/"  target="_blank" rel="noreferrer">API Explorer</a> that lets you try various endpoints right through the website. Just choose an endpoint, click the &ldquo;Get Token&rdquo; button <em>(you need to have an account and be signed in),</em> and then fill in whatever details are required for the endpoint you selected. It even links to the relevant documentation, and code you can use outside of the API Explorer&hellip; very handy.</p>

<h3 class="relative group">List Contents of a Folder
    <div id="list-contents-of-a-folder" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#list-contents-of-a-folder" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Here&rsquo;s the <a href="https://dropbox.github.io/dropbox-api-v2-explorer/#files_list_folder"  target="_blank" rel="noreferrer">list_folder</a> API call (leave all the fields empty except access token), which shows the only two files in my account - the ones Dropbox added when I signed up.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;entries&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;.tag&#34;</span><span class="p">:</span> <span class="s2">&#34;file&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Get Started with Dropbox.pdf&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;path_lower&#34;</span><span class="p">:</span> <span class="s2">&#34;/get started with dropbox.pdf&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;path_display&#34;</span><span class="p">:</span> <span class="s2">&#34;/Get Started with Dropbox.pdf&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;id:XhBonzeH_8AAAAAAAAAABQ&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;client_modified&#34;</span><span class="p">:</span> <span class="s2">&#34;2017-12-18T00:15:27Z&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;server_modified&#34;</span><span class="p">:</span> <span class="s2">&#34;2017-12-18T00:15:28Z&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;rev&#34;</span><span class="p">:</span> <span class="s2">&#34;17769f930&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;size&#34;</span><span class="p">:</span> <span class="mi">1102331</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;content_hash&#34;</span><span class="p">:</span> <span class="s2">&#34;f7ad488deb7d81790340ecd676fe6e47f0a6064fb99b982685b752d58611c1cb&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;.tag&#34;</span><span class="p">:</span> <span class="s2">&#34;file&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Get Started with Dropbox Paper.url&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;path_lower&#34;</span><span class="p">:</span> <span class="s2">&#34;/get started with dropbox paper.url&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;path_display&#34;</span><span class="p">:</span> <span class="s2">&#34;/Get Started with Dropbox Paper.url&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;id:XhBonzeH_8AAAAAAAAAABg&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;client_modified&#34;</span><span class="p">:</span> <span class="s2">&#34;2017-12-18T00:15:28Z&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;server_modified&#34;</span><span class="p">:</span> <span class="s2">&#34;2017-12-18T00:15:28Z&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;rev&#34;</span><span class="p">:</span> <span class="s2">&#34;27769f930&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;size&#34;</span><span class="p">:</span> <span class="mi">81</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;content_hash&#34;</span><span class="p">:</span> <span class="s2">&#34;16f386add4634a2e6e5a7fc782c51131a5347b9aabcc3cade0bc6c8bf7e304d9&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;cursor&#34;</span><span class="p">:</span> <span class="s2">&#34;AAFFJ-wCdYaJb11Vghos85aOI3eVAmvpJolSrVtaPev6PlXlRPPjpkAjdwl7eZoe33qTGoL2XuKDxzd-fNXuqMGBiy4JLu8nrsiDP9zRHCBXoQXfQWmgLUBo9U9vBp003hff6bMSBHpsSQ5dGovH5kSd&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;has_more&#34;</span><span class="p">:</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h3 class="relative group">Create a New Folder
    <div id="create-a-new-folder" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#create-a-new-folder" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>You can <a href="https://dropbox.github.io/dropbox-api-v2-explorer/#files_create_folder"  target="_blank" rel="noreferrer">create a new folder</a>:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="dropbox-api&mdash;create-folder"
    src="/what-is-dropbox-api/dropbox-api---create-folder.png"
    width="1264"
      height="838"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="dropbox-api&mdash;folder-created"
    src="/what-is-dropbox-api/dropbox-api---folder-created.png"
    width="532"
      height="208"></figure>

<h3 class="relative group">Move a File
    <div id="move-a-file" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#move-a-file" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Now <a href="https://dropbox.github.io/dropbox-api-v2-explorer/#files_move"  target="_blank" rel="noreferrer">move a file</a> into the folder you just created:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="dropbox-api&mdash;move-file"
    src="/what-is-dropbox-api/dropbox-api---move-file.png"
    width="720"
      height="633"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="dropbox-api&mdash;file-moved"
    src="/what-is-dropbox-api/dropbox-api---file-moved.png"
    width="605"
      height="188"></figure>

<h2 class="relative group">Authenticating
    <div id="authenticating" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#authenticating" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>In order to play around <em>without</em> using their API Explorer, you&rsquo;ll need an access token. You can either copy the one that the API Explorer generated for you, or you can create a new app.</p>
<p>If you decide to create a new app, <a href="https://www.dropbox.com/developers/apps"  target="_blank" rel="noreferrer">go here</a>, press &ldquo;Create app&rdquo;, choose &ldquo;Dropbox API&rdquo; and &ldquo;Full Dropbox&rdquo;, then smash a bunch of keys for the name of your app - because apparently it has to be a unique name among every app anyone has ever made. 😕</p>
<p>Press &ldquo;Create app&rdquo; and on the next page you&rsquo;ll find a section that lets you generate an access token.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="dropbox-api&mdash;create-app"
    src="/what-is-dropbox-api/dropbox-api---create-app.png"
    width="720"
      height="272"></figure>

<h2 class="relative group">Trying it out
    <div id="trying-it-out" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#trying-it-out" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The API Explorer also includes the actual code you can use to make the call on your own. Look for the &ldquo;Show Code&rdquo; button and click it.</p>
<p><em>You&rsquo;ll need these headers specified for the following examples:</em></p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="dropbox-api&mdash;headers"
    src="/what-is-dropbox-api/dropbox-api---headers.png"
    width="755"
      height="159"></figure>

<h3 class="relative group">List Contents of a Folder
    <div id="list-contents-of-a-folder-1" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#list-contents-of-a-folder-1" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Check out the <a href="https://dropbox.github.io/dropbox-api-v2-explorer/#files_list_folder"  target="_blank" rel="noreferrer">list folder</a> endpoint again in the API Explorer. Click the Show Code button and you should see something similar to this. You might even want to enter some values and watch how the code block is updated.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">curl -X POST https://api.dropboxapi.com/2/files/list_folder \
  --header &#39;Authorization: Bearer null&#39; \
  --header &#39;Content-Type: application/json&#39; \
  --data &#39;{&#34;path&#34;:&#34;&#34;}</code></pre></div>
<p>You can use that to construct a call in Postman. You can also get the same info from the <a href="https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder"  target="_blank" rel="noreferrer">docs</a>, but it&rsquo;s great that you can tweak the values in the API Explorer, and when you&rsquo;re happy with the results then you can copy the &ldquo;Show Code&rdquo; section.</p>
<p>I decided to list all folders recursively from the very top.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="dropbox-api&mdash;postman-list-folder"
    src="/what-is-dropbox-api/dropbox-api---postman-list-folder.png"
    width="558"
      height="674"></figure>

<h3 class="relative group">Move a File
    <div id="move-a-file-1" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#move-a-file-1" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Let&rsquo;s try moving a file again. I grabbed this code from the <a href="https://dropbox.github.io/dropbox-api-v2-explorer/#files_move"  target="_blank" rel="noreferrer">API Explorer</a> too.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="dropbox-api&mdash;move-file-postman-2"
    src="/what-is-dropbox-api/dropbox-api---move-file-postman-2.png"
    width="779"
      height="589"></figure>

<h2 class="relative group">Thoughts
    <div id="thoughts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#thoughts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The API Explorer is a really cool tool. It&rsquo;s a good example of dogfooding, where developers use their own code (the API endpoints) to make a tool they can also use (the API Explorer). It also allows you to find the exact endpoint you&rsquo;re interested in, then find links to documentation and the code needed to implement it outside the explorer.</p>
<p>They have a lot of <a href="https://www.dropbox.com/developers/documentation"  target="_blank" rel="noreferrer">examples in various languages</a>. I briefly checked out the <a href="https://www.dropbox.com/developers/documentation/dotnet#tutorial"  target="_blank" rel="noreferrer">.NET examples</a>, and they even offer a Dropbox.NET SDK to make development far easier. It&rsquo;s more friendly looking than having to call a REST endpoint directly in code.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">static</span> <span class="kd">async</span> <span class="n">Task</span> <span class="n">Run</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">dbx</span> <span class="p">=</span> <span class="k">new</span> <span class="n">DropboxClient</span><span class="p">(</span><span class="s">&#34;YOUR ACCESS TOKEN&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">full</span> <span class="p">=</span> <span class="k">await</span> <span class="n">dbx</span><span class="p">.</span><span class="n">Users</span><span class="p">.</span><span class="n">GetCurrentAccountAsync</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;{0} - {1}&#34;</span><span class="p">,</span> <span class="n">full</span><span class="p">.</span><span class="n">Name</span><span class="p">.</span><span class="n">DisplayName</span><span class="p">,</span> <span class="n">full</span><span class="p">.</span><span class="n">Email</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
]]></content:encoded><media:content url="https://grantwinney.com/what-is-dropbox-api/feature.webp" medium="image" type="image/webp"/></item><item><title>Access Buckets and Files on Backblaze With the B2 Cloud Storage API</title><link>https://grantwinney.com/what-is-backblaze-b2-api/</link><pubDate>Sun, 17 Dec 2017 19:37:29 +0000</pubDate><guid>https://grantwinney.com/what-is-backblaze-b2-api/</guid><description>The Backblaze B2 Storage API, built on top of Backblaze&amp;rsquo;s cloud storage, lets you access and manage your buckets. Let&amp;rsquo;s check it out!</description><content:encoded><![CDATA[<p>If you&rsquo;re not familiar with <a href="https://secure.backblaze.com/r/00d15h"  target="_blank" rel="noreferrer">Backblaze</a>, they&rsquo;re a handy and inexpensive service that backs up your computer. I&rsquo;ve been using them for years, and I even had to restore files when my hard drive succumbed to the &ldquo;click of death&rdquo; a couple years ago, so totally worth it.</p>
<p>They have a cloud storage service too, in the same vein as AWS, Rackspace, etc, so <a href="https://www.backblaze.com/b2/sign-up.html"  target="_blank" rel="noreferrer">sign up</a> and then we&rsquo;ll check out the <a href="https://www.backblaze.com/b2/docs/"  target="_blank" rel="noreferrer">Backblaze B2 Storage API</a>.</p>
<p>First though, two things to consider:</p>
<ul>
<li>If you&rsquo;re unfamiliar with APIs, <a href="https://grantwinney.com/what-is-an-api/"  target="_blank" rel="noreferrer">read this</a> to familiarize yourself with the concept.</li>
<li>Install <a href="https://www.postman.com/"  target="_blank" rel="noreferrer">Postman</a>, which allows you to access API endpoints without having to write an app, as well as save the calls you make and sync them online.</li>
</ul>

<h2 class="relative group">Authenticating
    <div id="authenticating" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#authenticating" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>As usual, you&rsquo;ll need to prove who you are before you can go making requests.</p>

<h3 class="relative group">Generate an Application Key
    <div id="generate-an-application-key" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#generate-an-application-key" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>While logged in to the Backblaze site, click on the &ldquo;My Account&rdquo; link in the upper-right, then the &ldquo;Show Account ID and Application Key&rdquo; link. A dialog captioned &ldquo;Account ID &amp; Application Key&rdquo; appears. Press the &ldquo;Create Application Key&rdquo; button and you&rsquo;ll have the two vital pieces of data you need for authenticating with Backblaze.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="instagram-api&mdash;account-id-application-key"
    src="/what-is-backblaze-b2-api/instagram-api---account-id-application-key.png"
    width="1354"
      height="936"></figure>

<h3 class="relative group">Get an Authorization Token
    <div id="get-an-authorization-token" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#get-an-authorization-token" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>You&rsquo;ll need to make an API call to get the authorization token, which then lets you make other more interesting API calls.</p>

<h4 class="relative group">Using Curl
    <div id="using-curl" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#using-curl" aria-label="Anchor">#</a>
    </span>
    
</h4>
<p>To <a href="https://www.backblaze.com/b2/docs/b2_authorize_account.html"  target="_blank" rel="noreferrer">get the authorization token</a>, you can do one of a couple of things. If you have <code>curl</code> available in your terminal, just run this, substituting <code>ACCOUNT_ID</code> and <code>APPLICATION_KEY</code> with the appropriate values.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">curl https://api.backblazeb2.com/b2api/v1/b2_authorize_account -u &#34;ACCOUNT_ID:APPLICATION_KEY&#34;</span></span></code></pre></div></div>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;absoluteMinimumPartSize&#34;</span><span class="p">:</span> <span class="mi">5000000</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;accountId&#34;</span><span class="p">:</span> <span class="err">&lt;your_account_id&gt;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;apiUrl&#34;</span><span class="p">:</span> <span class="s2">&#34;https://api001.backblazeb2.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;authorizationToken&#34;</span><span class="p">:</span> <span class="err">&lt;your_new_shiny_auth_token&gt;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;downloadUrl&#34;</span><span class="p">:</span> <span class="s2">&#34;https://f001.backblazeb2.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;minimumPartSize&#34;</span><span class="p">:</span> <span class="mi">100000000</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;recommendedPartSize&#34;</span><span class="p">:</span> <span class="mi">100000000</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h4 class="relative group">Using Postman
    <div id="using-postman" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#using-postman" aria-label="Anchor">#</a>
    </span>
    
</h4>
<p>You can also do it from within Postman, by doing a <code>GET</code> on <a href="https://api.backblazeb2.com/b2api/v1/b2_authorize_account"  target="_blank" rel="noreferrer">https://api.backblazeb2.com/b2api/v1/b2_authorize_account</a>, and setting up authorization like in the following screenshot. Just add the <code>ACCOUNT_ID</code> and <code>APPLICATION_KEY</code> in the username and password fields, and click the &ldquo;Preview Request&rdquo; button. That adds a new &ldquo;Authorization&rdquo; field under the &ldquo;Headers&rdquo; tab, which is the base-64 encoded version of your account id and application key.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="backblaze-api&mdash;requesting-auth-token"
    src="/what-is-backblaze-b2-api/backblaze-api---requesting-auth-token.png"
    width="813"
      height="590"></figure>

<h3 class="relative group">A Quick Note on apiUrl
    <div id="a-quick-note-on-apiurl" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#a-quick-note-on-apiurl" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Take a look at the response you get with the auth token in it. There&rsquo;s a field called <code>apiUrl</code>, which is important too. When you make other API calls, if you try to use the same base url you used to authenticate, you&rsquo;ll get an error:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">https://api.backblazeb2.com/b2api/v1/b2_create_bucket?accountId=...</span></span></code></pre></div></div>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;code&#34;</span><span class="p">:</span> <span class="s2">&#34;bad_request&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;message&#34;</span><span class="p">:</span> <span class="s2">&#34;this request should go to a host name for B2_API&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="mi">400</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Just substitute <code>api.backblazeb2.com</code> with whatever value is in the <code>apiUrl</code> field:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">https://api001.backblazeb2.com/b2api/v1/b2_create_bucket?accountId=...</span></span></code></pre></div></div>
<hr>

<h2 class="relative group">Testing It Out
    <div id="testing-it-out" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#testing-it-out" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There&rsquo;s a lot you can do - <a href="https://www.backblaze.com/b2/docs/"  target="_blank" rel="noreferrer">check out the docs</a>. I&rsquo;ll just touch the tip of the iceberg here.</p>

<h3 class="relative group">Create a Bucket
    <div id="create-a-bucket" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#create-a-bucket" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Let&rsquo;s try <a href="https://www.backblaze.com/b2/docs/b2_create_bucket.html"  target="_blank" rel="noreferrer">creating a bucket</a>. Use the <code>apiUrl</code> and <code>authorizationToken</code> values you got in the previous call. Notice that we&rsquo;re using the <code>GET</code> action here.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="backblaze-b2-api&mdash;create-bucket"
    src="/what-is-backblaze-b2-api/backblaze-b2-api---create-bucket.png"
    width="784"
      height="594"></figure>
<p>Flip over to the website if you want, and make sure it was created.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="backblaze-b2-api&mdash;bucket-created"
    src="/what-is-backblaze-b2-api/backblaze-b2-api---bucket-created.png"
    width="871"
      height="619"></figure>
<p>It&rsquo;s odd that they allow use of the <code>GET</code>, but it&rsquo;s documented. Usually you <code>GET</code> some piece of data, whereas creating something would generally be a <code>POST</code>. You can do it that way too. <em>(It&rsquo;s not shown here, but the Authorization token is still in the headers section.)</em></p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="backblaze-b2-api&mdash;create-another-bucket"
    src="/what-is-backblaze-b2-api/backblaze-b2-api---create-another-bucket.png"
    width="776"
      height="559"></figure>
<p>I implemented it in C#, so you can try it out from an actual language and not just Postman. Either copy from here <em>(fill in your details)</em> or <a href="https://dotnetfiddle.net/tFcFdj"  target="_blank" rel="noreferrer">try it on DotNetFiddle</a>.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">System.Net</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">					
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Program</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="kd">public</span> <span class="kd">static</span> <span class="k">void</span> <span class="n">Main</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">	<span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="kt">var</span> <span class="n">apiUrl</span> <span class="p">=</span> <span class="s">&#34;API_URL&#34;</span><span class="p">;</span>                  <span class="c1">//Provided by b2_authorize_account </span>
</span></span><span class="line"><span class="cl">		<span class="kt">var</span> <span class="n">authToken</span> <span class="p">=</span> <span class="s">&#34;AUTH_TOKEN&#34;</span><span class="p">;</span>            <span class="c1">//Provided by b2_authorize_account</span>
</span></span><span class="line"><span class="cl">		<span class="kt">var</span> <span class="n">accountId</span> <span class="p">=</span> <span class="s">&#34;ACCOUNT_ID&#34;</span><span class="p">;</span>            <span class="c1">//B2 Cloud Storage AccountID</span>
</span></span><span class="line"><span class="cl">		<span class="kt">var</span> <span class="n">bucketName</span> <span class="p">=</span> <span class="s">&#34;my-very-first-bucket&#34;</span><span class="p">;</span> <span class="c1">//The unique bucket ID</span>
</span></span><span class="line"><span class="cl">		<span class="kt">var</span> <span class="n">bucketType</span> <span class="p">=</span> <span class="s">&#34;allPrivate&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">		
</span></span><span class="line"><span class="cl">		<span class="kt">var</span> <span class="n">postUrl</span> <span class="p">=</span> <span class="s">$&#34;{apiUrl}/b2api/v1/b2_create_bucket&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">		<span class="kt">var</span> <span class="n">jsonData</span> <span class="p">=</span> <span class="s">$&#34;{{\&#34;</span><span class="n">accountId</span><span class="err">\</span><span class="s">&#34;:\&#34;{accountId}\&#34;,\&#34;bucketName\&#34;:\&#34;{bucketName}\&#34;,\&#34;bucketType\&#34;:\&#34;{bucketType}\&#34;}}&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">		
</span></span><span class="line"><span class="cl">		<span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">client</span> <span class="p">=</span> <span class="k">new</span> <span class="n">WebClient</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">		<span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="n">client</span><span class="p">.</span><span class="n">Headers</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="s">&#34;Authorization&#34;</span><span class="p">,</span> <span class="n">authToken</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">			<span class="kt">var</span> <span class="n">response</span> <span class="p">=</span> <span class="n">client</span><span class="p">.</span><span class="n">UploadString</span><span class="p">(</span><span class="n">postUrl</span><span class="p">,</span> <span class="n">jsonData</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">			<span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">response</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">		<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h3 class="relative group">Delete a Bucket
    <div id="delete-a-bucket" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#delete-a-bucket" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Might as well clean up after ourselves. Now that you&rsquo;ve created a bucket, how about <a href="https://www.backblaze.com/b2/docs/b2_delete_bucket.html"  target="_blank" rel="noreferrer">deleting the bucket</a>? That&rsquo;s as easy as posting to the right endpoint and providing the id of the bucket (returned in the creation above).</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="backblaze-b2-api&mdash;delete-bucket"
    src="/what-is-backblaze-b2-api/backblaze-b2-api---delete-bucket.png"
    width="704"
      height="557"></figure>
<p>This highlights another odd decision though. REST provides a <code>DELETE</code> action that would&rsquo;ve been more intuitive, so that a call like this could&rsquo;ve worked (if they had designed it that way).</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">DELETE https://api001.backblazeb2.com/b2api/v1/b2_delete_bucket/&lt;your_bucket_id&gt;</span></span></code></pre></div></div>
<p>One of the things I&rsquo;ve noticed about REST is that there are few hard and fast rules&hellip; just a lot of guidelines, suggestions, and differing opinions. In this case it&rsquo;s not bad at all, but try hard enough and you can do some truly unintuitive stuff with REST.</p>
<p>I implemented this one in C# too. Copy the code below or <a href="https://dotnetfiddle.net/QDAonx"  target="_blank" rel="noreferrer">try it on DotNetFiddle</a>. Don&rsquo;t forget to fill in your own details.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">System.Net</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">					
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Program</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="kd">public</span> <span class="kd">static</span> <span class="k">void</span> <span class="n">Main</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">	<span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="kt">var</span> <span class="n">apiUrl</span> <span class="p">=</span> <span class="s">&#34;API_URL&#34;</span><span class="p">;</span>        <span class="c1">//Provided by b2_authorize_account </span>
</span></span><span class="line"><span class="cl">		<span class="kt">var</span> <span class="n">authToken</span> <span class="p">=</span> <span class="s">&#34;AUTH_TOKEN&#34;</span><span class="p">;</span>  <span class="c1">//Provided by b2_authorize_account</span>
</span></span><span class="line"><span class="cl">		<span class="kt">var</span> <span class="n">accountId</span> <span class="p">=</span> <span class="s">&#34;ACCOUNT_ID&#34;</span><span class="p">;</span>  <span class="c1">//B2 Cloud Storage AccountID </span>
</span></span><span class="line"><span class="cl">		<span class="kt">var</span> <span class="n">bucketId</span> <span class="p">=</span> <span class="s">&#34;BUCKET_ID&#34;</span><span class="p">;</span>    <span class="c1">//The unique bucket ID		</span>
</span></span><span class="line"><span class="cl">		
</span></span><span class="line"><span class="cl">		<span class="kt">var</span> <span class="n">postUrl</span> <span class="p">=</span> <span class="s">$&#34;{apiUrl}/b2api/v1/b2_delete_bucket&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">		<span class="kt">var</span> <span class="n">jsonData</span> <span class="p">=</span> <span class="s">$&#34;{{\&#34;</span><span class="n">accountId</span><span class="err">\</span><span class="s">&#34;:\&#34;{accountId}\&#34;,\&#34;bucketId\&#34;:\&#34;{bucketId}\&#34;}}&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">		
</span></span><span class="line"><span class="cl">		<span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">client</span> <span class="p">=</span> <span class="k">new</span> <span class="n">WebClient</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">		<span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="n">client</span><span class="p">.</span><span class="n">Headers</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="s">&#34;Authorization&#34;</span><span class="p">,</span> <span class="n">authToken</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">			
</span></span><span class="line"><span class="cl">			<span class="kt">var</span> <span class="n">response</span> <span class="p">=</span> <span class="n">client</span><span class="p">.</span><span class="n">UploadString</span><span class="p">(</span><span class="n">postUrl</span><span class="p">,</span> <span class="n">jsonData</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">			
</span></span><span class="line"><span class="cl">			<span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">response</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">		<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h2 class="relative group">Thoughts
    <div id="thoughts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#thoughts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>It&rsquo;s great that the Backblaze API docs include sample usages in 6 different languages for each endpoint. This should make it far easier to get started, although I noticed the code snippets are a bit outdated, at least the C# snippet I started to use before I wrote my own.</p>
<p>The requirement to setup an &ldquo;application&rdquo;, in order to generate an authentication token and allow calls to their API, seems to be pretty common. It makes sense, since any system - no matter how sophisticated - has limits. If a single user/account is found to be making too many requests, they can be throttled or disabled.</p>
]]></content:encoded><media:content url="https://grantwinney.com/what-is-backblaze-b2-api/feature.webp" medium="image" type="image/webp"/></item><item><title>Accessing Tweets and More With the Twitter API</title><link>https://grantwinney.com/what-is-twitter-api/</link><pubDate>Sat, 16 Dec 2017 04:58:47 +0000</pubDate><guid>https://grantwinney.com/what-is-twitter-api/</guid><description>The Twitter API lets you access tweets, users who tweet, metadata, manipulate lists, and more. Let&amp;rsquo;s check it out!</description><content:encoded><![CDATA[<p>Let&rsquo;s check out the Twitter API, how to use it and what it has to offer. You can start with their <a href="https://developer.twitter.com/en/docs/basics/getting-started"  target="_blank" rel="noreferrer">getting started guide</a> if you like, but basically Twitter offers API calls that return various JSON payloads, or what they call <a href="https://developer.twitter.com/en/docs/tweets/data-dictionary/overview/intro-to-tweet-json"  target="_blank" rel="noreferrer">tweet data dictionaries</a>.</p>
<p>Their API allows you to access tweets (a basic message), users (metadata about the senders of tweets), entities (components of a message, like hashtags and urls), and &ldquo;extended&rdquo; entities (media, such as videos and pictures). You can do <a href="https://developer.twitter.com/en/docs/api-reference-index"  target="_blank" rel="noreferrer">a lot more</a> too, like manipulate lists, report users as spam, etc.</p>
<p>First though, two things to consider:</p>
<ul>
<li>If you&rsquo;re unfamiliar with APIs, <a href="https://grantwinney.com/what-is-an-api/"  target="_blank" rel="noreferrer">read this first</a> to familiarize yourself with the concept.</li>
<li>Install <a href="https://www.getpostman.com/"  target="_blank" rel="noreferrer">Postman</a>, which allows you to access API endpoints without having to write an app.</li>
</ul>

<h2 class="relative group">Authenticating
    <div id="authenticating" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#authenticating" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Before you can do anything, you&rsquo;ll need to authenticate. Twitter wants to know you&rsquo;re a valid (authorized) user before you start using their API. For example, I just used Postman to get some of my followers&rsquo; IDs without authenticating. They don&rsquo;t allow it and they let me know in the response.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="twitter_api_bad_authentication"
    src="/what-is-twitter-api/twitter_api_bad_authentication.png"
    width="882"
      height="423"></figure>
<p>Go ahead and try it yourself. We&rsquo;ll fix it up pretty soon, so it&rsquo;ll actually work.</p>

<h3 class="relative group">Create an Application
    <div id="create-an-application" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#create-an-application" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Normally, if you&rsquo;re accessing an API it&rsquo;s because you&rsquo;re trying to create your own application to do something with the results of those API calls. The first step in &ldquo;authorizing&rdquo; yourself is to <a href="https://apps.twitter.com/"  target="_blank" rel="noreferrer">tell Twitter a little about the application you&rsquo;d like to make</a>&hellip; of course, you don&rsquo;t have an <em>actual</em> app to make yet, so just fill in whatever bogus info you&rsquo;d like!</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="twitter_create_application-1"
    src="/what-is-twitter-api/twitter_create_application-1.png"
    width="590"
      height="581"></figure>
<p>After filling in the first three fields <em>(in a real-world app, you&rsquo;d need to specify a redirect URL too),</em> and selecting that all-important developer agreement box, it should create your application and show a message at the top. Step one complete.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="twitter_application_created"
    src="/what-is-twitter-api/twitter_application_created.png"
    width="766"
      height="808"></figure>

<h3 class="relative group">Generate an Access Token
    <div id="generate-an-access-token" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#generate-an-access-token" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>You still need an access token, but if you flip to the &ldquo;Keys and Access Tokens&rdquo; tab you&rsquo;ll notice that there&rsquo;s a message that you don&rsquo;t have one yet.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="twitter_application_no_token"
    src="/what-is-twitter-api/twitter_application_no_token.png"
    width="772"
      height="623"></figure>
<p>Time to fix that. Click on <em>&ldquo;Create my access token&rdquo;</em> to generate a random token and token secret, similar to below. You&rsquo;ll need those values marked by red arrows in the next step.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="twitter_application_new_token"
    src="/what-is-twitter-api/twitter_application_new_token.png"
    width="764"
      height="887"></figure>

<h3 class="relative group">Use the Access Token
    <div id="use-the-access-token" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#use-the-access-token" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Back in Postman where the previous API call failed, click the Authorization tab, choose OAuth 1.0, and enter the values indicated by the red arrows up above into Postman.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="twitter_api_config_oauth_in_postman"
    src="/what-is-twitter-api/twitter_api_config_oauth_in_postman.png"
    width="959"
      height="375"></figure>
<p>Try an API call again and it should work this time.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="twitter_api_postman_request_works"
    src="/what-is-twitter-api/twitter_api_postman_request_works.png"
    width="869"
      height="533"></figure>
<hr>

<h2 class="relative group">What else can it do?
    <div id="what-else-can-it-do" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-else-can-it-do" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Lots, as it turns out. There&rsquo;s an <a href="https://developer.twitter.com/en/docs/api-reference-index"  target="_blank" rel="noreferrer">API reference</a> that gives us a lot to try.</p>
<p>You could <a href="https://developer.twitter.com/en/docs/developer-utilities/terms-of-service/api-reference/get-help-tos"  target="_blank" rel="noreferrer">get the terms of service</a> you skipped when you signed up for Twitter. If you do, would you give me the cliff&rsquo;s notes version?</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="twitter_api_get_tos"
    src="/what-is-twitter-api/twitter_api_get_tos.png"
    width="879"
      height="396"></figure>
<p>You could <a href="https://developer.twitter.com/en/docs/trends/locations-with-trending-topics/api-reference/get-trends-closest"  target="_blank" rel="noreferrer">use your coordinates to get a WOEID</a> <em>(</em><a href="https://www.latlong.net/"  target="_blank" rel="noreferrer"><em>find your lat/long here</em></a><em>)</em>, and then <a href="https://developer.twitter.com/en/docs/trends/trends-for-location/api-reference/get-trends-place"  target="_blank" rel="noreferrer">use the WOEID to find trends for that location</a>. I tried it out for Cleveland. <em>(Oddly, nothing in the list of results, which is clearly for Cleveland, matches the &ldquo;Cleveland trends&rdquo; I see when I actually visit twitter.com. I dunno&hellip;)</em></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="err">GET</span> <span class="err">https:</span><span class="c1">//api.twitter.com/1.1/trends/closest.json?lat=41.499320&amp;long=-81.694361
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Cleveland&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;placeType&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;code&#34;</span><span class="p">:</span> <span class="mi">7</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Town&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;http://where.yahooapis.com/v1/place/2381475&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;parentid&#34;</span><span class="p">:</span> <span class="mi">23424977</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;country&#34;</span><span class="p">:</span> <span class="s2">&#34;United States&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;woeid&#34;</span><span class="p">:</span> <span class="mi">2381475</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;countryCode&#34;</span><span class="p">:</span> <span class="s2">&#34;US&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">]</span></span></span></code></pre></div></div>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="err">GET</span> <span class="err">https:</span><span class="c1">//api.twitter.com/1.1/trends/place.json?id=2381475
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;trends&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Jeezy&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;http://twitter.com/search?q=Jeezy&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;promoted_content&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;query&#34;</span><span class="p">:</span> <span class="s2">&#34;Jeezy&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;tweet_volume&#34;</span><span class="p">:</span> <span class="mi">42661</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Embiid&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;http://twitter.com/search?q=Embiid&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;promoted_content&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;query&#34;</span><span class="p">:</span> <span class="s2">&#34;Embiid&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;tweet_volume&#34;</span><span class="p">:</span> <span class="mi">56065</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Roberson&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;http://twitter.com/search?q=Roberson&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;promoted_content&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;query&#34;</span><span class="p">:</span> <span class="s2">&#34;Roberson&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;tweet_volume&#34;</span><span class="p">:</span> <span class="mi">15400</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;#LivePD&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;http://twitter.com/search?q=%23LivePD&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;promoted_content&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;query&#34;</span><span class="p">:</span> <span class="s2">&#34;%23LivePD&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;tweet_volume&#34;</span><span class="p">:</span> <span class="mi">16908</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Russ&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;http://twitter.com/search?q=Russ&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;promoted_content&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;query&#34;</span><span class="p">:</span> <span class="s2">&#34;Russ&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;tweet_volume&#34;</span><span class="p">:</span> <span class="mi">38797</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="err">...</span>
</span></span><span class="line"><span class="cl">        <span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;as_of&#34;</span><span class="p">:</span> <span class="s2">&#34;2017-12-16T04:17:41Z&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;created_at&#34;</span><span class="p">:</span> <span class="s2">&#34;2017-12-16T04:15:44Z&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;locations&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Cleveland&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;woeid&#34;</span><span class="p">:</span> <span class="mi">2381475</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">]</span></span></span></code></pre></div></div>
<p>You&rsquo;re not limited to just requesting data either. There are plenty of API endpoints that allow you to <em>create</em> something too. For example, you could <a href="https://developer.twitter.com/en/docs/accounts-and-users/create-manage-lists/api-reference/post-lists-create"  target="_blank" rel="noreferrer">create a new list</a>.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="err">POST</span> <span class="err">https:</span><span class="c1">//api.twitter.com/1.1/lists/create.json?name=Public Figures&amp;description=Politics, celebrities, whatever...
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">941910108359086080</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;id_str&#34;</span><span class="p">:</span> <span class="s2">&#34;941910108359086080&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Public Figures&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;uri&#34;</span><span class="p">:</span> <span class="s2">&#34;/GrantWinney/lists/public-figures&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;subscriber_count&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;member_count&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;mode&#34;</span><span class="p">:</span> <span class="s2">&#34;public&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;description&#34;</span><span class="p">:</span> <span class="s2">&#34;Politics, celebrities, whatever...&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;slug&#34;</span><span class="p">:</span> <span class="s2">&#34;public-figures&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;full_name&#34;</span><span class="p">:</span> <span class="s2">&#34;@GrantWinney/public-figures&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;created_at&#34;</span><span class="p">:</span> <span class="s2">&#34;Sat Dec 16 05:57:24 +0000 2017&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;following&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;user&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="err">...</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="twitter_api_create_list"
    src="/what-is-twitter-api/twitter_api_create_list.png"
    width="605"
      height="215"></figure>
<p>If you&rsquo;d like to develop an application in a particular language to take advantage of the API, Twitter has a <a href="https://developer.twitter.com/en/docs/developer-utilities/twitter-libraries"  target="_blank" rel="noreferrer">list of libraries</a> that&rsquo;ll get you started.</p>

<h2 class="relative group">Notes
    <div id="notes" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#notes" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you get an error like this one, even though some requests are making it through, double-check the request and parameters. While experimenting I&rsquo;d sometimes get a more specific error, but I saw this one quite a few times when I&rsquo;d added invalid query parameter names or invalid values for valid names.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;errors&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;code&#34;</span><span class="p">:</span> <span class="mi">32</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;message&#34;</span><span class="p">:</span> <span class="s2">&#34;Could not authenticate you.&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
]]></content:encoded><media:content url="https://grantwinney.com/what-is-twitter-api/feature.webp" medium="image" type="image/webp"/></item><item><title>Taming the Erlang Beast</title><link>https://grantwinney.com/taming-the-erlang-beast/</link><pubDate>Wed, 01 Nov 2017 16:50:27 +0000</pubDate><guid>https://grantwinney.com/taming-the-erlang-beast/</guid><description>Becoming an Erlang developer has not always been easy, but over the last couple of years I&amp;rsquo;ve learned a few ways to tame the beast. It doesn&amp;rsquo;t need to become any other language, but there&amp;rsquo;s definitely room for improving the developer experience!</description><content:encoded><![CDATA[<p>When I started programming in Erlang professionally, it was a steeper climb than I had anticipated. There&rsquo;s a <em>lot</em> that&rsquo;s different from C# - static vs dynamic types, object-oriented vs functional, the immutability of variables and heavy emphasis on pattern-matching and recursion. It&rsquo;s a very different way of thinking. And unfortunately for me, there&rsquo;s no Visual Studio for Erlang to hold your hand.</p>
<p>Over the last couple of years I&rsquo;ve learned a few ways to tame the Erlang beast. It doesn&rsquo;t need to become C# or any other language, but there&rsquo;s definitely room for improving the developer experience.</p>

<h2 class="relative group">Dialyzer
    <div id="dialyzer" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#dialyzer" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="http://erlang.org/doc/man/dialyzer.html"  target="_blank" rel="noreferrer">Dialyzer</a> is a static analysis tool that reports when you&rsquo;re attempting to pass the wrong types between functions, have unreachable code, etc.</p>
<p>First, you have to run a command like this, in order to build up a persistent lookup table (PLT) containing type information for modules of the Erlang standard library. That information in turn is used by the actual analysis.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="n">dialyzer</span> <span class="o">--</span><span class="n">build_plt</span> <span class="o">--</span><span class="n">apps</span> <span class="n">erts</span> <span class="n">kernel</span> <span class="n">stdlib</span></span></span></code></pre></div></div>
<p>If we run Dialyzer against the following module, we&rsquo;ll be presented with a few warnings. Note that this module would have compiled just fine, and then would&rsquo;ve thrown an exception at runtime.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">module</span><span class="p">(</span><span class="n">dialsample</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">export</span><span class="p">([</span><span class="n">function1</span><span class="o">/</span><span class="mi">0</span><span class="p">]).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">function1</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="n">add_lists_of_ages</span><span class="p">(</span><span class="mi">20</span><span class="p">,</span> <span class="mi">25</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl"><span class="nf">add_lists_of_ages</span><span class="p">(</span><span class="nv">Ages1</span><span class="p">,</span> <span class="nv">Ages2</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Ages1</span> <span class="o">++</span> <span class="nv">Ages2</span><span class="p">.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">unused_function</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="n">ok</span><span class="p">.</span></span></span></code></pre></div></div>
<p>Let&rsquo;s break the following output down a bit, in case it&rsquo;s the first time you&rsquo;ve seen Dialyzer in action. It&rsquo;s warning us that since we&rsquo;re using the <code>++</code> operator, the first parameter to <code>add_lists_of_ages</code> <em>must</em> be a list of something (anything). So it knows that passing <code>20</code> as the first parameter is going to fail. It&rsquo;s also warning us that <code>unused_function</code> will never be called, since it&rsquo;s not exported and nothing in the module calls it.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">&gt; dialyzer --src dialsample.erl

dialsample.erl:4: Function function1/0 has no local return
dialsample.erl:5: The call dialsample:add_lists_of_ages(20,25) will never return since it differs
  in the 1st argument from the success typing arguments: ([any()],any())

dialsample.erl:7: Function add_lists_of_ages/2 has no local return
dialsample.erl:8: The call erlang:&#39;++&#39;(Ages1::20,Ages2::25) will never return since it differs in
  the 1st argument from the success typing arguments: ([any()],any())

dialsample.erl:10: Function unused_function/0 will never be called</code></pre></div>
<p>Dialyzer is helpful by itself, but it&rsquo;s even more powerful when used with specs, which I&rsquo;ll cover next. If you&rsquo;d like to learn more about Dialyzer, start here:</p>
<ul>
<li><a href="http://erlang.org/doc/man/dialyzer.html"  target="_blank" rel="noreferrer">Dialyzer, a DIscrepancy AnaLYZer for ERlang programs</a> (official docs)</li>
<li><a href="http://learnyousomeerlang.com/dialyzer"  target="_blank" rel="noreferrer">Type Specifications and Erlang</a> (Learn You Some Erlang)</li>
</ul>

<h2 class="relative group">Specs
    <div id="specs" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#specs" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Erlang is strongly typed (like C#, Java and others), but it&rsquo;s dynamic (not static) typed. That means it has <a href="http://erlang.org/doc/reference_manual/data_types.html"  target="_blank" rel="noreferrer">distinct data types</a>, but it resolves them at runtime instead of compile time - in other words, you don&rsquo;t realize you&rsquo;ve screwed up until your code is executed and blows up in your face.</p>
<p>Even when everything is working smoothly, it&rsquo;s absolutely painful to revisit a function that accepts multiple data types months later and try to expand on it. Or refactor it. Or just look at it.</p>
<p>Dialyzer is powerful on its own, but coupling it with <a href="http://erlang.org/doc/reference_manual/typespec.html#id80050"  target="_blank" rel="noreferrer">specs</a> dials things up to 11. Here&rsquo;s the above example again, but including specs this time.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">module</span><span class="p">(</span><span class="n">dialsample</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">export</span><span class="p">([</span><span class="n">function1</span><span class="o">/</span><span class="mi">0</span><span class="p">]).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">function1</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="n">pos_integer</span><span class="p">()].</span>
</span></span><span class="line"><span class="cl"><span class="nf">function1</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="n">add_lists_of_ages</span><span class="p">(</span><span class="mi">20</span><span class="p">,</span> <span class="mi">25</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">add_lists_of_ages</span><span class="p">([</span><span class="n">pos_integer</span><span class="p">()],</span> <span class="p">[</span><span class="n">pos_integer</span><span class="p">()])</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="n">pos_integer</span><span class="p">()].</span>
</span></span><span class="line"><span class="cl"><span class="nf">add_lists_of_ages</span><span class="p">(</span><span class="nv">Ages1</span><span class="p">,</span> <span class="nv">Ages2</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Ages1</span> <span class="o">++</span> <span class="nv">Ages2</span><span class="p">.</span></span></span></code></pre></div></div>
<p>Now when we run Dialyzer it recognizes that the function should receive (and return) lists of positive integers (and not just lists of anything).</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">dialsample.erl:6: Function function1/0 has no local return
dialsample.erl:7: The call dialsample:add_lists_of_ages(20,25) will never return since the success typing
  is ([any()],any()) -&gt; any() and the contract is ([pos_integer()],[pos_integer()]) -&gt; [pos_integer()]
  
dialsample.erl:10: Function add_lists_of_ages/2 has no local return
dialsample.erl:11: The call erlang:&#39;++&#39;(Ages1::20,Ages2::25) will never return since it differs in the
  1st argument from the success typing arguments: ([any()],any())</code></pre></div>
<p>Dialyzer always errs on the side of caution though, so as not to provide false-positive warnings. In other words, you get the most bang for your buck if you add specs to as much of your codebase as possible. The more you do, the more accurate and helpful Dialyzer becomes.</p>
<ul>
<li><a href="http://erlang.org/doc/reference_manual/typespec.html"  target="_blank" rel="noreferrer">Types and Function Specifications</a> (official docs)</li>
<li><a href="http://learnyousomeerlang.com/types-or-lack-thereof"  target="_blank" rel="noreferrer">Types (or lack thereof)</a> (Learn You Some Erlang)</li>
</ul>

<h2 class="relative group">Records
    <div id="records" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#records" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="http://erlang.org/doc/reference_manual/records.html"  target="_blank" rel="noreferrer">Records</a> are glorified tuples, but provide some real benefits over a simple tuple. You can group information together under a name (the name of the record, also the first element of the tuple) and access all the fields in it by name too.</p>
<p>I suggest using a record whenever you identify a few parameters that all seem to be related. It&rsquo;s somewhat analogous to grouping fields together into a class in other languages.</p>
<p>Let&rsquo;s look at a small module that defines a record and then acts on it. Notice how it&rsquo;s defined, then used in <code>generate_employee()</code>, and then accessed in the last two functions. The alternative would be to pass around all those individual fields as separate ungrouped fields, but that could become a maintenance nightmare quickly.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">module</span><span class="p">(</span><span class="n">person</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">record</span><span class="p">(</span><span class="nl">employee</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span> <span class="n">first_name</span> <span class="p">::</span> <span class="n">string</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">          <span class="n">last_name</span> <span class="p">::</span> <span class="n">string</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">          <span class="n">hire_date</span> <span class="p">::</span> <span class="n">tuple</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">          <span class="n">active</span> <span class="p">::</span> <span class="n">boolean</span><span class="p">()</span> <span class="p">}).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">export</span><span class="p">([</span><span class="n">generate_employee</span><span class="o">/</span><span class="mi">0</span><span class="p">,</span> <span class="n">get_name</span><span class="o">/</span><span class="mi">1</span><span class="p">,</span> <span class="n">get_active_employees</span><span class="o">/</span><span class="mi">1</span><span class="p">]).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">generate_employee</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="nl">#employee</span><span class="p">{}.</span>
</span></span><span class="line"><span class="cl"><span class="nf">generate_employee</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nl">#employee</span> <span class="p">{</span> <span class="n">first_name</span> <span class="o">=</span> <span class="s">&#34;Jane&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">last_name</span> <span class="o">=</span> <span class="s">&#34;Doe&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">hire_date</span> <span class="o">=</span> <span class="p">{{</span><span class="mi">2017</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span><span class="mi">31</span><span class="p">},{</span><span class="mi">6</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">55</span><span class="p">}},</span>
</span></span><span class="line"><span class="cl">                <span class="n">active</span> <span class="o">=</span> <span class="n">true</span> <span class="p">}.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">get_name</span><span class="p">(</span><span class="nl">#employee</span><span class="p">{})</span> <span class="o">-&gt;</span> <span class="n">string</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_name</span><span class="p">(</span><span class="nv">Employee</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nn">io</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="nv">Employee</span><span class="nl">#employee.first_name</span> <span class="o">++</span> <span class="s">&#34; &#34;</span> <span class="o">++</span> <span class="nv">Employee</span><span class="nl">#employee.last_name</span> <span class="o">++</span> <span class="s">&#34;</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">get_active_employees</span><span class="p">([</span><span class="nl">#employee</span><span class="p">{}])</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="nl">#employee</span><span class="p">{}].</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_active_employees</span><span class="p">(</span><span class="nv">Employees</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nn">lists</span><span class="p">:</span><span class="nf">filter</span><span class="p">(</span><span class="k">fun</span><span class="p">(</span><span class="nv">Employee</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nv">Employee</span><span class="nl">#employee.active</span> <span class="o">=:=</span> <span class="n">true</span> <span class="k">end</span><span class="p">,</span> <span class="nv">Employees</span><span class="p">).</span></span></span></code></pre></div></div>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">10&gt; c(person).
{ok,person}
11&gt; person:get_name(person:generate_employee()).
Jane Doe
ok
12&gt; person:get_active_employees([person:generate_employee()]).
[{employee,&#34;Jane&#34;,&#34;Doe&#34;,{{2017,10,31},{6,1,55}},true}]</code></pre></div>
<ul>
<li><a href="http://erlang.org/doc/programming_examples/records.html"  target="_blank" rel="noreferrer">Records: Programming Examples</a> (official docs)</li>
<li><a href="http://learnyousomeerlang.com/a-short-visit-to-common-data-structures#records"  target="_blank" rel="noreferrer">A Short Visit to Common Data Structures: Records</a> (Learn You Some Erlang)</li>
</ul>

<h2 class="relative group">EDoc
    <div id="edoc" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#edoc" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you&rsquo;ve been a programmer for longer than a few months you know we love to argue about certain things. Tabs vs spaces for example. I say one letter per line.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="https://imgs.xkcd.com/comics/third_way.png"
    ></figure>
<p><a href="https://xkcd.com/1285"  target="_blank" rel="noreferrer"><em>https://xkcd.com/1285</em></a></p>
<p>The necessity of comments is another sticking point. Some love them, some think they&rsquo;re evil incarnate. Personally, I think comments are fine, but that the &ldquo;why&rdquo; of the code is more important than the &ldquo;what&rdquo;. Not having any comments <em>anywhere</em>, no documentation or anything, is less than ideal. Write a large chunk of code, then check it in with a dozen other programmer&rsquo;s large chunks of code, then step away from the thing for a few months and try to remember what you did and why.</p>
<p>Erlang has a documentation system called <a href="http://erlang.org/doc/apps/edoc/chapter.html"  target="_blank" rel="noreferrer">EDoc</a>, which allows you document your code and even generate HTML page from it. Here&rsquo;s the previous example again, this time with comments added.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="c">%% @author Grant Winney
</span></span></span><span class="line"><span class="cl"><span class="c">%% @copyright 2017 Yours Truly
</span></span></span><span class="line"><span class="cl"><span class="c">%% @reference See &lt;a href=&#34;http://homestarrunner.com&#34;&gt;HomestarRunner&lt;/a&gt; with any questions.
</span></span></span><span class="line"><span class="cl"><span class="c">%% @version 42
</span></span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">module</span><span class="p">(</span><span class="n">person</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">record</span><span class="p">(</span><span class="nl">employee</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span> <span class="n">first_name</span> <span class="p">::</span> <span class="n">string</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">          <span class="n">last_name</span> <span class="p">::</span> <span class="n">string</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">          <span class="n">hire_date</span> <span class="p">::</span> <span class="n">tuple</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">          <span class="n">active</span> <span class="p">::</span> <span class="n">boolean</span><span class="p">()</span> <span class="p">}).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">export</span><span class="p">([</span><span class="n">generate_employee</span><span class="o">/</span><span class="mi">0</span><span class="p">,</span> <span class="n">get_name</span><span class="o">/</span><span class="mi">1</span><span class="p">,</span> <span class="n">get_full_name</span><span class="o">/</span><span class="mi">1</span><span class="p">,</span> <span class="n">get_active_employees</span><span class="o">/</span><span class="mi">1</span><span class="p">]).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">generate_employee</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="nl">#employee</span><span class="p">{}.</span>
</span></span><span class="line"><span class="cl"><span class="nf">generate_employee</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nl">#employee</span> <span class="p">{</span> <span class="n">first_name</span> <span class="o">=</span> <span class="s">&#34;Jane&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">last_name</span> <span class="o">=</span> <span class="s">&#34;Doe&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">hire_date</span> <span class="o">=</span> <span class="p">{{</span><span class="mi">2017</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span><span class="mi">31</span><span class="p">},{</span><span class="mi">6</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">55</span><span class="p">}},</span>
</span></span><span class="line"><span class="cl">                <span class="n">active</span> <span class="o">=</span> <span class="n">true</span> <span class="p">}.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">%% @deprecated Use get_full_name going forward. Kthxbye.
</span></span></span><span class="line"><span class="cl"><span class="c">%% @equiv person:get_full_name(Employee)
</span></span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">get_name</span><span class="p">(</span><span class="nl">#employee</span><span class="p">{})</span> <span class="o">-&gt;</span> <span class="n">string</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_name</span><span class="p">(</span><span class="nv">Employee</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nn">io</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="nv">Employee</span><span class="nl">#employee.first_name</span> <span class="o">++</span> <span class="s">&#34; &#34;</span> <span class="o">++</span> <span class="nv">Employee</span><span class="nl">#employee.last_name</span> <span class="o">++</span> <span class="s">&#34;</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">%% @doc Get the first and last names of an employee.
</span></span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">get_full_name</span><span class="p">(</span><span class="nl">#employee</span><span class="p">{})</span> <span class="o">-&gt;</span> <span class="n">string</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_full_name</span><span class="p">(</span><span class="nv">Employee</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nn">io</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="nv">Employee</span><span class="nl">#employee.first_name</span> <span class="o">++</span> <span class="s">&#34; &#34;</span> <span class="o">++</span> <span class="nv">Employee</span><span class="nl">#employee.last_name</span> <span class="o">++</span> <span class="s">&#34;</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">get_active_employees</span><span class="p">([</span><span class="nl">#employee</span><span class="p">{}])</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="nl">#employee</span><span class="p">{}].</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_active_employees</span><span class="p">(</span><span class="nv">Employees</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nn">lists</span><span class="p">:</span><span class="nf">filter</span><span class="p">(</span><span class="k">fun</span><span class="p">(</span><span class="nv">Employee</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nv">Employee</span><span class="nl">#employee.active</span> <span class="o">=:=</span> <span class="n">true</span> <span class="k">end</span><span class="p">,</span> <span class="nv">Employees</span><span class="p">).</span></span></span></code></pre></div></div>
<p>Once you&rsquo;ve added comments, you can easily generate an HTML document from the erl shell with a one-liner, and the results are pretty good. Compare the page below to the comments above - is everything there?</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="nn">edoc</span><span class="p">:</span><span class="nf">files</span><span class="p">([</span><span class="s">&#34;person.erl&#34;</span><span class="p">]).</span></span></span></code></pre></div></div>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/taming-the-erlang-beast/edoc.png"
    width="1290"
      height="1504"></figure>
<p>I really like the &ldquo;Learn You Some Erlang&rdquo; site, but unfortunately there&rsquo;s nothing on there about EDoc! Lots of info in the official docs though.</p>
<ul>
<li><a href="http://erlang.org/doc/apps/edoc/chapter.html"  target="_blank" rel="noreferrer">Welcome to EDoc</a></li>
<li><a href="http://erlang.org/doc/apps/edoc/"  target="_blank" rel="noreferrer">EDoc Reference Manual</a></li>
</ul>

<h2 class="relative group">EUnit
    <div id="eunit" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#eunit" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>You should strive to write tests for all your code. Well, as much as it makes sense. And not because someone wrote about it in a book or sold you on it at a conference, but because it just makes good sense. Once you write some code, or refactor it, or change any code <em>around</em> it, you should test everything to make sure it still works. Do you want to test it manually every time, or would you rather it happens automatically? Please tell me you said automatically.</p>
<p>Erlang comes with a unit testing suite called EUnit, which you can use to test isolated blocks of your code. It&rsquo;s similar in vein to testing suites used by other languages, like NUnit, JUnit, xUnit (see a pattern yet?), etc.</p>
<p>Here&rsquo;s the same code as before, stripped of all the EDoc and spec stuff, but with EUnit tests now. The first test is making sure employees&rsquo; full names are returned as expected, while the second makes sure that only active employees are returned.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">module</span><span class="p">(</span><span class="n">eunitsample</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">record</span><span class="p">(</span><span class="nl">employee</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span> <span class="n">first_name</span> <span class="p">::</span> <span class="n">string</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">          <span class="n">last_name</span> <span class="p">::</span> <span class="n">string</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">          <span class="n">hire_date</span> <span class="p">::</span> <span class="n">tuple</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">          <span class="n">active</span> <span class="p">::</span> <span class="n">boolean</span><span class="p">()</span> <span class="p">}).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">export</span><span class="p">([</span><span class="n">generate_employee</span><span class="o">/</span><span class="mi">0</span><span class="p">,</span> <span class="n">get_name</span><span class="o">/</span><span class="mi">1</span><span class="p">,</span> <span class="n">get_active_employees</span><span class="o">/</span><span class="mi">1</span><span class="p">]).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">generate_employee</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nl">#employee</span> <span class="p">{</span> <span class="n">first_name</span> <span class="o">=</span> <span class="s">&#34;Jane&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">last_name</span> <span class="o">=</span> <span class="s">&#34;Doe&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">hire_date</span> <span class="o">=</span> <span class="p">{{</span><span class="mi">2017</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span><span class="mi">31</span><span class="p">},{</span><span class="mi">6</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">55</span><span class="p">}},</span>
</span></span><span class="line"><span class="cl">                <span class="n">active</span> <span class="o">=</span> <span class="n">true</span> <span class="p">}.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">get_name</span><span class="p">(</span><span class="nv">Employee</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Employee</span><span class="nl">#employee.first_name</span> <span class="o">++</span> <span class="s">&#34; &#34;</span> <span class="o">++</span> <span class="nv">Employee</span><span class="nl">#employee.last_name</span><span class="p">.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">get_active_employees</span><span class="p">(</span><span class="nv">Employees</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nn">lists</span><span class="p">:</span><span class="nf">filter</span><span class="p">(</span><span class="k">fun</span><span class="p">(</span><span class="nv">Employee</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nv">Employee</span><span class="nl">#employee.active</span> <span class="o">=:=</span> <span class="n">true</span> <span class="k">end</span><span class="p">,</span> <span class="nv">Employees</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">%%%%%%%%%%%%%%%%
</span></span></span><span class="line"><span class="cl"><span class="c">%% EUNIT TESTS
</span></span></span><span class="line"><span class="cl"><span class="c">%%%%%%%%%%%%%%%%
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">include_lib</span><span class="p">(</span><span class="s">&#34;eunit/include/eunit.hrl&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">get_name_test_</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="o">?</span><span class="p">_</span><span class="n">assertEqual</span><span class="p">(</span><span class="s">&#34;Jack Bauer&#34;</span><span class="p">,</span> <span class="n">get_name</span><span class="p">(</span><span class="nl">#employee</span> <span class="p">{</span> <span class="n">first_name</span> <span class="o">=</span> <span class="s">&#34;Jack&#34;</span><span class="p">,</span> <span class="n">last_name</span> <span class="o">=</span> <span class="s">&#34;Bauer&#34;</span> <span class="p">})),</span>
</span></span><span class="line"><span class="cl">        <span class="o">?</span><span class="p">_</span><span class="n">assertEqual</span><span class="p">(</span><span class="s">&#34;Diana Prince&#34;</span><span class="p">,</span> <span class="n">get_name</span><span class="p">(</span><span class="nl">#employee</span> <span class="p">{</span> <span class="n">first_name</span> <span class="o">=</span> <span class="s">&#34;Diana&#34;</span><span class="p">,</span> <span class="n">last_name</span> <span class="o">=</span> <span class="s">&#34;Prince&#34;</span> <span class="p">}))</span>
</span></span><span class="line"><span class="cl">    <span class="p">].</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">get_active_employees_returns_active_employee_test</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Employees</span> <span class="o">=</span> <span class="p">[</span><span class="nl">#employee</span> <span class="p">{</span> <span class="n">first_name</span> <span class="o">=</span> <span class="s">&#34;Someone1&#34;</span><span class="p">,</span> <span class="n">active</span> <span class="o">=</span> <span class="n">true</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">                 <span class="nl">#employee</span> <span class="p">{</span> <span class="n">first_name</span> <span class="o">=</span> <span class="s">&#34;Someone2&#34;</span><span class="p">,</span> <span class="n">active</span> <span class="o">=</span> <span class="n">false</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">                 <span class="nl">#employee</span> <span class="p">{</span> <span class="n">first_name</span> <span class="o">=</span> <span class="s">&#34;Someone3&#34;</span><span class="p">,</span> <span class="n">active</span> <span class="o">=</span> <span class="n">true</span> <span class="p">}],</span>
</span></span><span class="line"><span class="cl">    <span class="nv">ActiveEmployees</span> <span class="o">=</span> <span class="n">get_active_employees</span><span class="p">(</span><span class="nv">Employees</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c">%% we should only get the two &#34;active&#34; records back
</span></span></span><span class="line"><span class="cl">    <span class="o">?</span><span class="n">assertEqual</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="nb">length</span><span class="p">(</span><span class="nv">ActiveEmployees</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c">%% ...which should include &#34;Someone1&#34; and &#34;Someone3&#34;
</span></span></span><span class="line"><span class="cl">    <span class="o">?</span><span class="n">assertNotEqual</span><span class="p">(</span><span class="n">false</span><span class="p">,</span> <span class="nn">lists</span><span class="p">:</span><span class="nf">keyfind</span><span class="p">(</span><span class="s">&#34;Someone1&#34;</span><span class="p">,</span> <span class="nl">#employee.first_name</span><span class="p">,</span> <span class="nv">ActiveEmployees</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">    <span class="o">?</span><span class="n">assertNotEqual</span><span class="p">(</span><span class="n">false</span><span class="p">,</span> <span class="nn">lists</span><span class="p">:</span><span class="nf">keyfind</span><span class="p">(</span><span class="s">&#34;Someone3&#34;</span><span class="p">,</span> <span class="nl">#employee.first_name</span><span class="p">,</span> <span class="nv">ActiveEmployees</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c">%% ...but not &#34;Someone2&#34;
</span></span></span><span class="line"><span class="cl">    <span class="o">?</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">false</span><span class="p">,</span> <span class="nn">lists</span><span class="p">:</span><span class="nf">keyfind</span><span class="p">(</span><span class="s">&#34;Someone2&#34;</span><span class="p">,</span> <span class="nl">#employee.first_name</span><span class="p">,</span> <span class="nv">ActiveEmployees</span><span class="p">)).</span></span></span></code></pre></div></div>
<p>To run these tests, just pop into an erl shell, recompile and run them:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">&gt; c(eunitsample).
{ok,eunitsample}
&gt; eunit:test(eunitsample).
  All 3 tests passed.
ok</code></pre></div>
<p>Tests are a great form of documentation. Sure, you can document with EDoc what these functions do and why they exist, but that doesn&rsquo;t stop bugs. Now if anyone changes <code>get_name</code> to return only a first name, or adds an additional filter to <code>get_active_employees</code> that changes the results, the tests will fail. And this is HUGE in a language like Erlang, where nearly anything that might go wrong will do so at runtime.</p>
<ul>
<li><a href="http://erlang.org/doc/apps/eunit/chapter.html"  target="_blank" rel="noreferrer">EUnit - a Lightweight Unit Testing Framework for Erlang</a> (official docs)</li>
<li><a href="http://learnyousomeerlang.com/eunit"  target="_blank" rel="noreferrer">EUnit: The need for tests</a> (Learn You Some Erlang)</li>
</ul>

<h1 class="relative group">Did I miss anything?
    <div id="did-i-miss-anything" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#did-i-miss-anything" aria-label="Anchor">#</a>
    </span>
    
</h1>
<p>If you use any of these - or hopefully all of them! - you should find that the wild Erlang beast is greatly tamed. You&rsquo;ll have cleaner, more predictable, less buggy code.</p>
<p>These certainly aren&rsquo;t all the tools in an Erlang developer&rsquo;s toolbox. What else do you like to use? Share your thoughts below so I can learn more too!</p>
]]></content:encoded><media:content url="https://grantwinney.com/taming-the-erlang-beast/feature.webp" medium="image" type="image/webp"/></item><item><title>Concatenate Binaries and Strings in Erlang</title><link>https://grantwinney.com/erlang-concatenate-binaries-and-strings/</link><pubDate>Tue, 26 Sep 2017 16:09:00 +0000</pubDate><guid>https://grantwinney.com/erlang-concatenate-binaries-and-strings/</guid><description>Concatenating strings and binaries in Erlang can get ugly quick. Let&amp;rsquo;s make it easier.</description><content:encoded><![CDATA[
<h2 class="relative group">The Problem
    <div id="the-problem" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-problem" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There have been a number of times when using Erlang that I&rsquo;ve found myself concatenating a list of binaries and strings. I usually resort to manual conversions one way or the other&hellip; and I think you&rsquo;ll agree they&rsquo;re both pretty ugly.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="o">&lt;&lt;</span> <span class="o">&lt;&lt;</span><span class="s">&#34;One&#34;</span><span class="o">&gt;&gt;/</span><span class="n">binary</span><span class="p">,</span> <span class="p">(</span><span class="nb">list_to_binary</span><span class="p">(</span><span class="s">&#34; Two &#34;</span><span class="p">))</span><span class="o">/</span><span class="n">binary</span><span class="p">,</span> <span class="o">&lt;&lt;</span><span class="s">&#34;Three&#34;</span><span class="o">&gt;&gt;/</span><span class="n">binary</span><span class="p">,</span> <span class="p">(</span><span class="nb">list_to_binary</span><span class="p">(</span><span class="s">&#34; Four!&#34;</span><span class="p">))</span><span class="o">/</span><span class="n">binary</span> <span class="o">&gt;&gt;</span><span class="p">.</span>
</span></span><span class="line"><span class="cl"><span class="c">% &lt;&lt;&#34;One Two Three Four!&#34;&gt;&gt;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">binary_to_list</span><span class="p">(</span><span class="o">&lt;&lt;</span><span class="s">&#34;One&#34;</span><span class="o">&gt;&gt;</span><span class="p">)</span> <span class="o">++</span> <span class="s">&#34; Two &#34;</span> <span class="o">++</span> <span class="nb">binary_to_list</span><span class="p">(</span><span class="o">&lt;&lt;</span><span class="s">&#34;Three&#34;</span><span class="o">&gt;&gt;</span><span class="p">)</span> <span class="o">++</span> <span class="s">&#34; Four!&#34;</span><span class="p">.</span>
</span></span><span class="line"><span class="cl"><span class="err">%</span> <span class="s">&#34;One Two Three Four!&#34;</span></span></span></code></pre></div></div>
<p>There&rsquo;s a helpful <a href="http://erlang.org/doc/man/lists.html#concat-1"  target="_blank" rel="noreferrer">lists:concat</a> function that quickly converts a list of elements to a single string. Unfortunately, the list of allowed types doesn&rsquo;t include binaries.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="nn">lists</span><span class="p">:</span><span class="nf">concat</span><span class="p">([</span><span class="s">&#34;one&#34;</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="n">asdf</span><span class="p">,</span><span class="mi">1</span><span class="p">.</span><span class="mi">25</span><span class="p">]).</span>
</span></span><span class="line"><span class="cl"><span class="err">%</span> <span class="s">&#34;one5asdf1.25000000000000000000e+00&#34;</span></span></span></code></pre></div></div>

<h2 class="relative group">The Solution
    <div id="the-solution" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-solution" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Here&rsquo;s a small snippet with some eunit tests that&rsquo;ll convert binaries to strings, run them through the <code>lists:concat</code> function, then return the binary or string you asked for.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">module</span><span class="p">(</span><span class="n">utils</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">export</span><span class="p">([</span><span class="n">concat</span><span class="o">/</span><span class="mi">2</span><span class="p">]).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">%% EXTERNAL
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">concat</span><span class="p">(</span><span class="nv">Words</span><span class="p">,</span> <span class="n">string</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="n">internal_concat</span><span class="p">(</span><span class="nv">Words</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">concat</span><span class="p">(</span><span class="nv">Words</span><span class="p">,</span> <span class="n">binary</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">list_to_binary</span><span class="p">(</span><span class="n">internal_concat</span><span class="p">(</span><span class="nv">Words</span><span class="p">)).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">%% INTERNAL
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">internal_concat</span><span class="p">(</span><span class="nv">Elements</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">NonBinaryElements</span> <span class="o">=</span> <span class="p">[</span><span class="k">case</span> <span class="nv">Element</span> <span class="k">of</span> <span class="p">_</span> <span class="k">when</span> <span class="nb">is_binary</span><span class="p">(</span><span class="nv">Element</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">binary_to_list</span><span class="p">(</span><span class="nv">Element</span><span class="p">);</span> <span class="p">_</span> <span class="o">-&gt;</span> <span class="nv">Element</span> <span class="k">end</span> <span class="p">||</span> <span class="nv">Element</span> <span class="o">&lt;-</span> <span class="nv">Elements</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nn">lists</span><span class="p">:</span><span class="nf">concat</span><span class="p">(</span><span class="nv">NonBinaryElements</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">%% EUNIT TESTS
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">include_lib</span><span class="p">(</span><span class="s">&#34;eunit/include/eunit.hrl&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">concat_conversion_test_</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="s">&#34;list of strings to string&#34;</span><span class="p">,</span> <span class="o">?</span><span class="p">_</span><span class="n">assertEqual</span><span class="p">(</span><span class="s">&#34;This and that.&#34;</span><span class="p">,</span> <span class="nn">utils</span><span class="p">:</span><span class="nf">concat</span><span class="p">([</span><span class="s">&#34;This&#34;</span><span class="p">,</span> <span class="s">&#34; and&#34;</span><span class="p">,</span> <span class="s">&#34; that.&#34;</span><span class="p">],</span> <span class="n">string</span><span class="p">))},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="s">&#34;list of strings to binary&#34;</span><span class="p">,</span> <span class="o">?</span><span class="p">_</span><span class="n">assertEqual</span><span class="p">(</span><span class="o">&lt;&lt;</span><span class="s">&#34;This and that.&#34;</span><span class="o">&gt;&gt;</span><span class="p">,</span> <span class="nn">utils</span><span class="p">:</span><span class="nf">concat</span><span class="p">([</span><span class="s">&#34;This&#34;</span><span class="p">,</span> <span class="s">&#34; and&#34;</span><span class="p">,</span> <span class="s">&#34; that.&#34;</span><span class="p">],</span> <span class="n">binary</span><span class="p">))},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="s">&#34;list of binaries to string&#34;</span><span class="p">,</span> <span class="o">?</span><span class="p">_</span><span class="n">assertEqual</span><span class="p">(</span><span class="s">&#34;This and that.&#34;</span><span class="p">,</span> <span class="nn">utils</span><span class="p">:</span><span class="nf">concat</span><span class="p">([</span><span class="o">&lt;&lt;</span><span class="s">&#34;This&#34;</span><span class="o">&gt;&gt;</span><span class="p">,</span> <span class="o">&lt;&lt;</span><span class="s">&#34; and&#34;</span><span class="o">&gt;&gt;</span><span class="p">,</span> <span class="o">&lt;&lt;</span><span class="s">&#34; that.&#34;</span><span class="o">&gt;&gt;</span><span class="p">],</span> <span class="n">string</span><span class="p">))},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="s">&#34;list of binaries to binary&#34;</span><span class="p">,</span> <span class="o">?</span><span class="p">_</span><span class="n">assertEqual</span><span class="p">(</span><span class="o">&lt;&lt;</span><span class="s">&#34;This and that.&#34;</span><span class="o">&gt;&gt;</span><span class="p">,</span> <span class="nn">utils</span><span class="p">:</span><span class="nf">concat</span><span class="p">([</span><span class="o">&lt;&lt;</span><span class="s">&#34;This&#34;</span><span class="o">&gt;&gt;</span><span class="p">,</span> <span class="o">&lt;&lt;</span><span class="s">&#34; and&#34;</span><span class="o">&gt;&gt;</span><span class="p">,</span> <span class="o">&lt;&lt;</span><span class="s">&#34; that.&#34;</span><span class="o">&gt;&gt;</span><span class="p">],</span> <span class="n">binary</span><span class="p">))},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="s">&#34;mix of values to string&#34;</span><span class="p">,</span> <span class="o">?</span><span class="p">_</span><span class="n">assertEqual</span><span class="p">(</span><span class="s">&#34;This and that 5asdf hi.&#34;</span><span class="p">,</span> <span class="nn">utils</span><span class="p">:</span><span class="nf">concat</span><span class="p">([</span><span class="o">&lt;&lt;</span><span class="s">&#34;This&#34;</span><span class="o">&gt;&gt;</span><span class="p">,</span> <span class="s">&#34; and&#34;</span><span class="p">,</span> <span class="o">&lt;&lt;</span><span class="s">&#34; that &#34;</span><span class="o">&gt;&gt;</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="n">asdf</span><span class="p">,</span> <span class="s">&#34; hi.&#34;</span><span class="p">],</span> <span class="n">string</span><span class="p">))},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="s">&#34;mix of values to binary&#34;</span><span class="p">,</span> <span class="o">?</span><span class="p">_</span><span class="n">assertEqual</span><span class="p">(</span><span class="o">&lt;&lt;</span><span class="s">&#34;This and that 5asdf hi.&#34;</span><span class="o">&gt;&gt;</span><span class="p">,</span> <span class="nn">utils</span><span class="p">:</span><span class="nf">concat</span><span class="p">([</span><span class="o">&lt;&lt;</span><span class="s">&#34;This&#34;</span><span class="o">&gt;&gt;</span><span class="p">,</span> <span class="s">&#34; and&#34;</span><span class="p">,</span> <span class="o">&lt;&lt;</span><span class="s">&#34; that &#34;</span><span class="o">&gt;&gt;</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="n">asdf</span><span class="p">,</span> <span class="s">&#34; hi.&#34;</span><span class="p">],</span> <span class="n">binary</span><span class="p">))}</span>
</span></span><span class="line"><span class="cl">    <span class="p">].</span></span></span></code></pre></div></div>

<h2 class="relative group">Usage
    <div id="usage" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#usage" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>To use it, just pass a list of any element types that <code>lists:concat</code> would normally allow, as well as binaries.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="nn">utils</span><span class="p">:</span><span class="nf">concat</span><span class="p">([</span><span class="o">&lt;&lt;</span><span class="s">&#34;One&#34;</span><span class="o">&gt;&gt;</span><span class="p">,</span> <span class="s">&#34; two &#34;</span><span class="p">,</span> <span class="n">three</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">1</span><span class="p">.</span><span class="mi">25</span><span class="p">],</span> <span class="n">binary</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="c">% &lt;&lt;&#34;One two three51.25000000000000000000e+00&#34;&gt;&gt;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nn">utils</span><span class="p">:</span><span class="nf">concat</span><span class="p">([</span><span class="o">&lt;&lt;</span><span class="s">&#34;One&#34;</span><span class="o">&gt;&gt;</span><span class="p">,</span> <span class="s">&#34; two &#34;</span><span class="p">,</span> <span class="n">three</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">1</span><span class="p">.</span><span class="mi">25</span><span class="p">],</span> <span class="n">string</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="err">%</span> <span class="s">&#34;One two three51.25000000000000000000e+00&#34;</span></span></span></code></pre></div></div>
<p>If you find a better way to do this, let me know!</p>
]]></content:encoded><media:content url="https://grantwinney.com/erlang-concatenate-binaries-and-strings/feature.webp" medium="image" type="image/webp"/></item><item><title>Creating Your First Chrome Extension</title><link>https://grantwinney.com/making-your-first-chrome-extension/</link><pubDate>Wed, 16 Aug 2017 10:27:33 +0000</pubDate><guid>https://grantwinney.com/making-your-first-chrome-extension/</guid><description>We all have our favorite web browser with our favorite extensions loaded, but have you ever considered writing your own? In the past few months I&amp;rsquo;ve created a couple extensions to suit my own needs. Here&amp;rsquo;s what I&amp;rsquo;ve learned!</description><content:encoded><![CDATA[<p>We all have our favorite web browser with our favorite extensions loaded, but have you ever considered writing your own? You&rsquo;ve probably had at least one idea for <em>something</em> that it&rsquo;d be nice to have, but there&rsquo;s nothing out there that quite does what you&rsquo;re looking for.</p>
<p>As technology becomes more and more a part of our everyday lives, it&rsquo;s useful to understand how to manipulate it to suit our individual needs. These days, knowing at least a little coding is like learning word processing or writing a basic html page was 15 years ago. It&rsquo;s becoming a basic computer skill.</p>
<p>In the past few months I&rsquo;ve created a couple extensions to suit my own needs - unpolished but functional - and threw them in the Chrome Web Store just in case someone out there found them useful. Here&rsquo;s what I&rsquo;ve learned.</p>

<h2 class="relative group">Hello World!
    <div id="hello-world" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#hello-world" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Let&rsquo;s start with the easiest thing possible.</p>
<p>The most important file in a Chrome extension is the <code>manifest.json</code> file. It specifies everything about your extension, from meta information (name, author, version) to requested permissions, the locations of scripts, icons to display in the browser, and much more. You can <a href="https://developer.chrome.com/extensions/manifest"  target="_blank" rel="noreferrer">read more about the many settings here</a>, but you might want to wait until the end of this post, unless you just want to delve right into the deep end!</p>
<p>Here&rsquo;s a bare-bones <code>manifest.json</code> file for our very first &ldquo;Hello World&rdquo; extension. The first three lines are required, and the extension won&rsquo;t load without them - leave them be for now. The &ldquo;background&rdquo; field specifies a script to load, and the empty &ldquo;browser_action&rdquo; lets Chrome know there&rsquo;s no html page to display (pop up) when the extension icon is clicked (more on that later). Create a folder and copy this into a file named &ldquo;manifest.json&rdquo;.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;My First Extension&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;1.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;manifest_version&#34;</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;background&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;scripts&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;helloworld.js&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;browser_action&#34;</span><span class="p">:</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Create a second file and name it &ldquo;helloworld.js&rdquo;, then paste the following into it. All we&rsquo;re doing is listening for the <code>browserAction.onClicked</code> event, and when it fires (when you click the icon for your extension) we can take some action.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">chrome</span><span class="p">.</span><span class="nx">browserAction</span><span class="p">.</span><span class="nx">onClicked</span><span class="p">.</span><span class="nx">addListener</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">tab</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">alert</span><span class="p">(</span><span class="s1">&#39;HELLOOOOO WORLD!!&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span></span></span></code></pre></div></div>
<p>Now let&rsquo;s try it out&hellip;</p>

<h2 class="relative group">Testing an extension
    <div id="testing-an-extension" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#testing-an-extension" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Chrome makes it easy to test your extensions locally.</p>
<p>Open the &ldquo;extensions&rdquo; pane where you&rsquo;d normally look for new extensions, and then select the &ldquo;Developer Mode&rdquo; checkbox in the corner. New options appear, allowing you to load your extension and run it.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/making-your-first-chrome-extension/chrome-dev-1.png"
    width="753"
      height="104"></figure>
<p>Press the &ldquo;Load unpacked extension&hellip;&rdquo; button and navigate to the folder where you stored the two files you just created. Select the folder and press &ldquo;Select&rdquo;.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/making-your-first-chrome-extension/chrome-dev-2.png"
    width="945"
      height="400"></figure>
<p>Look for the new generic icon (since we didn&rsquo;t specify an icon for it to use) in the upper-right corner, and press it. This fires our <code>browserAction.onClicked</code> event and executes our code - in this case a simple alert box.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/making-your-first-chrome-extension/chrome-dev-3.png"
    width="212"
      height="79"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/making-your-first-chrome-extension/chrome-dev-4.png"
    width="477"
      height="213"></figure>
<p>If there’s anything wrong with your manifest.json file, or you’ve pointed to a resource that’s missing or inaccessible, you&rsquo;ll see an error section like this:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="chrome-dev-mode-warnings"
    src="/making-your-first-chrome-extension/chrome-dev-mode-warnings.png"
    width="1496"
      height="988"></figure>
<p>Fix your mistakes and click the “Reload” link to try again.</p>

<h2 class="relative group">Hello World! (popup edition)
    <div id="hello-world-popup-edition" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#hello-world-popup-edition" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Sometimes, having an extension do only one thing may be exactly what you want. Most of the time though, you&rsquo;ll probably want to display your own HTML page when a user clicks your extension icon. So let&rsquo;s try that next.</p>
<p>Open the <code>manifest.json</code> file, then take out the background script and give it a page to show instead.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;My Second Extension&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;1.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;manifest_version&#34;</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;browser_action&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;default_popup&#34;</span><span class="p">:</span> <span class="s2">&#34;popup.html&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Create an html page named &ldquo;popup.html&rdquo; with some <em>really</em> advanced markup:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span>HELLO WORLD!!<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p>On the extensions page, find your extension (it should be at the top of the list) and look for the &ldquo;Reload&rdquo; link. Click that, then click on the extension icon again.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="chrome-dev-5"
    src="/making-your-first-chrome-extension/chrome-dev-5.png"
    width="1472"
      height="314"></figure>
<p>You should see the HTML page you just created. It&rsquo;s a thing of beauty, right? ;)</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/making-your-first-chrome-extension/chrome-dev-6.png"
    width="320"
      height="276"></figure>

<h3 class="relative group">Generate an email
    <div id="generate-an-email" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#generate-an-email" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>You can add any HTML you want in there. How about a box where someone can create an email? Change the HTML file to include a few relevant fields and a submit button:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;script.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">body</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;width:200px&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>Email Someone!<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">p</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;font-size:smaller&#34;</span><span class="p">&gt;&lt;</span><span class="nt">em</span><span class="p">&gt;</span>(not as sophisticated as it sounds...)<span class="p">&lt;/</span><span class="nt">em</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>Email: <span class="p">&lt;</span><span class="nt">input</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;emailAddress&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>Subject: <span class="p">&lt;</span><span class="nt">input</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;emailSubject&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>Body: <span class="p">&lt;</span><span class="nt">textarea</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;emailBody&#34;</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;min-height:50px;&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">textarea</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;&lt;</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;submit&#34;</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;send&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p>You can use Javascript to handle what happens when the submit button is pressed. Create the &ldquo;script.js&rdquo; file referenced above, and paste this code into it. <em>(I registered the button&rsquo;s</em> <em><code>_onclick_</code></em> <em>event inside the page&rsquo;s</em> <em><code>_load_</code></em> <em>event to make sure that the page is fully loaded before trying to access elements on the page.)</em></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;load&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="nx">load</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">&#39;send&#39;</span><span class="p">).</span><span class="nx">onclick</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">var</span> <span class="nx">email</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">&#39;emailAddress&#39;</span><span class="p">).</span><span class="nx">value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="kd">var</span> <span class="nx">subject</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">&#39;emailSubject&#39;</span><span class="p">).</span><span class="nx">value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="kd">var</span> <span class="nx">body</span> <span class="o">=</span> <span class="nb">encodeURIComponent</span><span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">&#39;emailBody&#39;</span><span class="p">).</span><span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nb">window</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="sb">`mailto:</span><span class="si">${</span><span class="nx">email</span><span class="si">}</span><span class="sb">?subject=</span><span class="si">${</span><span class="nx">subject</span><span class="si">}</span><span class="sb">&amp;body=</span><span class="si">${</span><span class="nx">body</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span></span></span></code></pre></div></div>
<p>Click the icon again to bring up your new form, then fill in the fields and hit enter. Assuming you have a default mail client that handles &ldquo;mailto&rdquo; links (like gmail), it should open a new email for you.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/making-your-first-chrome-extension/chrome-dev-5-1.png"
    width="498"
      height="534"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/making-your-first-chrome-extension/chrome-dev-6-1.png"
    width="632"
      height="454"></figure>

<h3 class="relative group">Change the color of the current page
    <div id="change-the-color-of-the-current-page" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#change-the-color-of-the-current-page" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Alright, one more example. Say you&rsquo;d like a series of buttons to do different things - you can do that too. Add a new section to the <code>manifest.json</code> file to specify a permission, so that the file looks like this:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Button it up!&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;1.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;manifest_version&#34;</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;browser_action&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;default_popup&#34;</span><span class="p">:</span> <span class="s2">&#34;popup.html&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;permissions&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;activeTab&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Now we can make changes to the currently active tab, such as changing colors on it. The beauty of this particular permission is that it doesn&rsquo;t prompt the user for confirmation (unlike the &ldquo;tabs&rdquo; permission, which gives your extension the ability to affect <em>any</em> open tab), although google will ask you to justify using that permission when you upload your extension to their store.</p>
<p>Change the HTML file to throw a few buttons on the page:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;script.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">body</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;width:200px&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;&lt;</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;button&#34;</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;khaki&#34;</span> <span class="na">value</span><span class="o">=</span><span class="s">&#34;YELLOW&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;&lt;</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;button&#34;</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;lightblue&#34;</span> <span class="na">value</span><span class="o">=</span><span class="s">&#34;BLUE&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;&lt;</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;button&#34;</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;palegreen&#34;</span> <span class="na">value</span><span class="o">=</span><span class="s">&#34;GREEN&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p>And finally, change the script:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;load&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="nx">load</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span><span class="s1">&#39;khaki&#39;</span><span class="p">,</span><span class="s1">&#39;lightblue&#39;</span><span class="p">,</span><span class="s1">&#39;palegreen&#39;</span><span class="p">].</span><span class="nx">forEach</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">color</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="nx">color</span><span class="p">).</span><span class="nx">onclick</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">chrome</span><span class="p">.</span><span class="nx">tabs</span><span class="p">.</span><span class="nx">query</span><span class="p">({</span><span class="s2">&#34;active&#34;</span><span class="o">:</span><span class="kc">true</span><span class="p">,</span><span class="s2">&#34;lastFocusedWindow&#34;</span><span class="o">:</span> <span class="kc">true</span><span class="p">},</span> <span class="kd">function</span><span class="p">(</span><span class="nx">tabs</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nx">chrome</span><span class="p">.</span><span class="nx">tabs</span><span class="p">.</span><span class="nx">insertCSS</span><span class="p">(</span><span class="nx">tabs</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">id</span><span class="p">,</span> <span class="p">{</span><span class="s1">&#39;code&#39;</span><span class="o">:</span><span class="sb">`html,body,div,p{background:</span><span class="si">${</span><span class="nx">color</span><span class="si">}</span><span class="sb">!important}`</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">            <span class="p">});</span>
</span></span><span class="line"><span class="cl">        <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span></span></span></code></pre></div></div>
<p>This script is a little more complex than the previous one. I&rsquo;m just taking a shortcut to register the <code>onclick</code> event for each of the three buttons. As long as the IDs on the buttons match the values in the array, it&rsquo;ll work. One more thing - the <code>!important</code> css tag is one you don&rsquo;t want to use very often, but in this case it ensures we can override other styles on the page and change the background color to what we want.</p>

<h2 class="relative group">Hello World! (now with more options)
    <div id="hello-world-now-with-more-options" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#hello-world-now-with-more-options" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>So far, our extension has been completely uncustomizable.. what about an &ldquo;options&rdquo; page? Most likely you&rsquo;ll want someone to be able to edit and save some settings, and that&rsquo;s where the options page comes in.</p>
<p>First, make a couple tweaks to the manifest.json file, to specify an options page and to request permission to store data.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Options Please&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;1.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;manifest_version&#34;</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;options_page&#34;</span><span class="p">:</span> <span class="s2">&#34;options.html&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;permissions&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;storage&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Create a new file named &ldquo;options.html&rdquo;, that allows for some user input.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;script.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;main&#34;</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;padding:30px&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>Enter your name: <span class="p">&lt;</span><span class="nt">input</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;name&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">input</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;save&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;button&#34;</span> <span class="na">value</span><span class="o">=</span><span class="s">&#34;SAVE&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p>Next, modify the &ldquo;script.js&rdquo; file to store and retrieve the settings - in this case, just a name. We&rsquo;re not doing much <em>with</em> the name, but if you save and close the options page, then open it back up, the name is pulled out of storage.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;load&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="nx">load</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">chrome</span><span class="p">.</span><span class="nx">storage</span><span class="p">.</span><span class="nx">local</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s1">&#39;name&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">result</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">result</span> <span class="o">!=</span> <span class="kc">undefined</span> <span class="o">&amp;&amp;</span> <span class="nx">result</span><span class="p">.</span><span class="nx">name</span> <span class="o">!=</span> <span class="kc">undefined</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">&#39;name&#39;</span><span class="p">).</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">result</span><span class="p">.</span><span class="nx">name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">&#39;save&#39;</span><span class="p">).</span><span class="nx">onclick</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">chrome</span><span class="p">.</span><span class="nx">storage</span><span class="p">.</span><span class="nx">local</span><span class="p">.</span><span class="nx">set</span><span class="p">({</span><span class="s1">&#39;name&#39;</span><span class="o">:</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">&#39;name&#39;</span><span class="p">).</span><span class="nx">value</span><span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span></span></span></code></pre></div></div>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/making-your-first-chrome-extension/chrome-dev-7.png"
    width="484"
      height="318"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/making-your-first-chrome-extension/chrome-dev-8.png"
    width="544"
      height="182"></figure>

<h2 class="relative group">Publishing an extension
    <div id="publishing-an-extension" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#publishing-an-extension" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The last thing you might be interested in is actually publishing your extension in the Chrome Web Store once you&rsquo;ve finished it. I won&rsquo;t repeat all the steps here, since there&rsquo;s already indepth documentation on how to <a href="https://developer.chrome.com/webstore/publish"  target="_blank" rel="noreferrer">publish in the Chrome Web Store</a>.</p>
<p>The process wasn&rsquo;t too complicated, but like everything it&rsquo;s more clear after you run through it once. There&rsquo;s a nominal fee for uploading extensions - I think it&rsquo;s like $5 for up to 20 extensions. For most of us, that $5 is probably the only money we&rsquo;ll ever spend on it.</p>

<h2 class="relative group">What next?
    <div id="what-next" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-next" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Google has some good documentation:</p>
<ul>
<li>Start with the basics in <a href="https://developer.chrome.com/extensions"  target="_blank" rel="noreferrer">What are extensions</a>?</li>
<li>From there, move to <a href="https://developer.chrome.com/extensions/getstarted"  target="_blank" rel="noreferrer">Getting Started</a>.</li>
<li>Check out the <a href="https://developer.chrome.com/extensions/samples"  target="_blank" rel="noreferrer">Sample Extensions</a>.</li>
</ul>
<p>Learning from others is a good idea too. There are some truly amazing extensions, and by using an extension called <a href="https://chrome.google.com/webstore/detail/chrome-extension-source-v/jifpbeccnghkjeaalbbjmodiffmgedin"  target="_blank" rel="noreferrer">Chrome Extension Source Viewer</a>, you can view the source code of any other extension in the store (including itself). It&rsquo;s helpful if you&rsquo;re trying to figure out how someone did something, or just to verify that an extension isn&rsquo;t doing something malicious.</p>
<p>Pick a relatively straight-forward example like the <a href="https://chrome.google.com/webstore/detail/showpassword/bbiclfnbhommljbjcoelobnnnibemabl/support?hl=en-US"  target="_blank" rel="noreferrer">ShowPassword</a> extension, or any other extension you’re currently using and really think is good_._ Check out their manifest.json and various html files, js scripts and other resources like images. Install the extension and compare various actions in it with what you see in the files, so you can see how they affect the user experience.</p>
<p>Good luck!</p>
]]></content:encoded><media:content url="https://grantwinney.com/making-your-first-chrome-extension/feature.webp" medium="image" type="image/webp"/></item><item><title>Safely Build on a Ghost Theme</title><link>https://grantwinney.com/safely-customize-a-theme-in-ghost/</link><pubDate>Mon, 24 Jul 2017 18:57:36 +0000</pubDate><guid>https://grantwinney.com/safely-customize-a-theme-in-ghost/</guid><description/><content:encoded><![CDATA[<p>As of this writing, my blog runs on the <a href="https://ghost.org/"  target="_blank" rel="noreferrer">Ghost platform</a>, and I was mildly surprised when I ran a <code>ghost update</code> the other day and suddenly my custom themes and scripts were just gone! Luckily I use <a href="https://m.do.co/c/448f25462030"  target="_blank" rel="noreferrer">DigitalOcean</a> with backups enabled, and I had a backup from just a couple days before. I rolled back, verified my styles and customizations were present, then ran <code>ghost update</code> again. Wiped out.</p>
<p>In retrospect, this makes sense. There are going to be updates to Casper, the default Ghost theme, and how should they reconcile that with any local changes I&rsquo;ve made? They can&rsquo;t reasonably, so they just overwrite it. WordPress had the concept of <a href="https://codex.wordpress.org/Child_Themes"  target="_blank" rel="noreferrer">child themes</a>, which allowed for extending a base theme, so I attempted to do something similar with Ghost.</p>
<p>If you find yourself wanting to use most of the Casper (or any other) theme, but with some customizations (and you don&rsquo;t want to shove them all into the &ldquo;Code injection&rdquo; tab in the ghost admin screen), then you may want to do what I did.</p>

<h2 class="relative group">Fork the theme in GitHub and clone it locally
    <div id="fork-the-theme-in-github-and-clone-it-locally" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#fork-the-theme-in-github-and-clone-it-locally" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Fork the theme (i.e. <a href="https://github.com/TryGhost/Casper"  target="_blank" rel="noreferrer">Casper</a>) in GitHub and then clone it locally:</p>
<p><code>git clone https://github.com/YOUR-USERNAME/YOUR-FORKED-REPO.git</code></p>
<p>Rename the directory on your local machine so it&rsquo;s different than the default theme name - something like <code>Casper-YOUR-USERNAME</code>. This is necessary when uploading the child theme later - the name cannot be the same as the default.</p>

<h2 class="relative group">Modify it to your liking
    <div id="modify-it-to-your-liking" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#modify-it-to-your-liking" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Make any changes you&rsquo;d like to the theme. I made small tweaks to add more social icons and whitespace, change the blockquote style, use <a href="http://prismjs.com/"  target="_blank" rel="noreferrer">Prism.js</a> for code styling, etc.</p>

<h2 class="relative group">Package and upload it
    <div id="package-and-upload-it" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#package-and-upload-it" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Compress the local directory so you end up with a <code>Casper-YOUR-USERNAME.zip</code> file, and then use the Design tab under settings in the Ghost admin to upload your new theme. Activate it (it should prompt you). Restart Ghost using <code>ghost restart</code> if you don&rsquo;t see the changes applied (but they should be).</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/safely-customize-a-theme-in-ghost/ghost-upload-theme.png"
    width="984"
      height="566"></figure>
<p>It&rsquo;s worth noting that <a href="https://github.com/marketplace/actions/deploy-ghost-theme"  target="_blank" rel="noreferrer">there&rsquo;s instructions for automating this process</a> too, where pushing changes to a custom theme in GitHub will cause it to be automatically deployed to a working Ghost blog. Very convenient!</p>

<h2 class="relative group">Fetch latest official changes, periodically
    <div id="fetch-latest-official-changes-periodically" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#fetch-latest-official-changes-periodically" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>To avoid missing out on future updates to the default Casper theme, <a href="https://gist.github.com/CristinaSolana/1885435"  target="_blank" rel="noreferrer">keep your fork up to date</a> <em>(don&rsquo;t forget to commit your changes first)</em>.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">cd into/cloned/fork-repo
git remote add upstream git://github.com/ORIGINAL-DEV-USERNAME/REPO-YOU-FORKED-FROM.git
git fetch upstream
git pull upstream master
git push</code></pre></div>
<p>If you&rsquo;re a fan of <a href="https://grantwinney.com/creating-a-git-alias/"  target="_blank" rel="noreferrer">git aliases</a>, after you&rsquo;ve run the above once you could add something like this to your .gitconfig file:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">updatecasper = !cd your/casper/location &amp;&amp; git fetch upstream &amp;&amp; git pull upstream master &amp;&amp; git push</code></pre></div>
<p><strong>NOTES:</strong></p>
<ol>
<li>
<p>You might have to replace &ldquo;git://github.com&rdquo; with the &ldquo;<a href="https://github.com"  target="_blank" rel="noreferrer">https://github.com</a>&rdquo; URL instead. When I use the &ldquo;git://&rdquo; one, I sometimes get a fatal error that says it <em>&ldquo;does not appear to be a git repository&rdquo;</em>. Maybe I&rsquo;m doing something wrong?</p>
</li>
<li>
<p>Replace &ldquo;master&rdquo; with &ldquo;main&rdquo; or whatever the branch is in the repo you&rsquo;re merging from.</p>
</li>
<li>
<p>This method of updating my local fork worked perfectly fine for me, but you may also want to checkout the <a href="https://help.github.com/articles/syncing-a-fork/"  target="_blank" rel="noreferrer">official doc on Syncing a fork</a> which suggests a <code>git merge</code> instead of a <code>git pull</code>. There&rsquo;s also a <a href="https://gist.github.com/CristinaSolana/1885435#gistcomment-2114661"  target="_blank" rel="noreferrer">comment</a> under the gist I linked to above, that suggests a way to keep forks up-to-date all through GitHub.</p>
</li>
</ol>
]]></content:encoded><media:content url="https://grantwinney.com/safely-customize-a-theme-in-ghost/feature.webp" medium="image" type="image/webp"/></item><item><title>What is an API?</title><link>https://grantwinney.com/what-is-an-api/</link><pubDate>Sun, 23 Jul 2017 19:41:19 +0000</pubDate><guid>https://grantwinney.com/what-is-an-api/</guid><description>An API is an Application Programming Interface, but what&amp;rsquo;s that really mean? In a more practical sense, it&amp;rsquo;s one programmer hiding the (possibly messy) details of their own code behind a nice veneer, in order to make it easier for another programmer to consume it in their own program.</description><content:encoded><![CDATA[<p>To define it, an API is an Application Programming Interface, but what&rsquo;s that really mean? In a more practical sense, it&rsquo;s one programmer hiding the (possibly messy) details of their own code behind a nice veneer, in order to make it easier for another programmer to consume it in their own program. 😉</p>

<h2 class="relative group">First, what&rsquo;s an interface?
    <div id="first-whats-an-interface" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#first-whats-an-interface" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Before we talk about programming, let&rsquo;s consider what an interface is in the most general sense. We&rsquo;re <em>surrounded</em> by interfaces and may not even realize it most of the time. In fact, the <em>better</em> the interface, the less aware we are that it exists! Whenever something complex is abstracted away for us, hiding the complex details behind a little display screen or a simple button push, someone took the time to create an interface for us.</p>
<p>The dashboard in a car is one example. We don&rsquo;t have to count the number of times the crankshaft in the engine turns - just glance at the RPM gauge. We don&rsquo;t have to manually inspect the level of gas in the tank, or read the output of the low-fuel sensor - we get a nice readout on the dash and a dedicated indicator light. The dashboard is an interface, abstracting away the inner-workings of dozens of sensors and components all over the car.</p>
<p>A kitchen is another example. From toasters to microwaves to coffee machines, all kinds of devices take only a button push or two to set off a complex chain of internal motion that we don&rsquo;t need to worry about.</p>

<h2 class="relative group">What&rsquo;s that have to do with programming?
    <div id="whats-that-have-to-do-with-programming" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whats-that-have-to-do-with-programming" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Programming languages have their own interfaces that provide layers of abstraction too. Code is organized into classes and modules, and then select pieces of the code are marked as &ldquo;public&rdquo; or &ldquo;exported&rdquo; to let you know those are the ones that are safe to use.</p>
<p>Imagine we have a simple <code>Person</code> class, and a <code>PersonReport</code> class that in effect answers questions about a collection of people, like <em>&ldquo;who&rsquo;s an adult?&rdquo;</em> or <em>&ldquo;who&rsquo;s a parent?&rdquo;</em>.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Person</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">Age</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Person</span><span class="p">&gt;</span> <span class="n">Children</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">PersonReport</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Person</span><span class="p">&gt;</span> <span class="n">GetAdults</span><span class="p">(</span><span class="n">List</span><span class="p">&lt;</span><span class="n">Person</span><span class="p">&gt;</span> <span class="n">persons</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">persons</span><span class="p">.</span><span class="n">Where</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">Age</span> <span class="p">&gt;=</span> <span class="m">18</span><span class="p">).</span><span class="n">ToList</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Person</span><span class="p">&gt;</span> <span class="n">GetParents</span><span class="p">(</span><span class="n">List</span><span class="p">&lt;</span><span class="n">Person</span><span class="p">&gt;</span> <span class="n">persons</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">persons</span><span class="p">.</span><span class="n">Where</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">Children</span><span class="p">.</span><span class="n">Count</span> <span class="p">&gt;</span> <span class="m">0</span><span class="p">).</span><span class="n">ToList</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Similar to the dashboard in our car, we don&rsquo;t have to worry about the details underneath. Their abstracted away behind a clean interface. In this case the &ldquo;messy details&rdquo; is a simple one-liner of LINQ, but it could be many lines calling other methods and other classes. The point is, we just ask for parents and get the results.</p>

<h2 class="relative group">So.. what <em>is</em> an API then?
    <div id="so-what-is-an-api-then" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#so-what-is-an-api-then" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Well, the above&hellip; and more. Let&rsquo;s consider some code written by someone else (maybe another team in our company, or even a different company) and provided to us as a framework or library. Nearly every language has an existing framework. These are the building-blocks of the language, as well as thousands or even millions of lines of code that prevent you and I from having to reinvent the same wheel over and over.</p>
<ul>
<li>In C#, it&rsquo;s the <a href="https://msdn.microsoft.com/en-us/library/ff361664%5C%28v=vs.110%5C%29.aspx"  target="_blank" rel="noreferrer">.NET Framework</a> developed by Microsoft</li>
<li>In Erlang, it&rsquo;s the <a href="https://github.com/erlang/otp/tree/master/lib/stdlib/src"  target="_blank" rel="noreferrer">OTP</a> libraries from Ericsson</li>
<li>Java has its <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/#base"  target="_blank" rel="noreferrer">base libraries</a> and a bunch of other frameworks, and on and on&hellip;</li>
</ul>
<p>In the sample of code above, even the call to <code>persons.Where(x =&gt; x.Children.Count &gt; 0).ToList();</code> involved an API call, in the sense that I don&rsquo;t have to know or care <em>how</em> the LINQ library manages to filter my list of persons by a particular attribute. I just specify the condition on which to filter, and call the <code>Where()</code> function to do its magic.</p>

<h2 class="relative group">API calls in the cloud, and the REST interface
    <div id="api-calls-in-the-cloud-and-the-rest-interface" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#api-calls-in-the-cloud-and-the-rest-interface" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>So far I&rsquo;ve been talking about frameworks and libraries which are normally copied to locally and compiled or interpreted with the rest of the app. But what if the code is running on someone else&rsquo;s computer instead of ours, and we can&rsquo;t copy it? It could be running on one server in a basement somewhere or in a state-of-the-art facility – if we don&rsquo;t have access to it, what do we do?</p>
<p>That&rsquo;s what most people mean by APIs. There are thousands of services all over the world, written in any one (or a combination) of a hundred different languages, and the problem is how to integration it using the language of your choice? There are a number of ways, but the most popular right now is the REST interface.</p>
<p>But let&rsquo;s back up again for a minute.</p>
<p>Any time we access a page in a web browser, the browser sends a &ldquo;GET&rdquo; message to the page&rsquo;s url like (i.e. &ldquo;<a href="http://www.example.com"  target="_blank" rel="noreferrer">http://www.example.com</a>&rdquo;). In return, some web server somewhere in the world returns a block of html markup representing that page to you. Your browser uses that and says &ldquo;okay, there&rsquo;s a table tag so let&rsquo;s add a table, and a blockquote tag so let&rsquo;s format that however it&rsquo;s supposed to look, and oh! an image tag&rdquo;. It renders the page and does more &ldquo;GET&rdquo; operations to get all the images and stylesheets and so on and so forth.</p>
<p>If we&rsquo;re visit a page where we&rsquo;re updating our profile information, the browser might send a &ldquo;POST&rdquo; operation to the website, so some web server somewhere in the world can update our data. And if we choose to delete our profile, well then it might send a &ldquo;DELETE&rdquo; operation to the website, so it can delete our data from the database.</p>
<p>If you want to see something kind of neat, download <a href="https://www.getpostman.com/"  target="_blank" rel="noreferrer">Postman</a> and try performing a &ldquo;GET&rdquo; on some websites. You&rsquo;ll see the markup your browser gets when it requests a site, and you can even use this tool to send &ldquo;POST&rdquo; and &ldquo;DELETE&rdquo; requests to sites too, if you know of a URL that expects them.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-an-api/image.png"
    width="799"
      height="688"></figure>
<p>Performing a GET with Postman</p>
<p>Just like the browser issues REST commands to get, update, and delete data from around the world, our own apps can too. We can do a &ldquo;GET&rdquo; request to a certain endpoint URL such as &ldquo;<a href="http://www.example.com/users/1234%22"  target="_blank" rel="noreferrer">http://www.example.com/users/1234"</a>, and some server somewhere in the world can do whatever it does and return the data for user 1234 to us. The difference is that instead of returning HTML markup like with the browser, it&rsquo;s likely to return your data in JSON format. Going anymore deeply into REST and JSON will have to be saved for another post though.. this one&rsquo;s already long enough as-is.</p>
<p>So all that to say, if someone asks you what an API is&hellip;</p>
<p>An API is some code running on someone else&rsquo;s machine <em>(where? how? who cares!),</em> which they&rsquo;ve made accessible to you. And the <em>way</em> you typically access it is using REST, very similar to how your web browser does it.</p>
]]></content:encoded><media:content url="https://grantwinney.com/what-is-an-api/feature.webp" medium="image" type="image/webp"/></item><item><title>Unit Testing in Visual Studio for Mac</title><link>https://grantwinney.com/visual-studio-for-mac-tdd/</link><pubDate>Thu, 06 Jul 2017 12:15:51 +0000</pubDate><guid>https://grantwinney.com/visual-studio-for-mac-tdd/</guid><description>Are you a Mac user and .NET fan? Did you know there&amp;rsquo;s a native VS app now? Writing tests is important, so I decided to try out NUnit in @vs4mac.</description><content:encoded><![CDATA[<p>Last month at a user group, they selected the <a href="https://github.com/gigasquid/wonderland-clojure-katas/tree/master/magic-square"  target="_blank" rel="noreferrer">magic square kata</a>, which was a new one for me. Basically, you arrange 9 unique numbers in a 3x3 grid such that they add up to the same number horizontally, vertically and diagonally. I paired up with someone else who knew C#, and we tackled the kata in Visual Studio for Mac.</p>
<p>Although I&rsquo;ve kicked the tires on <a href="https://visualstudio.microsoft.com/vs/mac/"  target="_blank" rel="noreferrer">VS4Mac</a> a bit, one of the things I hadn&rsquo;t tested out was, well.. testing!</p>

<h2 class="relative group">Method 1: An NUnit Library Project
    <div id="method-1-an-nunit-library-project" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#method-1-an-nunit-library-project" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The easiest method is to just create a new &ldquo;NUnit Library Project&rdquo;. The VS4Mac team actually added a project type that includes the NUnit package and a test file out of the box. How convenient is that??</p>

<h3 class="relative group">Create a new project
    <div id="create-a-new-project" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#create-a-new-project" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Go to: <code>File / New Solution / Other / .NET / NUnit Library Project</code></p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/visual-studio-for-mac-tdd/vs4mac-test-setup01-1.png"
    width="1791"
      height="1290"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/visual-studio-for-mac-tdd/vs4mac-test-setup02-1.png"
    width="1710"
      height="745"></figure>

<h3 class="relative group">Create a class and some tests
    <div id="create-a-class-and-some-tests" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#create-a-class-and-some-tests" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>There&rsquo;s already a &ldquo;Test.cs&rdquo; file ready to go, with the proper NUnit attributes and everything. Let&rsquo;s create a regular class and add a couple tests against it.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/visual-studio-for-mac-tdd/vs4mac-test-setup03.png"
    width="2877"
      height="1552"></figure>

<h3 class="relative group">Run the tests
    <div id="run-the-tests" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#run-the-tests" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>If the &ldquo;Unit Test&rdquo; pane (or pad as they call it on the Mac) isn&rsquo;t visible, open it: <code>View / Pads / Unit Tests</code></p>
<p>Click the build button (black triangle in upper-left) to see the new tests, if necessary. Or just click the &ldquo;Run All&rdquo; button in the Unit Tests pad.</p>
<p>Let&rsquo;s change the logic so the tests fail (if they didn&rsquo;t already) and check out the failure results in the &ldquo;Test Results&rdquo; pad at the bottom. If that pad isn&rsquo;t visible, open it now: <code>View / Pads / Test Results</code></p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/visual-studio-for-mac-tdd/vs4mac-test-setup04.png"
    width="2876"
      height="1553"></figure>
<p>That&rsquo;s it! Using VS4Mac for TDD during a code kata doesn&rsquo;t get much easier than that. :)</p>

<h2 class="relative group">Method 2: Add NUnit to an Existing Project
    <div id="method-2-add-nunit-to-an-existing-project" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#method-2-add-nunit-to-an-existing-project" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>But what if we already have a project and just want to add tests to it?</p>
<p>Start by creating a Library project to act as the &ldquo;existing project&rdquo;:<br>
<code>File / New Solution / Other / .NET / Library</code></p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/visual-studio-for-mac-tdd/vs4mac-test-setup01.png"
    width="1798"
      height="1302"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/visual-studio-for-mac-tdd/vs4mac-test-setup02.png"
    width="1711"
      height="753"></figure>
<p>If the &ldquo;Solution&rdquo; pad isn&rsquo;t visible on the side: go to: <code>View / Pads / Solution</code></p>

<h3 class="relative group">Create a Test File
    <div id="create-a-test-file" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#create-a-test-file" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Right-click the project and choose <code>Add / New File.</code> Select <code>General / Empty Class</code> and name it &ldquo;MagicSquareTests.cs&rdquo;. Alternatively, do what I did and just rename the default &ldquo;MyClass.cs&rdquo; as my MagicSquare class, which should look something like this:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/visual-studio-for-mac-tdd/vs4mac-test-setup05.png"
    width="1905"
      height="550"></figure>

<h3 class="relative group">Add the NUnit Package via NuGet
    <div id="add-the-nunit-package-via-nuget" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#add-the-nunit-package-via-nuget" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Right-click on Packages in the Solution pad and choose &ldquo;Add Packages&rdquo;. All we need is NUnit, not the NUnit Console Runner.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/visual-studio-for-mac-tdd/vs4mac-test-setup06.png"
    width="1639"
      height="863"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/visual-studio-for-mac-tdd/vs4mac-test-setup07.png"
    width="404"
      height="450"></figure>
<p>The NUnit folder should be visible under the Packages folder.</p>

<h3 class="relative group">Create a Few Tests
    <div id="create-a-few-tests" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#create-a-few-tests" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Let&rsquo;s add some new tests to run against whatever logic the old project has. In my case, I added a single function for the magic square kata, and wrote a couple tests against it that were sure to fail:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/visual-studio-for-mac-tdd/vs4mac-test-setup08-1.png"
    width="2872"
      height="1655"></figure>
<p>The test runner tells us what failed and where.</p>

<h3 class="relative group">Run / Observe / Fix / Repeat!
    <div id="run--observe--fix--repeat" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#run--observe--fix--repeat" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Try adding enough code to get the tests to pass, and run again. Green = good!</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/visual-studio-for-mac-tdd/vs4mac-test-setup09-1.png"
    width="2876"
      height="1655"></figure>
]]></content:encoded><media:content url="https://grantwinney.com/visual-studio-for-mac-tdd/feature.webp" medium="image" type="image/webp"/></item><item><title>5 Quick Hacks for Your Ghost Theme</title><link>https://grantwinney.com/5-quick-hacks-for-your-ghost-theme/</link><pubDate>Sat, 01 Apr 2017 18:47:23 +0000</pubDate><guid>https://grantwinney.com/5-quick-hacks-for-your-ghost-theme/</guid><description>These &amp;ldquo;hacks&amp;rdquo; for Ghost add some cool features to any blog, and should be usable with any theme.</description><content:encoded><![CDATA[<p>As of this writing, I&rsquo;m using the default &ldquo;Casper&rdquo; theme that installs with Ghost (read more about <a href="https://grantwinney.com/migrating-a-blog-from-wordpress-to-ghost/"  target="_blank" rel="noreferrer">my migration from WordPress to Ghost</a>), but these hacks should be usable in any theme (with adjustments) as needed. Using these requires that you&rsquo;re able to SSH into your Ghost installation, or otherwise have access to upload and modify files.</p>

<h2 class="relative group">Hack #1: Adding a &ldquo;Subscribe to Tag&rdquo; RSS feed button
    <div id="hack-1-adding-a-subscribe-to-tag-rss-feed-button" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#hack-1-adding-a-subscribe-to-tag-rss-feed-button" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Ghost can generate RSS feeds for individual tags, but they&rsquo;re not all that discoverable. To help your visitors, <strong>add the last line</strong> in the following snippet to the <code>ghost/content/themes/casper/tag.hbs</code> file. It combines your blog url and the tag url to generate a custom RSS link your visitors can use.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;page-title&#34;</span><span class="p">&gt;</span>{{name}}<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h2</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;page-description&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    {{#if description}}
</span></span><span class="line"><span class="cl">        {{description}}
</span></span><span class="line"><span class="cl">    {{else}}
</span></span><span class="line"><span class="cl">        A {{../pagination.total}}-post collection
</span></span><span class="line"><span class="cl">    {{/if}}
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">a</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;icon-feed rss-tag&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;{{@blog.url}}{{url}}rss/&#34;</span><span class="p">&gt;</span>Subscribe to this tag<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p>This produces a new &ldquo;subscribe&rdquo; link under the tag description when viewing a particular tag. <em>(Here I&rsquo;ve applied some color and shadow effects to the other elements too.)</em></p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Subscribe link added under tag description"
    src="/5-quick-hacks-for-your-ghost-theme/ghost-subscribe-to-tag.png"
    width="319"
      height="156"></figure>
<hr>

<h2 class="relative group">Hack #2: Using a blog cover image for posts that have none
    <div id="hack-2-using-a-blog-cover-image-for-posts-that-have-none" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#hack-2-using-a-blog-cover-image-for-posts-that-have-none" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>When you view all posts for a tag, it loads a cover image at the top. If the tag has no image assigned to it, the <code>tag.hbs</code> template loads your blog&rsquo;s cover image instead:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">{{!-- If we have a tag cover, display that - else blog cover - else nothing --}}
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">header</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;main-header tag-head {{#if tag.image}}&#34;</span> 
</span></span><span class="line"><span class="cl">        <span class="na">style</span><span class="o">=</span><span class="s">&#34;background-image: url({{tag.image}}){{else}}{{#if @blog.cover}}&#34;</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;background-image: url({{@blog.cover}}){{else}}no-cover{{/if}}{{/if}}&#34;</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p>But what about posts? If there isn&rsquo;t a cover image for a post, the <code>post.hbs</code> template leaves an empty area - but you can change that behavior to mimic the tag view. Find the code in <code>post.hbs</code> that loads cover images, shortly after the <code>{{#post}}</code> expression, and modify it to load the blog cover image when no post cover image is available:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">{{!-- If we have a post cover, display that - else blog cover - else nothing --}}
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">header</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;main-header post-head {{#if image}}&#34;</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;background-image: url({{image}}){{else}}{{#if @blog.cover}}&#34;</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;background-image: url({{@blog.cover}}){{else}}no-cover{{/if}}{{/if}}&#34;</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p>Here&rsquo;s a post that has no cover image assigned to it, so it&rsquo;s loading my blog cover image instead.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Post with no cover image"
    src="/5-quick-hacks-for-your-ghost-theme/image-3.png"
    width="603"
      height="433"></figure>
<hr>

<h2 class="relative group">Hack #3: Adding thumbnail images next to post listings
    <div id="hack-3-adding-thumbnail-images-next-to-post-listings" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#hack-3-adding-thumbnail-images-next-to-post-listings" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Many of the themes in WordPress load a &ldquo;featured&rdquo; thumbnail image next to each individual post when viewing lists of posts. If, like me, you miss this feature and think the front page is a bit bland without thumbnails, you can modify one of the templates to include them.</p>
<p>Add the second line in the snippet below to <code>ghost/content/themes/casper/partials/loop.hbs</code>. That&rsquo;s the file that handles lists of posts, like on the front page of your blog or when you&rsquo;re viewing an individual tag. It inserts your post&rsquo;s cover image if there is one.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">section</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;post-excerpt&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    {{#if image}}<span class="p">&lt;</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;{{image}}&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;front-page-image&#34;</span> <span class="p">/&gt;</span>{{/if}}
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>{{excerpt words=&#34;26&#34;}} <span class="p">&lt;</span><span class="nt">a</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;read-more&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;{{url}}&#34;</span><span class="p">&gt;</span><span class="ni">&amp;raquo;</span><span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">section</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p>The <code>front-page-image</code> class is for adding styling, because it&rsquo;s gonna look pretty ugly without it. Here&rsquo;s how I defined it. You could place the following in a separate file and reference it in the <code>{{!-- Styles'n'Scripts --}}</code> section of <code>default.hbs</code>, include it in the &ldquo;Code Injection&rdquo; part of the admin panel, or just place it inline in the <code>img</code> element. Play around with it until you get something you like.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="c">/* Add thumbnail image to the main posts list */</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">front-page-image</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">width</span><span class="p">:</span> <span class="mi">200</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">max-height</span><span class="p">:</span> <span class="mi">200</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">float</span><span class="p">:</span> <span class="kc">right</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin-left</span><span class="p">:</span> <span class="mi">30</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin-bottom</span><span class="p">:</span> <span class="mi">15</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">box-shadow</span><span class="p">:</span> <span class="mi">5</span><span class="kt">px</span> <span class="mi">5</span><span class="kt">px</span> <span class="mi">3</span><span class="kt">px</span> <span class="kc">gray</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Here&rsquo;s what it ends up looking like. Notice how I have longer descriptions too. You can change <code>{{excerpt words=&quot;26&quot;}}</code> to a larger number in the <code>tag.hbs</code> file. I set it to 100.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Adding thumbnails to post listings"
    src="/5-quick-hacks-for-your-ghost-theme/ghost-thumbnails-in-post-list.png"
    width="932"
      height="619"></figure>
<hr>

<h2 class="relative group">Hack #4: Using a post&rsquo;s meta data as its title and excerpt in post listings
    <div id="hack-4-using-a-posts-meta-data-as-its-title-and-excerpt-in-post-listings" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#hack-4-using-a-posts-meta-data-as-its-title-and-excerpt-in-post-listings" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Ghost has built SEO functionality right into its core. While editing a post, you can add meta data about it. Ghost then uses that meta data to generate markup that can be consumed by search engines or used when posting to social media sites.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Adding meta data to a post|500"
    src="/5-quick-hacks-for-your-ghost-theme/ghost-meta-data-in-post-list-1.png"
    width="711"
      height="503"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Page source showing meta data"
    src="/5-quick-hacks-for-your-ghost-theme/ghost-meta-data-in-post-list-3.png"
    width="637"
      height="401"></figure>
<p>What if you wanted to use this data, when present, as the &ldquo;title&rdquo; and &ldquo;excerpt&rdquo; on your blog in lists of posts like on the front page? I mean, you&rsquo;ve already taken the extra step of adding a concise description of your post, so why not use that instead of having Ghost simply display the first xx words of your post?</p>
<p>Here&rsquo;s where <code>ghost/content/themes/casper/partials/loop.hbs</code> loops through each of your posts, writing the title and excerpt.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">{{#foreach posts}}
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">article</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;{{post_class}}&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">header</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;post-header&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">h2</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;post-title&#34;</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;{{url}}&#34;</span><span class="p">&gt;</span>{{title}}<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">header</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">section</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;post-excerpt&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>{{excerpt words=&#34;26&#34;}} <span class="p">&lt;</span><span class="nt">a</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;read-more&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;{{url}}&#34;</span><span class="p">&gt;</span><span class="ni">&amp;raquo;</span><span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">section</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p>You can modify that to check for the presence of a <code>meta_title</code> and <code>meta_description</code> first, then fall back to the regular title and first xx words of your post if needed.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">{{#foreach posts}}
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">article</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;{{post_class}}&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">header</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;post-header&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">h2</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;post-title&#34;</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;{{url}}&#34;</span><span class="p">&gt;</span>{{#if meta_title}}{{meta_title}}{{else}}{{title}}{{/if}}<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">header</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">section</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;post-excerpt&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>{{#if meta_description}}{{meta_description}}{{else}}{{excerpt words=&#34;26&#34;}}{{/if}} <span class="p">&lt;</span><span class="nt">a</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;read-more&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;{{url}}&#34;</span><span class="p">&gt;</span><span class="ni">&amp;raquo;</span><span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">section</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p>And here&rsquo;s how it renders. The first post has a meta title <em>and</em> meta description specified, but the second has neither.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Rendering meta data on page"
    src="/5-quick-hacks-for-your-ghost-theme/image-4.png"
    width="641"
      height="355"></figure>
<hr>

<h2 class="relative group">Hack #5: Adding a Table of Contents to your posts
    <div id="hack-5-adding-a-table-of-contents-to-your-posts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#hack-5-adding-a-table-of-contents-to-your-posts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Last but not least, what if you&rsquo;d like to generate a table of contents from the headers in your posts? It&rsquo;d be monotonous to add (and update) them yourself.</p>
<p>I originally found a nice script to generate a table of contents, then forked it, fixed it, and added a few more features to make it work with Ghost. Unfortunately, upgrading from Ghost 0.11 to 1.0 broke it for some reason. Instead of fixing it, I<a href="https://grantwinney.com/creating-a-table-of-contents-for-your-blog"  target="_blank" rel="noreferrer"> wrote a new script that should work with any theme</a>.</p>
<hr>
<p><strong>What do you think of these little hacks?</strong></p>
<p>Did you find them helpful? Did you improve on any of them, or come up with your own? Feel free to share your thoughts and comments below!</p>
]]></content:encoded><media:content url="https://grantwinney.com/5-quick-hacks-for-your-ghost-theme/feature.webp" medium="image" type="image/webp"/></item><item><title>Migrating a Blog from WordPress to Ghost</title><link>https://grantwinney.com/migrating-a-blog-from-wordpress-to-ghost/</link><pubDate>Sun, 26 Mar 2017 02:43:14 +0000</pubDate><guid>https://grantwinney.com/migrating-a-blog-from-wordpress-to-ghost/</guid><description/><content:encoded><![CDATA[<p>About a week ago I decided to migrate my blog to the Ghost platform. I&rsquo;d been thinking about it for awhile - even installed it once or twice to play around with it - but never fully committed. Truth is, I didn&rsquo;t really <em>want</em> to switch. I knew that, however little, the process would certainly be more painful than doing nothing. So the pain of going through the switch had to be outweighed by the pain of <em>not</em> switching. I guess that finally happened.</p>
<p>WordPress can do pretty much anything thanks to plugins, but that&rsquo;s its weakness too&hellip; <em>especially</em> if all you want to do is blog. Over the last few years I&rsquo;ve had to upgrade memory multiple times and setup disk swap space, slog through plugins to determine which were good and then keep them up-to-date, find workarounds for <a href="https://wptavern.com/zerif-lite-suspended-from-wordpress-theme-directory-300k-users-left-without-updates"  target="_blank" rel="noreferrer">unexpectedly dropped themes</a>, handle weird issues like the <a href="https://codex.wordpress.org/Common_WordPress_Errors"  target="_blank" rel="noreferrer">white screen of death</a>, deal with a busy interface that (despite their efforts) just leaves too much distraction on the screen while I try to write. There&rsquo;s just too much overhead. I don&rsquo;t <em>want</em> to spend so much of my free time maintaining a platform&hellip;</p>
<p><em>I want it to get out of my way and just let me write!</em></p>
<p>And so here I am, writing my first post on the Ghost platform. Does any of this sound familiar to you? If you&rsquo;re looking to just get back to the basics, and you want to try switching from WordPress to Ghost, then read on&hellip;</p>

<h2 class="relative group">What is this tutorial (and what is it not)?
    <div id="what-is-this-tutorial-and-what-is-it-not" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-is-this-tutorial-and-what-is-it-not" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Let me set the expectations right now&hellip; I got it to all work but I&rsquo;m no expert. This is my personal experience migrating (one time) from a <em><strong>self-hosted WordPress</strong></em> installation to a <em><strong>self-hosted Ghost</strong></em> installation, using <em><strong>DigitalOcean</strong></em> which has a &ldquo;one-click install&rdquo; process for both platforms.</p>
<p>A few things to consider:</p>
<p>First, if you&rsquo;ve been using a <em>managed</em> WordPress installation hosted on wordpress.com, then that&rsquo;s pretty restricted. You don&rsquo;t have the ability to install plugins or directly access the disk to get your images. Your data is obviously all accessible or visitors wouldn&rsquo;t be able to view it, but getting to that data won&rsquo;t be straight-forward. You may have to contact support, but I&rsquo;m not sure how easy they make it to leave their platform.</p>
<p>Second, DigitalOcean has their own page on <a href="https://www.digitalocean.com/community/tutorials/how-to-use-the-digitalocean-ghost-application"  target="_blank" rel="noreferrer">how to use install and use Ghost</a>, and Ghost has documentation on <a href="https://www.ghostforbeginners.com/migrating-your-wordpress-blog-to-ghost"  target="_blank" rel="noreferrer">migrating from WordPress</a>, so you may want to check those out first. Who knows, that might be all you need.</p>
<p>Finally, if you&rsquo;re just looking to have someone host and maintain Ghost for you, look no further than Ghost(Pro). <a href="https://ghost.org/pricing/"  target="_blank" rel="noreferrer">For $20/month they do everything for you</a>&hellip; hosting, backups, security, letting you modify the theme and use their API to interact with your site. You still need to download the data out of WordPress to give to them <em>(step 1 below),</em> and I&rsquo;m unclear whether they give you easy access to your data once it&rsquo;s uploaded, but it seems like a great deal and one I may consider in the future.</p>

<h2 class="relative group">Step 1: Get your data out of WordPress
    <div id="step-1-get-your-data-out-of-wordpress" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#step-1-get-your-data-out-of-wordpress" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>You don&rsquo;t want to lose those old posts! The folks at Ghost wrote up some <a href="https://docs.ghost.org/migration/wordpress"  target="_blank" rel="noreferrer">instructions for migrating posts from WordPress</a>, and it involves <a href="https://wordpress.org/plugins/ghost/"  target="_blank" rel="noreferrer">their very own WordPress plugin</a> that does some of the work for you. It runs through your content, exporting posts, pages and tags to a file (in JSON format) which you then download and import into Ghost. It has some issues that I&rsquo;ll cover later, but it gets the job done.</p>
<p><em><strong>Note 1:</strong></em> If you have a lot of drafts without titles, give them something now. Otherwise the import may fail later until you edit the JSON file and add titles manually. (Alternatively, move them to trash temporarily, which the plugin seems to ignore, and then copy/paste them into the new blog later on.)</p>
<p><em><strong>Note 2:</strong></em> I ran into a timeout issue running their plugin. After 30 seconds, it&rsquo;d timeout with a 500 server error. To fix it, I had to <a href="https://web.archive.org/web/20170815145950/http://www.clickonf5.org/11921/solution-for-wordpress-php-error-maximum-execution-time-of-30-seconds-exceeded/"  target="_blank" rel="noreferrer">increase the php timeout</a> to 120 seconds and run it again. The first suggested fix worked fine - you can find the <code>wp-config.php</code> file in the root of your blog installation, i.e: <code>/var/www/wp-config.php</code></p>
<p>Now you&rsquo;ve got your textual data, but you still need to get your images. To do that, you&rsquo;ll have to <a href="https://www.digitalocean.com/community/tutorials/how-to-connect-to-your-droplet-with-ssh"  target="_blank" rel="noreferrer">setup SSH access to your server</a> <em>(you may find</em> <a href="https://www.digitalocean.com/community/tutorials/how-to-use-ssh-keys-with-digitalocean-droplets"  target="_blank" rel="noreferrer"><em>this</em></a> <em>and</em> <a href="https://www.digitalocean.com/community/tutorials/how-to-use-ssh-keys-with-putty-on-digitalocean-droplets-windows-users"  target="_blank" rel="noreferrer"><em>this</em></a> <em>helpful as well).</em> Once you do, the images are in the <code>/var/www/wp-content/uploads</code> folder, organized by year. Just <a href="https://www.digitalocean.com/community/tutorials/how-to-use-sftp-to-securely-transfer-files-with-a-remote-server"  target="_blank" rel="noreferrer">download the whole directory using sftp</a>.</p>
<p>Another option (and the one I used) is to setup the <a href="https://wordpress.org/plugins/updraftplus/"  target="_blank" rel="noreferrer">UpdraftPlus Backup plugin</a> in WordPress. I was already using it to backup my site to a Google Drive account once a week so I just manually kicked off the job. Then it was as simple as downloading the file from Google Drive and unzipping it to get all my images.</p>

<h2 class="relative group">Step 2: Find some place to call home
    <div id="step-2-find-some-place-to-call-home" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#step-2-find-some-place-to-call-home" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I mentioned the easy route - Ghost(Pro) - but I&rsquo;m trying to minimize costs so I went the self-hosting way. <a href="https://m.do.co/c/448f25462030"  target="_blank" rel="noreferrer">I&rsquo;ve been using DigitalOcean for several years</a> so I stuck with them - they even provide a one-click droplet for Ghost that makes it super-easy to get up and running with the latest version. Press the &ldquo;Create Droplet&rdquo; button and select Ghost from the &ldquo;One-click apps&rdquo;. This is a huge convenience over <a href="http://docs.ghost.org/pl/installation/deploy/"  target="_blank" rel="noreferrer">deploying Ghost yourself</a>.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/migrating-a-blog-from-wordpress-to-ghost/ghost_install_1.png"
    width="1148"
      height="444"></figure>
<p>I selected the $10/month 1GB plan and Ghost seems plenty responsive - <em>much</em> quicker than WordPress was. You might consider selecting &ldquo;Backups&rdquo; under &ldquo;Additional Options&rdquo; too, especially if you find yourself mucking with the server configurations at all. For $2/month they take a complete snapshot of your entire server every week; restoring it is as easy as one button press.</p>
<p>If you don&rsquo;t currently have one, purchase a domain name and configure it to point at your droplet. <a href="https://affiliate.namecheap.com/?affId=113268"  target="_blank" rel="noreferrer">I&rsquo;ve had great luck with Namecheap</a>. If you do already have one, don&rsquo;t switch it yet because your visitors will hit a bunch of 404 pages until your content is uploaded.</p>
<p>When you <a href="https://www.digitalocean.com/community/tutorials/how-to-connect-to-your-droplet-with-ssh"  target="_blank" rel="noreferrer">SSH into the server</a>, it&rsquo;ll tell you a few things that have been setup by default:</p>
<ul>
<li>The &ldquo;ufw&rdquo; firewall is enabled, blocking all ports except 80 and 443 (http and https) and 22 (SSH and SFTP). That&rsquo;s good.</li>
<li>&ldquo;Let&rsquo;s Encrypt&rdquo; has been pre-installed. More on that later, but you&rsquo;ll want to set that up and enable HTTPS.</li>
<li>Ghost is configured to use MySQL. You can run <code>mysql_secure_installation</code> to ready your server for production use. I won&rsquo;t cover that here, but it&rsquo;s something to note.</li>
</ul>

<h2 class="relative group">Step 3: Clean up the exported file
    <div id="step-3-clean-up-the-exported-file" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#step-3-clean-up-the-exported-file" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>In the <a href="https://docs.ghost.org/migration/wordpress"  target="_blank" rel="noreferrer">instructions for migrating posts</a>, they warn you that the image directory in Ghost is similar but not identical. Open the JSON file you downloaded (in an editor like Atom or Notepad++), and take note of the various images paths in your posts. The date portion of the path should be fine, but <code>/wp-uploads/</code> needs to be replaced with <code>/content/images/</code>.</p>
<p>You might also want to do a search for your previous host&rsquo;s IP address. You can leave the domain name alone, but if the IP address is in the JSON file anywhere you&rsquo;ll want to replace it with the IP address of your new site <em>(which you&rsquo;ll eventually replace with the domain name, but not yet)</em>.</p>

<h2 class="relative group">Step 4: First time setup
    <div id="step-4-first-time-setup" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#step-4-first-time-setup" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Before I could access the admin area of Ghost, I had to open <code>http://&lt;your-ip-address&gt;/ghost/signup</code> and create a new account. From there, I opened the &ldquo;Labs&rdquo; section and attempted to import the file, but it kept failing. It took awhile to realize that the list of messages was <em>not</em> of successfully imported files but exactly the opposite - a list of failures and the reason(s) why. Several of my draft posts were missing titles and one had a missing date.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/migrating-a-blog-from-wordpress-to-ghost/ghost-import-failed-missing-date.png"
    width="1648"
      height="1464"></figure>
<p>You might find it easier to run the contents of the JSON file through a JSON formatter first, like <a href="https://atom.io/packages/pretty-json"  target="_blank" rel="noreferrer">this one</a> for Atom. It makes it easier to read, and it&rsquo;ll still import into Ghost just fine. Eventually, everything should import without errors.</p>
<p>Next, upload the images to the <code>/content/images/</code> directory <a href="https://www.digitalocean.com/community/tutorials/how-to-use-sftp-to-securely-transfer-files-with-a-remote-server"  target="_blank" rel="noreferrer">using the sftp command</a>, and change the owner to <code>ghost</code>. Something like this should work nicely:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sftp -r root@&lt;your-ip-address&gt;
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> /var/www/ghost/content/images
</span></span><span class="line"><span class="cl">put /local/path/to/your/images/uploads/*
</span></span><span class="line"><span class="cl">chown -R ghost:ghost /var/www/ghost/content/images</span></span></code></pre></div></div>
<p>If the posts and images have been correctly uploaded, you should be able to open a few posts and verify that they look okay and the images load correctly (you may have to change image links to your new site&rsquo;s IP address if you haven&rsquo;t assigned a domain name yet.)*</p>

<h2 class="relative group">Step 5: Assign the domain name
    <div id="step-5-assign-the-domain-name" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#step-5-assign-the-domain-name" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you&rsquo;re migrating from your old blog, it&rsquo;s time to point your domain name to the Ghost installation. Usually, this means going into your domain name provider&rsquo;s settings and changing the Type A record to point at your new site&rsquo;s IP address. Now your site is accessible via HTTP, but I&rsquo;d highly recommend enabling (and enforcing) HTTPS - you&rsquo;ll be able to login securely, and <a href="https://www.vice.com/en/article/google-will-soon-shame-all-websites-that-are-unencrypted-chrome-https/"  target="_blank" rel="noreferrer">Google is using HTTPS</a> both as a ranking factor in their search engine and eventually as the default for Chrome.</p>

<h2 class="relative group">Step 6: Setting up HTTPS
    <div id="step-6-setting-up-https" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#step-6-setting-up-https" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>It used to cost money, but thanks to <a href="https://letsencrypt.org/"  target="_blank" rel="noreferrer">Let&rsquo;s Encrypt</a> it&rsquo;s now free for everyone. It&rsquo;s sponsored by some major players in the tech industry including Mozilla, Google and the <a href="https://www.eff.org/"  target="_blank" rel="noreferrer">EFF</a>. I found the article &ldquo;<a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-16-04"  target="_blank" rel="noreferrer">Initial Server Setup with Ubuntu 16.04</a>&rdquo; helpful in configuring my server and setting up proper access - at least the first four steps.</p>
<p>DigitalOcean has an article on <a href="https://www.digitalocean.com/community/tutorials/how-to-secure-apache-with-let-s-encrypt-on-ubuntu-16-04"  target="_blank" rel="noreferrer">how to secure Apache with Let&rsquo;s Encrypt</a>, but I found a really nice writeup by Robert Nealan: &ldquo;<a href="https://www.robertnealan.com/setting-up-ssl-for-ghost-on-digitalocean-with-lets-encrypt/"  target="_blank" rel="noreferrer">Setting up SSL for Ghost on DigitalOcean with Let&rsquo;s Encrypt</a>&rdquo;. It worked great, and even showed how to setup CRON to automatically renew the certificate. <em>(A downside of Let&rsquo;s Encrypt is that the license must be renewed every 90 days, which makes sense but is still a pain.)</em></p>
<p><em><strong>Note:</strong></em> If you follow the tutorial by Robert Nealan, when you get to the step where you&rsquo;re uncommenting out the new nginx config lines (currently step 8 in his instructions), leave the following lines commented out (or just remove them). They&rsquo;ll break Ghost&rsquo;s ability to serve up the <code>sitemap.xml</code> and <code>robots.txt</code> files, both of which are useful.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">#        location ~ ^/(sitemap.xml|robots.txt) {
#                root /var/www/ghost/public;
#        }</code></pre></div>

<h2 class="relative group">Step 7: Second clean up and re-importing posts
    <div id="step-7-second-clean-up-and-re-importing-posts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#step-7-second-clean-up-and-re-importing-posts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you&rsquo;ve gotten this far, you may want to re-import your posts. Open that JSON file again and replace any occurrences of your site&rsquo;s IP address to your domain name. And if the domain name was previously using the insecure <code>http://</code>, you&rsquo;ll want to update all of those to <code>https://</code>. The previous instructions showed you how to redirect all <code>http</code> requests to <code>https</code>, so technically things should work just fine as-is, but those redirects cause a small amount of overhead and get you dinged on sites that analyze your website.</p>
<p>There are a couple ways you could approach this. One way is to do what I did. Go to the &ldquo;Labs&rdquo; panel and press &ldquo;DELETE&rdquo; to delete all your posts. It&rsquo;ll leave your images and other settings alone. After you&rsquo;ve updated the JSON file you got from the WordPress plugin, just re-import it and everything should be back and good to go.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/migrating-a-blog-from-wordpress-to-ghost/labs-settings-ghost.png"
    width="728"
      height="376"></figure>
<p>The other option, which I didn&rsquo;t try but you may want to if you&rsquo;ve made changes to your posts after you uploaded them into Ghost the first time, is to click the &ldquo;EXPORT&rdquo; button on that same screen to export your posts from Ghost. Update <em>that</em> file and then delete all your content and re-import the updated file.</p>
<p>Either way, verify you can still login and your posts are accessible and look okay. Assuming you had WordPress setup to use <em>only</em> your post title as the URL, which is also the default in Ghost, then everything should remain accessible in search engines and no one will be the wiser. Otherwise, you may have to navigate to the &ldquo;General&rdquo; panel and select &ldquo;Include the date in your post URLs&rdquo; under &ldquo;Dated Permalinks&rdquo;. I&rsquo;m not sure what that format looks like, but if it&rsquo;s still not the same as your old site you&rsquo;ll need to look at how to configure your server to direct requests for old paths to the new path. That&rsquo;s called doing a 301 permanent redirect, and will let search engines know to update their references to your posts. You don&rsquo;t want to lose traffic! I won&rsquo;t go into all that here, but <a href="https://www.digitalocean.com/community/tutorials/how-to-create-temporary-and-permanent-redirects-with-apache-and-nginx"  target="_blank" rel="noreferrer">this article explains more</a>.</p>

<h2 class="relative group">Step 8: A little more cleanup (maybe)
    <div id="step-8-a-little-more-cleanup-maybe" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#step-8-a-little-more-cleanup-maybe" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If that works, it&rsquo;s time to check each of your posts for formatting issues. The Ghost plugin for exporting your WordPress posts has a few issues. For one thing it messes with some formatting, especially italics. It converted <code>&lt;em&gt;one&lt;/em&gt; two</code> to <code>*one *two</code> all over, which wreaked havoc with the formatting of my posts.</p>
<p>Also, if you use a plugin that renders extra html output with css styling and such, like <a href="https://wordpress.org/plugins/table-of-contents-plus/"  target="_blank" rel="noreferrer">Table of Contents Plus</a>, it tries to convert the rendered output of that plugin but it doesn&rsquo;t do a great job. There may be some other oddities to clean up too, so you might as well take care of those now.</p>

<h2 class="relative group">Step 9: Configuring Disqus
    <div id="step-9-configuring-disqus" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#step-9-configuring-disqus" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>We&rsquo;re getting into the optional stuff now.</p>
<p>Most people want to encourage discussion on their blogs. There&rsquo;s no built-in commenting engine like WordPress has, but Disqus is popular and it integrates quickly and cleanly. Signup for an account and navigate to the install page for Ghost to get the embed code.</p>
<p><code>https://&lt;your-user-name&gt;.disqus.com/admin/install/platforms/ghost/</code></p>
<p>Although Disqus has instructions on where to place the code, <a href="https://www.ghostforbeginners.com/how-to-enable-comments-on-a-ghost-blog/"  target="_blank" rel="noreferrer">Ghost has their own instructions on enabling comments</a> that differ so I followed those.</p>
<p>There are a couple of variables in the code they provide (commented out by default) that apparently should be changed <em>(</em><a href="https://help.disqus.com/customer/en/portal/articles/2158629"  target="_blank" rel="noreferrer"><em>read more here</em></a><em>),</em> but they only tell you to replace <code>PAGE_IDENTIFIER</code> with <code>{{post.id}}</code>. I think the <code>PAGE_URL</code> placeholder should be replaced with the unique url for the post, but since I&rsquo;m not sure which variable holds that I just left them both commented out for now. It seems to work fine, and correctly loads existing comments from my old blog.</p>
<p>After you modify the file, run <code>service ghost restart</code> from the command line (your SSH session) and wait a few seconds. Check out a post and make sure the Disqus commenting system loads at the bottom.</p>

<h2 class="relative group">Step 10: Syntax Highlighting
    <div id="step-10-syntax-highlighting" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#step-10-syntax-highlighting" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you&rsquo;re frequently posting code snippets on your blog you&rsquo;ll want something that can format it nicely. You can always enclose code in three backticks (<code>```</code>) but Ghost doesn&rsquo;t have syntax highlighting built-in by default. Which is good, because many bloggers won&rsquo;t need it and it&rsquo;d be unnecessary bloat.</p>
<p>There&rsquo;s a couple of javascript libraries you can use - I chose <a href="http://prismjs.com"  target="_blank" rel="noreferrer">Prism.js</a>. It&rsquo;s awesome in that you choose exactly which languages (such as c# or perl) and plugins (such as showing line numbers) you want to have, and it provides the minimal amount of javascript and css for you to copy into your site.</p>
<p>Once you&rsquo;ve downloaded the <code>prism.js</code> and <code>prism.css</code> files, use SFTP to upload them to your server in the <code>themes/casper/assets</code> directory. Modify the <code>default.hbs</code> file in the Casper theme to reference both files in the section (near the top) that indicates it&rsquo;s for scripts and styles. Follow the existing format of using <code>{{assets}}</code> in the links&hellip; I think it helps with caching resources or something.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">{{!-- Styles&#39;n&#39;Scripts --}}
&lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; href=&#34;{{asset &#34;mine/prism.css&#34;}}&#34; /&gt;
&lt;script type=&#34;text/javascript&#34; src=&#34;{{asset &#34;mine/prism.js&#34;}}&#34;&gt;&lt;/script&gt;</code></pre></div>
<p>After including the links, restart Ghost again (<code>service ghost restart</code>) for the changes to take effect. If you need to support additional languages, both files have a custom link on the first line that includes all your selected languages and plugins. Using that link will preselect your current selections, so you can add what you want and then overwrite the files. Here&rsquo;s a sample URL:</p>
<blockquote><p><a href="http://prismjs.com/download.html?themes=prism-coy&amp;languages=csharp&#43;ruby&amp;plugins=line-numbers"  target="_blank" rel="noreferrer">http://prismjs.com/download.html?themes=prism-coy&amp;languages=csharp+ruby&amp;plugins=line-numbers</a></p>
</blockquote><p>Another nice-looking library is <a href="https://highlightjs.org/"  target="_blank" rel="noreferrer">highlight.js</a>. It has the added benefit of providing a single link hosted out on a CDN that provides something like 22 languages out of the box, so there&rsquo;s absolutely nothing to install except referencing that link. If enough other blogs are using that same CDN resource, your visitors may have already downloaded and cached it which makes your site load faster. If the languages you need are in those 22, that&rsquo;s the way to go.</p>

<h2 class="relative group">Step 11: Google analytics
    <div id="step-11-google-analytics" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#step-11-google-analytics" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Some people like to track traffic, and Google analytics can help you do that. You&rsquo;ll <a href="https://support.google.com/analytics/answer/1008080?hl=en"  target="_blank" rel="noreferrer">find instructions here</a> on how to get the analytics script, which you can then copy into the footer of your blog thanks to the &ldquo;Code Injection&rdquo; section of the admin panel.</p>
<p>Bing has its own set of <a href="http://www.bing.com/toolbox/webmaster/"  target="_blank" rel="noreferrer">Webmaster Tools</a> as well. You can get a link to include in the header of your blog via the same &ldquo;Code Injection&rdquo; section.</p>

<h2 class="relative group">Further Reading
    <div id="further-reading" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#further-reading" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>This post has gone on longer than I intended. Here are some other links you may find interesting too&hellip;</p>
<ul>
<li><a href="https://ghost.org/integrations/mailchimp/"  target="_blank" rel="noreferrer">Official Ghost + Mailchimp Integration</a></li>
<li><a href="https://ghost.org/vs/wordpress/"  target="_blank" rel="noreferrer">Ghost vs WordPress</a></li>
<li><a href="https://ghost.org/help/cloudflare-domain-setup/"  target="_blank" rel="noreferrer">Cloudflare domain setup</a></li>
</ul>
<p>Nothing left to do now but to get blogging!</p>
]]></content:encoded><media:content url="https://grantwinney.com/migrating-a-blog-from-wordpress-to-ghost/feature.webp" medium="image" type="image/webp"/></item><item><title>Evaluating a string of code in Erlang at runtime</title><link>https://grantwinney.com/how-to-evaluate-a-string-of-code-in-erlang-at-runtime/</link><pubDate>Sun, 05 Mar 2017 18:56:30 +0000</pubDate><guid>https://grantwinney.com/how-to-evaluate-a-string-of-code-in-erlang-at-runtime/</guid><description/><content:encoded><![CDATA[<p>Did you know that Erlang has the ability to read in a string representing a line of code to execute at runtime? It can parse it out, evaluate it and return the value.</p>
<p>Let&rsquo;s see how&hellip;</p>

<h2 class="relative group">Evaluating Simple Expressions
    <div id="evaluating-simple-expressions" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#evaluating-simple-expressions" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>At its most basic, we can just read any expression passed in and execute it.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">module</span><span class="p">(</span><span class="n">parser</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">export</span><span class="p">([</span>
</span></span><span class="line"><span class="cl">    <span class="n">evaluate_expression</span><span class="o">/</span><span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="p">]).</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">evaluate_expression</span><span class="p">(</span><span class="n">string</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">any</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">evaluate_expression</span><span class="p">(</span><span class="nv">Expression</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="n">ok</span><span class="p">,</span> <span class="nv">Tokens</span><span class="p">,</span> <span class="p">_}</span> <span class="o">=</span> <span class="nn">erl_scan</span><span class="p">:</span><span class="nf">string</span><span class="p">(</span><span class="nv">Expression</span><span class="p">),</span>    <span class="c">% scan the code into tokens
</span></span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="n">ok</span><span class="p">,</span> <span class="nv">Parsed</span><span class="p">}</span> <span class="o">=</span> <span class="nn">erl_parse</span><span class="p">:</span><span class="nf">parse_exprs</span><span class="p">(</span><span class="nv">Tokens</span><span class="p">),</span>     <span class="c">% parse the tokens into an abstract form
</span></span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="n">value</span><span class="p">,</span> <span class="nv">Result</span><span class="p">,</span> <span class="p">_}</span> <span class="o">=</span> <span class="nn">erl_eval</span><span class="p">:</span><span class="nf">exprs</span><span class="p">(</span><span class="nv">Parsed</span><span class="p">,</span> <span class="p">[]),</span>  <span class="c">% evaluate the expression, return the value
</span></span></span><span class="line"><span class="cl">    <span class="nv">Result</span><span class="p">.</span></span></span></code></pre></div></div>
<p>Let&rsquo;s try passing in some simple arithmetic expressions, remembering that statements end in commas and functions with a period, so our strings need to include those punctuations:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="o">&gt;</span> <span class="n">c</span><span class="p">(</span><span class="n">parser</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="n">ok</span><span class="p">,</span><span class="n">parser</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">&gt;</span> <span class="nn">parser</span><span class="p">:</span><span class="nf">evaluate_expression</span><span class="p">(</span><span class="s">&#34;4 &gt; 2.&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="n">true</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;</span> <span class="nn">parser</span><span class="p">:</span><span class="nf">evaluate_expression</span><span class="p">(</span><span class="s">&#34;4+2.&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="mi">6</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;</span> <span class="nn">parser</span><span class="p">:</span><span class="nf">evaluate_expression</span><span class="p">(</span><span class="s">&#34;A=7+2,A-4.&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="mi">5</span></span></span></code></pre></div></div>

<h2 class="relative group">Security Considerations
    <div id="security-considerations" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#security-considerations" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>As might be expected though, there are some serious security pitfalls if you allow just anyone to execute an arbitrary line of code though.</p>

<h3 class="relative group">A brief review of SQL injection attacks
    <div id="a-brief-review-of-sql-injection-attacks" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#a-brief-review-of-sql-injection-attacks" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Let&rsquo;s switch gears for a minute and talk about SQL injection attacks.</p>
<p>We&rsquo;ve just created a UI where a user can just type in their username to see information about themselves. Behind the scenes, we simply take the username as they entered it, and plug it into a query that looks like this:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="s2">&#34;select * from user_table where user_name = &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">username</span></span></span></code></pre></div></div>
<p>When it’s evaluated, it looks something like this, and it returns the record for the user to the page:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="s2">&#34;select * from user_table where user_name = &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s2">&#34;gwinney&#34;</span></span></span></code></pre></div></div>
<p>As long as the user plays nicely, everything okay. But what if they enter their name as <code>gwinney; delete * from user_table</code>? Now the query that’s run ends up looking like this:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">select</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">from</span><span class="w"> </span><span class="n">user_table</span><span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="n">user_name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">gwinney</span><span class="p">;</span><span class="w"> </span><span class="k">delete</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">from</span><span class="w"> </span><span class="n">user_table</span></span></span></code></pre></div></div>
<p>The solution to this is to sanitize all input, aka parameterize the query. I don’t want to dive too deeply into it here, but if we’ve done things the <em>right</em> way then the query looks more like this, which will of course fail because that crazy username doesn’t exist.</p>
<p><code>select * from user_table where user_name = 'gwinney; delete * from user_table'</code></p>

<h3 class="relative group">What’s this have to do with Erlang?
    <div id="whats-this-have-to-do-with-erlang" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whats-this-have-to-do-with-erlang" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Similarly, we can run into security issues with our expression code.</p>
<p>We’re allowed to include a call to <em>any</em> function – local functions as well as BIFs (Erlang’s built-in functions) and exported functions in other modules we’ve created – and it’ll parse and attempt to execute them.</p>
<p>If we make the above function accessible to the outside world, even indirectly, and the input isn’t sanitized, then we’ve handed over the ability for someone to directly call all kinds of functions they have no business calling. Oops.</p>
<p>So how do we prevent that?</p>

<h2 class="relative group">Intercepting Local Function Calls
    <div id="intercepting-local-function-calls" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#intercepting-local-function-calls" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>We can supply a function to <code>erl_eval:exprs</code> through which all calls to local functions will be passed, and that’s where we can take additional actions.</p>
<p>Local functions are those in the same module, which can be called without specifying the module name. Though some BIFs don’t <em>require</em> a module name, like <code>list_to_binary</code>, that&rsquo;s only because they’re auto-imported by the system – they’re still considered non-local.</p>
<p>There’s some new stuff in the code below – a function called <code>handle_local_function</code> and a local function called <code>get_random_number</code> <em>(thanks</em> <a href="https://xkcd.com/221/"  target="_blank" rel="noreferrer"><em>xkcd</em></a><em>)</em>. The handler function outputs an informational message and then handles the passed-in function name.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">module</span><span class="p">(</span><span class="n">parser</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">export</span><span class="p">([</span>
</span></span><span class="line"><span class="cl">    <span class="n">evaluate_expression</span><span class="o">/</span><span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="p">]).</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">evaluate_expression</span><span class="p">(</span><span class="n">string</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">any</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">evaluate_expression</span><span class="p">(</span><span class="nv">Expression</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="n">ok</span><span class="p">,</span> <span class="nv">Tokens</span><span class="p">,</span> <span class="p">_}</span> <span class="o">=</span> <span class="nn">erl_scan</span><span class="p">:</span><span class="nf">string</span><span class="p">(</span><span class="nv">Expression</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="n">ok</span><span class="p">,</span> <span class="nv">Parsed</span><span class="p">}</span> <span class="o">=</span> <span class="nn">erl_parse</span><span class="p">:</span><span class="nf">parse_exprs</span><span class="p">(</span><span class="nv">Tokens</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="n">value</span><span class="p">,</span> <span class="nv">Result</span><span class="p">,</span> <span class="p">_}</span> <span class="o">=</span> <span class="nn">erl_eval</span><span class="p">:</span><span class="nf">exprs</span><span class="p">(</span><span class="nv">Parsed</span><span class="p">,</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">                                        <span class="p">{</span><span class="n">value</span><span class="p">,</span> <span class="k">fun</span> <span class="n">handle_local_function</span><span class="o">/</span><span class="mi">2</span><span class="p">}),</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Result</span><span class="p">.</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">handle_local_function</span><span class="p">(</span><span class="n">atom</span><span class="p">(),</span> <span class="n">list</span><span class="p">())</span> <span class="o">-&gt;</span> <span class="n">any</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">handle_local_function</span><span class="p">(</span><span class="nv">FunctionName</span><span class="p">,</span> <span class="nv">Arguments</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nn">io</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34;Local call to </span><span class="si">~p</span><span class="s"> with </span><span class="si">~p~n</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[</span><span class="nv">FunctionName</span><span class="p">,</span> <span class="nv">Arguments</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nv">FunctionName</span> <span class="k">of</span>
</span></span><span class="line"><span class="cl">        <span class="n">get_random_number</span> <span class="o">-&gt;</span> <span class="n">get_random_number</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">what_time_is_it</span> <span class="o">-&gt;</span> <span class="nn">calendar</span><span class="p">:</span><span class="nf">universal_time</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">are_we_there_yet</span> <span class="o">-&gt;</span> <span class="s">&#34;no&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">_</span> <span class="o">-&gt;</span> <span class="s">&#34;uh uh uh. you didn&#39;t say the magic word!&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">.</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">get_random_number</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="n">integer</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_random_number</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="mi">4</span><span class="p">.</span>  <span class="err">%</span> <span class="n">chosen</span> <span class="n">by</span> <span class="n">fair</span> <span class="n">dice</span> <span class="n">roll</span><span class="p">;</span> <span class="n">guaranteed</span> <span class="n">to</span> <span class="n">be</span> <span class="n">random</span></span></span></code></pre></div></div>
<p>Let&rsquo;s run the module again and pass in some new expressions.</p>
<p>We can intercept local functions (which may not really exist, but the expression evaluator doesn’t know that) and redirect them as we please… or just spit out a message if the user tries to do something invalid:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="o">&gt;</span> <span class="n">c</span><span class="p">(</span><span class="n">parser</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="n">ok</span><span class="p">,</span><span class="n">parser</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">&gt;</span> <span class="nn">parser</span><span class="p">:</span><span class="nf">evaluate_expression</span><span class="p">(</span><span class="s">&#34;get_random_number().&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="nv">Local</span> <span class="n">call</span> <span class="n">to</span> <span class="n">get_random_number</span> <span class="n">with</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"><span class="mi">4</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">&gt;</span> <span class="nn">parser</span><span class="p">:</span><span class="nf">evaluate_expression</span><span class="p">(</span><span class="s">&#34;what_time_is_it().&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="nv">Local</span> <span class="n">call</span> <span class="n">to</span> <span class="n">what_time_is_it</span> <span class="n">with</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"><span class="p">{{</span><span class="mi">2017</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">5</span><span class="p">},{</span><span class="mi">15</span><span class="p">,</span><span class="mi">21</span><span class="p">,</span><span class="mi">53</span><span class="p">}}</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">&gt;</span> <span class="nn">parser</span><span class="p">:</span><span class="nf">evaluate_expression</span><span class="p">(</span><span class="s">&#34;are_we_there_yet().&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="nv">Local</span> <span class="n">call</span> <span class="n">to</span> <span class="n">are_we_there_yet</span> <span class="n">with</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"><span class="s">&#34;no&#34;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="nn">parser</span><span class="p">:</span><span class="nf">evaluate_expression</span><span class="p">(</span><span class="s">&#34;break_the_system().&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="nv">Local</span> <span class="n">call</span> <span class="n">to</span> <span class="n">break_the_system</span> <span class="n">with</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"><span class="s">&#34;uh uh uh. you didn&#39;t say the magic word!&#34;</span></span></span></code></pre></div></div>

<h2 class="relative group">Intercepting Non-Local Function Calls
    <div id="intercepting-non-local-function-calls" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#intercepting-non-local-function-calls" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Similarly, we can supply a function to <code>erl_eval:exprs</code> through which all calls to _<strong>non-</strong>_local functions will be passed <em>(anything outside of the current module, including BIFs and even the operators used in comparisons).</em></p>
<p>Here’s the code again, extended to handle non-local functions:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">module</span><span class="p">(</span><span class="n">parser</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">export</span><span class="p">([</span>
</span></span><span class="line"><span class="cl">    <span class="n">evaluate_expression</span><span class="o">/</span><span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="p">]).</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">evaluate_expression</span><span class="p">(</span><span class="n">string</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">any</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">evaluate_expression</span><span class="p">(</span><span class="nv">Expression</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="n">ok</span><span class="p">,</span> <span class="nv">Tokens</span><span class="p">,</span> <span class="p">_}</span> <span class="o">=</span> <span class="nn">erl_scan</span><span class="p">:</span><span class="nf">string</span><span class="p">(</span><span class="nv">Expression</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="n">ok</span><span class="p">,</span> <span class="nv">Parsed</span><span class="p">}</span> <span class="o">=</span> <span class="nn">erl_parse</span><span class="p">:</span><span class="nf">parse_exprs</span><span class="p">(</span><span class="nv">Tokens</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="n">value</span><span class="p">,</span> <span class="nv">Result</span><span class="p">,</span> <span class="p">_}</span> <span class="o">=</span> <span class="nn">erl_eval</span><span class="p">:</span><span class="nf">exprs</span><span class="p">(</span><span class="nv">Parsed</span><span class="p">,</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">                                        <span class="p">{</span><span class="n">value</span><span class="p">,</span> <span class="k">fun</span> <span class="n">handle_local_function</span><span class="o">/</span><span class="mi">2</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">                                        <span class="p">{</span><span class="n">value</span><span class="p">,</span> <span class="k">fun</span> <span class="n">handle_non_local_function</span><span class="o">/</span><span class="mi">2</span><span class="p">}),</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Result</span><span class="p">.</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">handle_local_function</span><span class="p">(</span><span class="n">atom</span><span class="p">(),</span> <span class="n">list</span><span class="p">())</span> <span class="o">-&gt;</span> <span class="n">any</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">handle_local_function</span><span class="p">(</span><span class="nv">FunctionName</span><span class="p">,</span> <span class="nv">Arguments</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nn">io</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34;Local call to </span><span class="si">~p</span><span class="s"> with </span><span class="si">~p~n</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[</span><span class="nv">FunctionName</span><span class="p">,</span> <span class="nv">Arguments</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nv">FunctionName</span> <span class="k">of</span>
</span></span><span class="line"><span class="cl">        <span class="n">get_random_number</span> <span class="o">-&gt;</span> <span class="n">get_random_number</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">what_time_is_it</span> <span class="o">-&gt;</span> <span class="nn">calendar</span><span class="p">:</span><span class="nf">universal_time</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">are_we_there_yet</span> <span class="o">-&gt;</span> <span class="s">&#34;no&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">_</span> <span class="o">-&gt;</span> <span class="s">&#34;uh uh uh. you didn&#39;t say the magic word!&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">.</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">handle_non_local_function</span><span class="p">(</span><span class="n">atom</span><span class="p">(),</span> <span class="n">list</span><span class="p">())</span> <span class="o">-&gt;</span> <span class="n">any</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">handle_non_local_function</span><span class="p">({</span><span class="nv">ModuleName</span><span class="p">,</span><span class="nv">FunctionName</span><span class="p">},</span> <span class="nv">Arguments</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nn">io</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34;Non-local call to </span><span class="si">~p</span><span class="s"> with </span><span class="si">~p~n</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[</span><span class="nv">FunctionName</span><span class="p">,</span> <span class="nv">Arguments</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nv">ModuleName</span> <span class="k">of</span>
</span></span><span class="line"><span class="cl">        <span class="n">erlang</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="k">case</span> <span class="nv">FunctionName</span> <span class="k">of</span>
</span></span><span class="line"><span class="cl">                <span class="n">&#39;&gt;&#39;</span> <span class="o">-&gt;</span> <span class="nb">apply</span><span class="p">(</span><span class="nv">ModuleName</span><span class="p">,</span> <span class="nv">FunctionName</span><span class="p">,</span> <span class="nv">Arguments</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="n">&#39;&lt;&#39;</span> <span class="o">-&gt;</span> <span class="nb">apply</span><span class="p">(</span><span class="nv">ModuleName</span><span class="p">,</span> <span class="nv">FunctionName</span><span class="p">,</span> <span class="nv">Arguments</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="nb">list_to_binary</span> <span class="o">-&gt;</span> <span class="nb">apply</span><span class="p">(</span><span class="nv">ModuleName</span><span class="p">,</span> <span class="nv">FunctionName</span><span class="p">,</span> <span class="nv">Arguments</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="p">_</span> <span class="o">-&gt;</span> <span class="s">&#34;nope&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="k">end</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">calendar</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="k">case</span> <span class="nv">FunctionName</span> <span class="k">of</span>
</span></span><span class="line"><span class="cl">                <span class="n">universal_time</span> <span class="o">-&gt;</span> <span class="nn">calendar</span><span class="p">:</span><span class="nf">universal_time</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">                <span class="n">lets_pretend_this_returns_four</span> <span class="o">-&gt;</span> <span class="mi">4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="n">something_ridiculous</span> <span class="o">-&gt;</span> <span class="s">&#34;what calendar are you using??&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="p">_</span> <span class="o">-&gt;</span> <span class="s">&#34;notgonnahappen&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="k">end</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">_</span> <span class="o">-&gt;</span> <span class="s">&#34;don&#39;t think about it&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">.</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">spec</span> <span class="n">get_random_number</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="n">integer</span><span class="p">().</span>
</span></span><span class="line"><span class="cl"><span class="nf">get_random_number</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="mi">4</span><span class="p">.</span>  <span class="err">%</span> <span class="n">chosen</span> <span class="n">by</span> <span class="n">fair</span> <span class="n">dice</span> <span class="n">roll</span><span class="p">;</span> <span class="n">guaranteed</span> <span class="n">to</span> <span class="n">be</span> <span class="n">random</span></span></span></code></pre></div></div>
<p>Note how we explicitly handle the <code>&gt;</code> and <code>&lt;</code> comparison operators that are part of the erlang module, how we can redirect non-existent functions to existing ones, and how we can display a message if a function is unsupported.</p>
<p>Greater than and less than comparisons are allowed, but not equality… because. Some functions are allowed, some aren’t, and some are redirected. In the last example below, an evil user tries to enact their nefarious plan to take part of the system down, but is foiled. :p</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="o">&gt;</span> <span class="n">c</span><span class="p">(</span><span class="n">parser</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="n">ok</span><span class="p">,</span><span class="n">parser</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">&gt;</span> <span class="nn">parser</span><span class="p">:</span><span class="nf">evaluate_expression</span><span class="p">(</span><span class="s">&#34;4 &lt; 2.&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="nv">Non</span><span class="o">-</span><span class="n">local</span> <span class="n">call</span> <span class="n">to</span> <span class="n">&#39;&lt;&#39;</span> <span class="n">with</span> <span class="p">[</span><span class="mi">4</span><span class="p">,</span><span class="mi">2</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="n">false</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">&gt;</span> <span class="nn">parser</span><span class="p">:</span><span class="nf">evaluate_expression</span><span class="p">(</span><span class="s">&#34;4 &gt; 2.&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="nv">Non</span><span class="o">-</span><span class="n">local</span> <span class="n">call</span> <span class="n">to</span> <span class="n">&#39;&gt;&#39;</span> <span class="n">with</span> <span class="p">[</span><span class="mi">4</span><span class="p">,</span><span class="mi">2</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="n">true</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">&gt;</span> <span class="nn">parser</span><span class="p">:</span><span class="nf">evaluate_expression</span><span class="p">(</span><span class="s">&#34;4 == 2.&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="nv">Non</span><span class="o">-</span><span class="n">local</span> <span class="n">call</span> <span class="n">to</span> <span class="n">&#39;==&#39;</span> <span class="n">with</span> <span class="p">[</span><span class="mi">4</span><span class="p">,</span><span class="mi">2</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="s">&#34;nope&#34;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">&gt;</span> <span class="nn">parser</span><span class="p">:</span><span class="nf">evaluate_expression</span><span class="p">(</span><span class="s">&#34;list_to_binary(</span><span class="se">\&#34;</span><span class="s">hi</span><span class="se">\&#34;</span><span class="s">).&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="nv">Non</span><span class="o">-</span><span class="n">local</span> <span class="n">call</span> <span class="n">to</span> <span class="nb">list_to_binary</span> <span class="n">with</span> <span class="p">[</span><span class="s">&#34;hi&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;&lt;</span><span class="s">&#34;hi&#34;</span><span class="o">&gt;&gt;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">&gt;</span> <span class="nn">parser</span><span class="p">:</span><span class="nf">evaluate_expression</span><span class="p">(</span><span class="s">&#34;binary_to_list(&lt;&lt;</span><span class="se">\&#34;</span><span class="s">hi</span><span class="se">\&#34;</span><span class="s">&gt;&gt;).&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="nv">Non</span><span class="o">-</span><span class="n">local</span> <span class="n">call</span> <span class="n">to</span> <span class="nb">binary_to_list</span> <span class="n">with</span> <span class="p">[</span><span class="o">&lt;&lt;</span><span class="s">&#34;hi&#34;</span><span class="o">&gt;&gt;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="s">&#34;nope&#34;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">&gt;</span> <span class="nn">parser</span><span class="p">:</span><span class="nf">evaluate_expression</span><span class="p">(</span><span class="s">&#34;calendar:universal_time().&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="nv">Non</span><span class="o">-</span><span class="n">local</span> <span class="n">call</span> <span class="n">to</span> <span class="n">universal_time</span> <span class="n">with</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"><span class="p">{{</span><span class="mi">2017</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">5</span><span class="p">},{</span><span class="mi">21</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">42</span><span class="p">}}</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">&gt;</span> <span class="nn">parser</span><span class="p">:</span><span class="nf">evaluate_expression</span><span class="p">(</span><span class="s">&#34;calendar:local_time().&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="nv">Non</span><span class="o">-</span><span class="n">local</span> <span class="n">call</span> <span class="n">to</span> <span class="n">local_time</span> <span class="n">with</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"><span class="s">&#34;notgonnahappen&#34;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">&gt;</span> <span class="nn">parser</span><span class="p">:</span><span class="nf">evaluate_expression</span><span class="p">(</span><span class="s">&#34;calendar:lets_pretend_this_returns_four().&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="nv">Non</span><span class="o">-</span><span class="n">local</span> <span class="n">call</span> <span class="n">to</span> <span class="n">lets_pretend_this_returns_four</span> <span class="n">with</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"><span class="mi">4</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">&gt;</span> <span class="nn">parser</span><span class="p">:</span><span class="nf">evaluate_expression</span><span class="p">(</span><span class="s">&#34;calendar:something_ridiculous().&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="nv">Non</span><span class="o">-</span><span class="n">local</span> <span class="n">call</span> <span class="n">to</span> <span class="n">something_ridiculous</span> <span class="n">with</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"><span class="s">&#34;what calendar are you using??&#34;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">&gt;</span> <span class="nn">parser</span><span class="p">:</span><span class="nf">evaluate_expression</span><span class="p">(</span><span class="s">&#34;sys:terminate(some_process, </span><span class="se">\&#34;</span><span class="s">buahaha</span><span class="se">\&#34;</span><span class="s">).&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="nv">Non</span><span class="o">-</span><span class="n">local</span> <span class="n">call</span> <span class="n">to</span> <span class="n">terminate</span> <span class="n">with</span> <span class="p">[</span><span class="n">some_process</span><span class="p">,</span><span class="s">&#34;buahaha&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="s">&#34;don&#39;t think about it&#34;</span></span></span></code></pre></div></div>

<h2 class="relative group">What&rsquo;s Next?
    <div id="whats-next" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whats-next" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Good examples in Erlang can be hard to come by, and what you see here was a fair amount of trial and error. If you find yourself trying to parse code and execute it at runtime, maybe this’ll help.</p>
<p>Other resources to check out:</p>
<ul>
<li>Official docs: <a href="http://erlang.org/doc/man/erl_scan.html"  target="_blank" rel="noreferrer">erl_scan</a>, <a href="http://erlang.org/doc/man/erl_parse.html"  target="_blank" rel="noreferrer">erl_parse</a>, <a href="http://erlang.org/doc/man/erl_eval.html"  target="_blank" rel="noreferrer">erl_eval</a> <em>(have a pot of coffee ready)</em></li>
<li><a href="http://people.apache.org/~dennisbyrne/infoq/DSLs_in_Erlang.ppt"  target="_blank" rel="noreferrer">Domain Specific Languages in Erlang</a> <em>(powerpoint presentation)</em></li>
<li><a href="http://stackoverflow.com/q/6786034"  target="_blank" rel="noreferrer">Can parameterized statement stop all SQL injection?</a> <em>(a thread with more details)</em></li>
</ul>
]]></content:encoded><media:content url="https://grantwinney.com/how-to-evaluate-a-string-of-code-in-erlang-at-runtime/feature.webp" medium="image" type="image/webp"/></item><item><title>What is charlieplexing? (a Raspberry Pi demo)</title><link>https://grantwinney.com/what-is-charlieplexing-a-short-demo-using-the-raspberry-pi/</link><pubDate>Fri, 17 Feb 2017 09:03:29 +0000</pubDate><guid>https://grantwinney.com/what-is-charlieplexing-a-short-demo-using-the-raspberry-pi/</guid><description>On past projects, when I needed multiple LEDs, I just connected each to its own GPIO pin. I knew the current only worked in one direction, but I didn&amp;rsquo;t think to take advantage of that fact. Charlieplexing is a method for arranging multiple LEDs so as to use the minimal number of pins possible.</description><content:encoded><![CDATA[<p>On past projects, when I&rsquo;ve needed multiple LEDs (like in my <a href="https://grantwinney.com/raspberry-pi-simon-game-clone/"  target="_blank" rel="noreferrer">Simon clone</a>), I just connected each individual LED to its own GPIO pin. I was aware that current had to travel through the LED in one direction and that it wouldn’t light if wired in the other direction, but it hadn’t occurred to me to take advantage of that fact.</p>
<p>This is where <a href="https://en.wikipedia.org/wiki/Charlieplexing"  target="_blank" rel="noreferrer">charlieplexing</a> comes in. We can arrange multiple LEDs such that we use the minimal number of GPIO pins possible.</p>

<h2 class="relative group">Connecting 2 LEDs to 2 GPIO pins
    <div id="connecting-2-leds-to-2-gpio-pins" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#connecting-2-leds-to-2-gpio-pins" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If we connect 2 LEDs to the same 2 GPIO pins, but with their anode and cathode reversed from one another, then only one will light at a time. To do this, set two GPIO pins (24 and 25 in my example) to GPIO.OUT, but one to HIGH (or 1) and the other to LOW (or 0).</p>
<p>By flip-flopping which one is HIGH and which one is LOW, we can turn one LED on at a time, making them blink opposite each other. If we do this fast enough, the on/off switching is still taking place but so rapidly that they appear to be simultaneously “on” to the human eye.</p>
<p>Try it out by setting up your circuit like the diagram and images below. You’ll need a couple resistors – 220 ohm is okay, 470 ohm is probably better – and a couple LEDs. I used a wire because really you only need one resistor in the path (it doesn’t matter <em>where</em> in the path it is – between the power source and LED, or between the LED and ground).</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-charlieplexing-a-short-demo-using-the-raspberry-pi/fritzing-charlieplexing-2-in-2.png"
    width="652"
      height="490"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-charlieplexing-a-short-demo-using-the-raspberry-pi/charlieplexing-2-2-1.jpg"
    width="1407"
      height="909"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-charlieplexing-a-short-demo-using-the-raspberry-pi/charlieplexing-2-2-2.jpg"
    width="1092"
      height="1059"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-charlieplexing-a-short-demo-using-the-raspberry-pi/charlieplexing-2-2-3.jpg"
    width="1253"
      height="821"></figure>
<p>Once the circuit is set up correctly, <a href="https://github.com/grantwinney/52-Weeks-of-Pi/blob/master/09-Charlieplexing-LEDs/charlieplexing-2-on-2.py"  target="_blank" rel="noreferrer">run this script</a>.</p>
<p>Here’s the important part – it sets the pins and flips the direction of the current every tenth of a second.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">GPIO</span><span class="o">.</span><span class="n">setup</span><span class="p">(</span><span class="n">PIN_A</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">OUT</span><span class="p">,</span> <span class="n">initial</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">GPIO</span><span class="o">.</span><span class="n">setup</span><span class="p">(</span><span class="n">PIN_B</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">OUT</span><span class="p">,</span> <span class="n">initial</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl"><span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">output</span><span class="p">(</span><span class="n">PIN_A</span><span class="p">,</span> <span class="ow">not</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">input</span><span class="p">(</span><span class="n">PIN_A</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">output</span><span class="p">(</span><span class="n">PIN_B</span><span class="p">,</span> <span class="ow">not</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">input</span><span class="p">(</span><span class="n">PIN_B</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">.01</span><span class="p">)</span></span></span></code></pre></div></div>

<h2 class="relative group">Connecting 6 LEDs to 3 GPIO pins
    <div id="connecting-6-leds-to-3-gpio-pins" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#connecting-6-leds-to-3-gpio-pins" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Connecting 2 LEDs to 2 pins doesn’t buy us anything, since we would’ve had to use 2 pins anyway.</p>
<p>But what if we use 3 GPIO pins? Then we need to make a change – they can’t <em>all</em> be set to GPIO.OUT. One pin is set to HIGH and one to LOW to turn a specific LED on. If the third pin is also set to HIGH or LOW, because of the way the LEDs are all interconnected (see the diagram below), it’s going to have a negative effect on our circuit.</p>
<p>Instead, the third pin needs to be set in such a way that it’s effectively removed from the circuit, and we can do that by setting it to input (GPIO.IN).</p>
<p>Check out the diagram below. Notice how, if pin 25 is HIGH, it affects two LEDs – the anode of both the pink and yellow LEDs are on pin 25. You can set pin 24 to LOW to light the yellow LED or set pin 23 to LOW to light the pink. But since we only want one at a time, either pin 23 or 24 has to be set to GPIO.IN so the LED connected to it won’t light up.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-charlieplexing-a-short-demo-using-the-raspberry-pi/fritzing-charlieplexing-6-in-3.png"
    width="630"
      height="490"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="charlieplexing-6-3-1.jpg"
    ></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-charlieplexing-a-short-demo-using-the-raspberry-pi/charlieplexing-6-3-2.jpg"
    width="1503"
      height="1089"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/what-is-charlieplexing-a-short-demo-using-the-raspberry-pi/charlieplexing-6-3-3.jpg"
    width="1439"
      height="1096"></figure>
<p>Setup a circuit like the diagrams and images above, then <a href="https://github.com/grantwinney/52-Weeks-of-Pi/blob/master/09-Charlieplexing-LEDs/charlieplexing-6-on-3.py"  target="_blank" rel="noreferrer">run this script</a>.</p>
<p>The main section of the code iterates through an array that specifies which pins are input, high output and low output, and adjusts the pins every tenth of a second so that each LED is flashed on and off in turn.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">led</span> <span class="ow">in</span> <span class="n">LEDS</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">idx</span><span class="p">,</span> <span class="n">pin</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">led</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">pin</span> <span class="o">==</span> <span class="n">O</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">GPIO</span><span class="o">.</span><span class="n">setup</span><span class="p">(</span><span class="n">PINS</span><span class="p">[</span><span class="n">idx</span><span class="p">],</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">IN</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">GPIO</span><span class="o">.</span><span class="n">setup</span><span class="p">(</span><span class="n">PINS</span><span class="p">[</span><span class="n">idx</span><span class="p">],</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">OUT</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="n">GPIO</span><span class="o">.</span><span class="n">output</span><span class="p">(</span><span class="n">PINS</span><span class="p">[</span><span class="n">idx</span><span class="p">],</span> <span class="n">pin</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">.1</span><span class="p">)</span></span></span></code></pre></div></div>

<h2 class="relative group">Seeing it in Action
    <div id="seeing-it-in-action" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#seeing-it-in-action" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://res.cloudinary.com/dxm4riq52/video/upload/v1583296460/Raspberry%20Pi/What_is_charlieplexing__Let_s_find_out_using_the_Raspberry_Pi_eveb2a.mp4"  target="_blank" rel="noreferrer">Here’s a short video I took</a>, showing both configurations in action&hellip; and if you find this useful, or have used charlieplexing in a project of your own, or just have something to share that I left out and others should know, please share your thoughts below!</p>
]]></content:encoded><media:content url="https://grantwinney.com/what-is-charlieplexing-a-short-demo-using-the-raspberry-pi/feature.webp" medium="image" type="image/webp"/></item><item><title>How to Create a Git Alias</title><link>https://grantwinney.com/creating-a-git-alias/</link><pubDate>Sat, 28 Jan 2017 13:07:51 +0000</pubDate><guid>https://grantwinney.com/creating-a-git-alias/</guid><description>If you&amp;rsquo;re unfamiliar with Git&amp;rsquo;s &amp;ldquo;alias&amp;rdquo; feature, it provides a way to create shortcuts for other Git commands, which can save you a lot of time. They’re easy to setup and maintain too. Let&amp;rsquo;s see how.</description><content:encoded><![CDATA[<p>If you&rsquo;re unfamiliar with Git&rsquo;s &ldquo;alias&rdquo; feature, it provides a way to easily create shortcuts for other Git commands. It can save a <em>lot</em> of time over calling some lengthy command that&rsquo;s tough to remember.. or even shorter ones that are used frequently.</p>

<h2 class="relative group">Using Aliases for Shortcuts
    <div id="using-aliases-for-shortcuts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#using-aliases-for-shortcuts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>We can create aliases for short commands, like assigning &ldquo;checkout&rdquo; to &ldquo;co&rdquo;:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git config --global alias.co checkout</span></span></code></pre></div></div>
<p>Or longer commands, like this one that displays a unique log view:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git config --global alias.hist <span class="s2">&#34;log --pretty=format:&#39;%h %ad | %s%d [%an]&#39; --graph --date=short&#34;</span></span></span></code></pre></div></div>
<p>And what if we&rsquo;re the sole developer of some project and want to add, commit and push files in one go?</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git config alias.shove <span class="s1">&#39;!git add . &amp;&amp; git commit --allow-empty-message -m &#34;&#34; &amp;&amp; git push&#39;</span></span></span></code></pre></div></div>
<p>I’d recommend using that last one only on repos you maintain by yourself, since you’ll want to be more careful on team projects. That’s why I omitted <code>--global</code>.</p>
<p>Alternatively, we could add a <code>$1</code> place-holder to force a commit message. <em>(The final colon is a bit of a hack, but</em> <a href="http://stackoverflow.com/a/25915221/301857"  target="_blank" rel="noreferrer"><em>there’s a reason for it</em></a><em>.)</em></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git config --global alias.cshove <span class="s1">&#39;!git add . &amp;&amp; git commit -m &#34;$1&#34; &amp;&amp; git push &amp;&amp; :&#39;</span></span></span></code></pre></div></div>
<p>Any aliases we configure are added into:</p>
<ul>
<li>the global <code>.gitconfig</code> file if we use <code>--global</code>, or</li>
<li>a single repository’s <code>.git/config</code> file if we use <code>--local</code> or just omit the modifier <em>(</em><a href="http://stackoverflow.com/a/2115116/301857"  target="_blank" rel="noreferrer"><em>learn more here</em></a><em>)</em></li>
</ul>
<p>Here’s what it looks like in the config file itself. The <code>git config alias</code> line above is just for convenience, but we can edit the file manually too.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">[alias]
    co = checkout
    hist = log --pretty=format:&#39;%h %ad | %s%d [%an]&#39; --graph --date=short
    shove = !git add . &amp;&amp; git commit --allow-empty-message -m \&#34;\&#34; &amp;&amp; git push
    cshove = !git add . &amp;&amp; git commit -m \&#34;$1\&#34; &amp;&amp; git push &amp;&amp; :</code></pre></div>
<p>When we define aliases, we use them like any other git command, i.e. by calling <code>git shove</code>.</p>

<h2 class="relative group">Using Aliases for Typos
    <div id="using-aliases-for-typos" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#using-aliases-for-typos" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There’s another good use for aliases too – fixing common typos.</p>
<p>By default, Git suggests corrections when it catches a typo, but takes no further action. Optionally, we can tell it to just run the autocorrected command when it has a single suggestion:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">&gt; git config --global help.autocorrect 30
 
&gt; git pulll
WARNING: You called a Git command named &#39;pulll&#39;, which does not exist.
Continuing under the assumption that you meant &#39;pull&#39;
in 3.0 seconds automatically...
Already up-to-date.</code></pre></div>
<p>If we don&rsquo;t want to do that though, which could be a dangerous move, especially if the time is set so low it can&rsquo;t be canceled in time, we can setup aliases for our own common typos.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">&gt; git config --global alias.pul pull
&gt; git pul
Already up-to-date.
 
&gt; git config --global alias.puhs push
&gt; git puhs
Everything up-to-date</code></pre></div>
<p>Here we&rsquo;ve told Git to run <code>git pull</code> when we type <code>git pul</code> by accident, and the same with <code>git push</code> and the typo <code>git puhs</code>.</p>

<h2 class="relative group">What’s Next?
    <div id="whats-next" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whats-next" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I’ve added a few aliases <em>I</em> find useful to my <a href="https://github.com/grantwinney/BlogCodeSamples/tree/master/DevTools/GitAliasTemplate"  target="_blank" rel="noreferrer">BlogCodeSamples repo</a>, so I don’t lose them. Maybe you’ll find them useful too&hellip;</p>
<p>To use them, clone the above repo or just copy the two <code>.gitconfig</code> files somewhere locally. You can reference external files from within your own <code>.gitconfig</code> file, which leaves your existing aliases and other settings untouched. Add the following section to your <code>.gitconfig</code> file, where <code>/your/path</code> is wherever you copied the files to.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">[include]
    path = &#34;/your/path/git-alias-template/alias-shortcuts.gitconfig&#34;
    path = &#34;/your/path/git-alias-template/alias-typos.gitconfig&#34;
    # etc...</code></pre></div>
<p>The <code>include</code> section <a href="https://git-scm.com/docs/git-config#_includes"  target="_blank" rel="noreferrer">imports settings from other files</a> and merges their functionality without affecting the original config file. You can read more about aliases at <a href="https://githowto.com/aliases"  target="_blank" rel="noreferrer">GitHowTo: Aliases</a> or <a href="https://git-scm.com/book/en/v2/Git-Basics-Git-Aliases"  target="_blank" rel="noreferrer">Git Basics – Git Aliases</a>.</p>
<p>If you have any helpful aliases of your own, feel free to share below! And if you’d like to learn even more interesting features of Git, you may want to check out the <a href="https://github.com/tiimgreen/github-cheat-sheet"  target="_blank" rel="noreferrer">GitHub Cheat Sheet</a>.</p>
]]></content:encoded><media:content url="https://grantwinney.com/creating-a-git-alias/feature.webp" medium="image" type="image/webp"/></item><item><title>5 Things You Can Do With a Locally Cloned GitHub Wiki</title><link>https://grantwinney.com/5-things-you-can-do-with-a-locally-cloned-github-wiki/</link><pubDate>Mon, 16 Jan 2017 07:52:51 +0000</pubDate><guid>https://grantwinney.com/5-things-you-can-do-with-a-locally-cloned-github-wiki/</guid><description>There’s a feature of every GitHub repo that in my experience doesn’t get a ton of love, and that&amp;rsquo;s the wiki. In all fairness, I&amp;rsquo;m not sure how much love it deserves - it&amp;rsquo;s sorely lacking in features. But did you know it&amp;rsquo;s a separate repo that you can clone and manipulate locally?</description><content:encoded><![CDATA[<p>For software developers, GitHub is a useful (possibly even indispensable) tool. We use it for our personal projects, finding new libraries to use, and collaborating as a team. There’s a feature of every GitHub repo that I hear little about and rarely think about – the wiki.</p>
<p>We can take notes in it and link pages together, but the GitHub wiki&rsquo;s lack of short-codes/widgets (i.e. adding a table of contents to the top of each page) and other basic features (like uploading images via the UI) makes it less useful than it could be.</p>
<p>For nearly a year, I used it at work for internal team documentation, and its shortcomings were frustrating. Then I discovered something that seems to open the door to new possibilities. <strong>A GitHub wiki is just another repo, which means it can be cloned locally and manipulated with other tools.</strong></p>

<h2 class="relative group">Cloning a GitHub Wiki
    <div id="cloning-a-github-wiki" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#cloning-a-github-wiki" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>GitHub uses a wiki system called <a href="https://github.com/gollum/gollum"  target="_blank" rel="noreferrer">Gollum</a>, which is built on top of Git and stores its files in a Git repository. In other words, each repository&rsquo;s wiki is itself a <em>separate</em> Git repository. We can clone a wiki, alter it, and commit our changes to it just like any other repo.</p>
<p>To clone a wiki, we can find the link conveniently shoved into the lower-right corner of the page, way near the bottom if there&rsquo;s already a lot of wiki pages in it.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/5-things-you-can-do-with-a-locally-cloned-github-wiki/image-4.png"
    width="273"
      height="120"></figure>
<p>We&rsquo;re presented with the &ldquo;https&rdquo; link, but can change it to the SSH link if needed.</p>
<ul>
<li>https: <code>https://github.com/your-account/your-project.wiki.git</code></li>
<li>ssh: <code>git clone git@github.com:your-account/your-project.wiki.git</code></li>
</ul>

<h2 class="relative group">Managing a Local Wiki
    <div id="managing-a-local-wiki" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#managing-a-local-wiki" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Now that we can clone a wiki locally, let&rsquo;s see what we can do with it.</p>

<h3 class="relative group">Edit a Wiki Offline (using Gollum)
    <div id="edit-a-wiki-offline-using-gollum" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#edit-a-wiki-offline-using-gollum" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>As mentioned, GitHub uses Gollum to power its wiki. We can install Gollum locally to browse and edit a (cloned) wiki too, which means it&rsquo;s possible to edit pages without an Internet connection and push them back up later.</p>
<p>Installing Gollum differs based on the environment it&rsquo;s going to run in, which is <a href="https://github.com/gollum/gollum?tab=readme-ov-file#installation"  target="_blank" rel="noreferrer">detailed in the readme file</a>. For us Windows users, it requires <a href="https://github.com/jruby/jruby/wiki/GettingStarted"  target="_blank" rel="noreferrer">JRuby</a>, which in turn requires Java. And although the JRuby site indicates that we just need to decide on <em>&ldquo;32- or 64-bit, and whether to bundle the Java Virtual Machine&rdquo;,</em> there&rsquo;s no option to bundle it and the installer complains if it&rsquo;s missing.</p>
<p>First, head to the <a href="https://www.java.com/en/"  target="_blank" rel="noreferrer">Java download page</a> and install the JRE, which includes the JVM that JRuby needs. Next, head to the <a href="https://www.jruby.org/download"  target="_blank" rel="noreferrer">JRuby download page</a> and install the x64 Windows exe with Ruby 3.1.x support and accept the defaults. At this point, we can verify in PowerShell that they&rsquo;re both installed:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="A terminal window showing Java and JRuby are installed"
    src="/5-things-you-can-do-with-a-locally-cloned-github-wiki/image-7.png"
    width="824"
      height="191"></figure>
<p>Verifying Java and JRuby are installed</p>
<p>Finally, start Windows PowerShell as an administrator and run <code>gem install gollum</code> to install Gollum – in other terminals, or in a non-admin PS window, it may fail. This took awhile for me, and seemed to lag on certain steps, but it eventually completed:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Console output from installing the Gollum gem"
    src="/5-things-you-can-do-with-a-locally-cloned-github-wiki/image-9.png"
    width="950"
      height="360"></figure>
<p>Let&rsquo;s change to the directory where the wiki is cloned and type &ldquo;gollum&rdquo; to fire up the Gollum server, which should show us which port it&rsquo;s running the site on:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Console output showing that Gollum is running"
    src="/5-things-you-can-do-with-a-locally-cloned-github-wiki/image-10.png"
    width="878"
      height="170"></figure>
<p>Open up the browser to <a href="http://localhost:4567"  target="_blank" rel="noreferrer">http://localhost:4567</a> and check it out. I used the wiki in my <a href="https://github.com/grantwinney/hide-comments-everywhere/wiki"  target="_blank" rel="noreferrer">Hide Comments Everywhere</a> repo – GitHub hosted is on the left, and Gollum local is on the right.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/5-things-you-can-do-with-a-locally-cloned-github-wiki/image-12-1.png"
    width="950"
      height="615"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/5-things-you-can-do-with-a-locally-cloned-github-wiki/image-13.png"
    width="950"
      height="615"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/5-things-you-can-do-with-a-locally-cloned-github-wiki/image-16.png"
    width="950"
      height="680"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/5-things-you-can-do-with-a-locally-cloned-github-wiki/image-17.png"
    width="950"
      height="680"></figure>
<p>The wiki files are just plain markdown files, so while we <em>could</em> use any editor with these, it&rsquo;s nice to have access to something that looks similar to GitHub. There&rsquo;s some minor differences of course, but it&rsquo;s largely the same.</p>
<p>Every modification (edits, deletes, renames, etc) is committed to the repo individually, and when you&rsquo;re finished making changes, a <code>git push</code> sends your changes back to GitHub.</p>
<p><em><strong>Note:</strong></em> <em>Gollum will only show changes to your wiki pages that have been committed. If you change a page in a text editor, Gollum will not show the updated page until it has been committed to the repo (you don&rsquo;t have to push it up).</em></p>

<h3 class="relative group">Generate HTML Docs
    <div id="generate-html-docs" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#generate-html-docs" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Many of us have GitHub repos marked as private, with things we&rsquo;re developing that we don&rsquo;t want to share with the world. But if our project is hidden, so is our wiki. What if we&rsquo;ve got some useful documentation that we <em>do</em> want to share?</p>
<p>Wiki pages can be run through tools that generate HTML pages, which can then be published online, eliminating the need to create documentation twice. One such tool is <a href="http://pandoc.org/index.html"  target="_blank" rel="noreferrer">Pandoc</a>, which can convert between many of the markup formats Gollum (and GitHub) supports.</p>
<p>The <a href="http://pandoc.org/installing.html"  target="_blank" rel="noreferrer">installation page</a> for Pandoc has instructions for different systems – for Windows, it&rsquo;s just a simple msi file that runs in under a minute. After it installs, we can verify the version in a terminal window:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Console output showing the Pandoc version installed"
    src="/5-things-you-can-do-with-a-locally-cloned-github-wiki/image-18.png"
    width="725"
      height="208"></figure>
<p>Verifying the Pandoc version</p>
<p>Running a conversion can be as simple as specifying two files and letting Pandoc make reasonable assumptions, probably based on file type:</p>
<p><code>pandoc .\Home.md -o .\Home.html</code></p>
<p>Here&rsquo;s how the main page of one of my project wikis looks. On the left is the GitHub hosted version, then the local Gollum version in the middle, and finally the Pandoc HTML one on the right.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Rendered outputs of a wiki page, on GitHub, in Gollum, and converted by Pandoc"
    src="/5-things-you-can-do-with-a-locally-cloned-github-wiki/image-19.png"
    width="1466"
      height="691"></figure>
<p>Various rendered outputs of the same wiki page</p>
<p>The output could use some CSS styling (it&rsquo;s actually nice that we get a very plain version, which we can style as we like), and may require further processing (for example, internal links between markdown pages would need updated to point to the equivalent HTML pages), but the result is really good and saves a lot of manual work.</p>
<p>We could further modify the output by adding a copyright notice, or generate a master document that acts as a table of contents, or even call a script to do this from a build server like Travis CI or TeamCity as part of a build to generate HTML documentation automatically. Lots of possibilities!</p>

<h3 class="relative group">Insert a Table of Contents
    <div id="insert-a-table-of-contents" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#insert-a-table-of-contents" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Even though we could have huge wiki pages with dozens of headers, there is no consistent way to autogenerate a table of contents using the default &ldquo;markdown&rdquo; syntax. <a href="https://github.com/gollum/gollum/wiki#user-content-table-of-contents-toc-tag"  target="_blank" rel="noreferrer">Gollum supports TOCs</a> with a simple <code>[[_TOC_]]</code> tag, but GitHub does not. If you&rsquo;ve ever been annoyed by this, <a href="https://github.com/isaacs/github/issues/215"  target="_blank" rel="noreferrer">you&rsquo;re not the only one</a> <em>(but the odds of it changing anytime soon are</em> <a href="https://github.com/github/markup/issues/904"  target="_blank" rel="noreferrer"><em>slim</em></a><em>).</em></p>
<p>One option is to write a short script in the language of your choice that parses the file and creates a TOC for you. Here&rsquo;s one I wrote in Perl that parses the file for headers, then inserts them into the top of the file and surrounds the TOC with the best <a href="http://stackoverflow.com/a/20885980/301857"  target="_blank" rel="noreferrer">approximation of a comment for markdown</a> that I could find so that it can update the TOC later.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-perl" data-lang="perl"><span class="line"><span class="cl"><span class="k">use</span> <span class="nn">strict</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">use</span> <span class="nn">warnings</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">use</span> <span class="nn">File::Copy</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">my</span> <span class="nv">$tocBegin</span> <span class="o">=</span> <span class="s">&#34;[//]: # (Start of TOC)\n&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">my</span> <span class="nv">$tocEnd</span> <span class="o">=</span> <span class="s">&#34;[//]: # (End of TOC)\n&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">foreach</span> <span class="k">my</span> <span class="nv">$file</span> <span class="p">(</span><span class="sr">&lt;*.md&gt;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">open</span><span class="p">(</span><span class="k">my</span> <span class="nv">$fh</span><span class="p">,</span> <span class="s">&#39;&lt;&#39;</span><span class="p">,</span> <span class="nv">$file</span><span class="p">)</span> <span class="ow">or</span> <span class="nb">die</span> <span class="s">&#34;Can&#39;t open $file: $!&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">my</span> <span class="nv">@lines</span> <span class="o">=</span> <span class="sr">&lt;$fh&gt;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">close</span> <span class="nv">$fh</span> <span class="ow">or</span> <span class="nb">die</span> <span class="s">&#34;Can&#39;t close $file: $!&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="k">my</span> <span class="nv">@headers</span> <span class="o">=</span> <span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="k">foreach</span> <span class="p">(</span><span class="nv">@lines</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nv">$_</span> <span class="o">=~</span><span class="sr"> /^###/</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nb">push</span> <span class="nv">@headers</span><span class="p">,</span> <span class="n">createLink</span><span class="p">(</span><span class="nv">$_</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">elsif</span> <span class="p">(</span><span class="nv">$_</span> <span class="o">=~</span><span class="sr"> /^##/</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nb">push</span> <span class="nv">@headers</span><span class="p">,</span> <span class="n">createLink</span><span class="p">(</span><span class="nv">$_</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">elsif</span> <span class="p">(</span><span class="nv">$_</span> <span class="o">=~</span><span class="sr"> /^#/</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nb">push</span> <span class="nv">@headers</span><span class="p">,</span> <span class="n">createLink</span><span class="p">(</span><span class="nv">$_</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nb">scalar</span><span class="p">(</span><span class="nv">@headers</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">next</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="nb">open</span><span class="p">(</span><span class="k">my</span> <span class="nv">$in</span><span class="p">,</span> <span class="s">&#39;&lt;&#39;</span><span class="p">,</span> <span class="nv">$file</span><span class="p">)</span> <span class="ow">or</span> <span class="nb">die</span> <span class="s">&#34;Can&#39;t open $file&#39; $!&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">open</span><span class="p">(</span><span class="k">my</span> <span class="nv">$out</span><span class="p">,</span> <span class="s">&#39;&gt;&#39;</span><span class="p">,</span> <span class="s">&#34;$file.new&#34;</span><span class="p">)</span> <span class="ow">or</span> <span class="nb">die</span> <span class="s">&#34;Can&#39;t write $file.new: $!&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="k">print</span> <span class="nv">$out</span> <span class="nv">$tocBegin</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">print</span> <span class="nv">$out</span> <span class="s">&#34;*TABLE OF CONTENTS*\n&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">foreach</span><span class="p">(</span><span class="nv">@headers</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">print</span> <span class="nv">$out</span> <span class="nv">$_</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">print</span> <span class="nv">$out</span> <span class="s">&#34;\n---\n&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">print</span> <span class="nv">$out</span> <span class="nv">$tocEnd</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="k">my</span> <span class="nv">$traversingOldToc</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span><span class="p">(</span><span class="sr">&lt;$in&gt;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nv">$_</span> <span class="ow">eq</span> <span class="nv">$tocBegin</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nv">$traversingOldToc</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">elsif</span> <span class="p">(</span><span class="nv">$_</span> <span class="ow">eq</span> <span class="nv">$tocEnd</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nv">$traversingOldToc</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">elsif</span> <span class="p">(</span><span class="nv">$traversingOldToc</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">print</span> <span class="nv">$out</span> <span class="nv">$_</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="nb">close</span> <span class="nv">$in</span> <span class="ow">or</span> <span class="nb">die</span> <span class="s">&#34;Can&#39;t close $file: $!&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">close</span> <span class="nv">$out</span> <span class="ow">or</span> <span class="nb">die</span> <span class="s">&#34;Can&#39;t close $file.new: $!&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="n">move</span><span class="p">(</span><span class="s">&#34;$file.new&#34;</span><span class="p">,</span> <span class="nv">$file</span><span class="p">)</span> <span class="ow">or</span> <span class="nb">die</span> <span class="s">&#34;Can&#39;t rename $file.new to $file: $!&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">sub</span> <span class="nf">createLink</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">my</span> <span class="nv">$currentLine</span> <span class="o">=</span> <span class="nv">$_</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="k">my</span> <span class="nv">$indent</span> <span class="o">=</span> <span class="nv">$_</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="k">my</span> <span class="nv">$text</span> <span class="o">=</span> <span class="nb">substr</span><span class="p">(</span><span class="nv">$currentLine</span><span class="p">,</span> <span class="nv">$indent</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$text</span> <span class="o">=~</span> <span class="sr">s/^\s+|\s+$//g</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">my</span> <span class="nv">$link</span> <span class="o">=</span> <span class="nb">lc</span> <span class="nv">$text</span> <span class="o">=~</span> <span class="sr">s/ /-/</span><span class="n">rg</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s">&#34; &#34;</span> <span class="n">x</span> <span class="p">((</span><span class="nv">$indent</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span><span class="o">*</span><span class="mi">2</span><span class="p">)</span> <span class="o">.</span> <span class="s">&#34;- &#34;</span> <span class="o">.</span> <span class="s">&#34;&lt;a href=\&#34;#user-content-$link\&#34;&gt;$text&lt;/a&gt;\n&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>You&rsquo;d need to do some work to get this production-ready (such as stripping punctuation from the headers since they aren&rsquo;t included in the anchors) but it does the trick in simple scenarios. Here&rsquo;s how it renders:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/5-things-you-can-do-with-a-locally-cloned-github-wiki/custom-toc-in-wiki-pages.png"
    width="1440"
      height="679"></figure>
<p>Now that I showed you the harder way, you could search for one of the many projects out there that do it for you. For example, a quick search turned up <a href="https://github.com/thlorenz/doctoc"  target="_blank" rel="noreferrer">DocToc</a>, which you can install with <a href="https://www.npmjs.com/"  target="_blank" rel="noreferrer">npm</a>. If you check the output below, you&rsquo;ll see that DocToc recursively checks inside other directories (like &ldquo;images&rdquo;), just in case you&rsquo;re structuring your wiki differently than the default &ldquo;all pages in a single folder&rdquo; style.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">grantwMac:pinboard-bookmarks-to-chrome.wiki gwinney$ doctoc .
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">DocToccing &#34;.&#34; and its sub directories for github.com.
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">Found Another-test.md, Home.md, Manual-Test-Scenarios.md, My-test-wiki-page.md, Rate-Limiting-Retrieval-of-URLs-from-Pinboard.md, Rationale-for-Not-Using-Sync-Storage.md in &#34;.&#34;
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">Found nothing in &#34;images&#34;
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">==================
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">&#34;Home.md&#34; is up to date
</span></span><span class="line"><span class="cl">&#34;Manual-Test-Scenarios.md&#34; is up to date
</span></span><span class="line"><span class="cl">&#34;Rate-Limiting-Retrieval-of-URLs-from-Pinboard.md&#34; is up to date
</span></span><span class="line"><span class="cl">&#34;Rationale-for-Not-Using-Sync-Storage.md&#34; is up to date
</span></span><span class="line"><span class="cl">&#34;Another-test.md&#34; will be updated
</span></span><span class="line"><span class="cl">&#34;My-test-wiki-page.md&#34; will be updated
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">Everything is OK.
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">grantwMac:pinboard-bookmarks-to-chrome.wiki gwinney$ git status
</span></span><span class="line"><span class="cl">On branch master
</span></span><span class="line"><span class="cl">Your branch is up-to-date with &#39;origin/master&#39;.
</span></span><span class="line"><span class="cl">Changes not staged for commit:
</span></span><span class="line"><span class="cl">  (use &#34;git add &lt;file&gt;...&#34; to update what will be committed)
</span></span><span class="line"><span class="cl">  (use &#34;git checkout -- &lt;file&gt;...&#34; to discard changes in working directory)
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> modified:   Another-test.md
</span></span><span class="line"><span class="cl"> modified:   My-test-wiki-page.md</span></span></code></pre></div></div>
<p>The end-result is similar in appearance to mine, even using comments (albeit regular HTML comments) to mark the TOC so it can be updated later.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">&lt;!-- START doctoc generated TOC please keep comment here to allow auto update --&gt;
</span></span><span class="line"><span class="cl">&lt;!-- DON&#39;T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --&gt;
</span></span><span class="line"><span class="cl">*Table of Contents*  *generated with [DocToc](https://github.com/thlorenz/doctoc)*
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">- [Important stuff](#important-stuff)
</span></span><span class="line"><span class="cl">  - [Less Important but Relevant Stuff](#less-important-but-relevant-stuff)
</span></span><span class="line"><span class="cl">    - [Wassup](#wassup)
</span></span><span class="line"><span class="cl">- [Next major point](#next-major-point)
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">&lt;!-- END doctoc generated TOC please keep comment here to allow auto update --&gt;</span></span></code></pre></div></div>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/5-things-you-can-do-with-a-locally-cloned-github-wiki/custom-toc-from-doctoc-wiki.png"
    width="1440"
      height="648"></figure>
<p>While we&rsquo;re at it, pandoc can do it too by specifying the <code>--toc</code> argument. Here&rsquo;s a quick script for converting your markdown files into HTML documentation that includes a table of contents.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">FILES</span><span class="o">=</span><span class="s2">&#34;*.md&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> f in <span class="nv">$FILES</span>
</span></span><span class="line"><span class="cl"><span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="nv">base</span><span class="o">=</span><span class="sb">`</span>basename <span class="nv">$f</span> <span class="s2">&#34;.md&#34;</span><span class="sb">`</span>
</span></span><span class="line"><span class="cl">    pandoc --toc -s -f markdown <span class="nv">$f</span> &gt; <span class="s2">&#34;</span><span class="nv">$base</span><span class="s2">&#34;</span>.html
</span></span><span class="line"><span class="cl"><span class="k">done</span></span></span></code></pre></div></div>

<h3 class="relative group">Easily Upload (and Add) Images
    <div id="easily-upload-and-add-images" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#easily-upload-and-add-images" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The GitHub wiki has a button for inserting image links into your wiki page, but it assumes the image is already uploaded somewhere and that you know the URL. There&rsquo;s no mechanism for dropping an image into the editor or otherwise uploading files to the wiki.</p>
<p>If you&rsquo;ve got a wiki page to create that involves a lot of images, you may want to clone locally. Gollum supports uploading files when you start it with the right option, as well as <a href="https://github.com/gollum/gollum"  target="_blank" rel="noreferrer">a myriad of other options</a> you&rsquo;ll want to check out too.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">gollum --allow-uploads dir</span></span></code></pre></div></div>
<p>Drag a file over the editor and it&rsquo;ll show a light green border around it. The file is copied to the repository, and a link to the file is inserted into the wiki page. If the file is an image, it should be displayed.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">[[/uploads/custom toc from doctoc wiki5.png]]</span></span></code></pre></div></div>
<p>Files dropped onto the editor this way are committed, and will be pushed up with the rest of your wiki.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/5-things-you-can-do-with-a-locally-cloned-github-wiki/gollum-wiki-file-upload-enabled.png"
    width="935"
      height="584"></figure>
<p>The <code>show-all</code> flag complements this nicely too.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">gollum --allow-uploads dir --show-all</span></span></code></pre></div></div>
<p>With that option enabled, clicking the &ldquo;All&rdquo; or &ldquo;Files&rdquo; buttons in the wiki will show <em>everything,</em> not just pages.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/5-things-you-can-do-with-a-locally-cloned-github-wiki/gollum-wiki-view-all-files-option.png"
    width="843"
      height="458"></figure>

<h3 class="relative group">Manipulate Pages with the Ruby API
    <div id="manipulate-pages-with-the-ruby-api" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#manipulate-pages-with-the-ruby-api" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>One more option for manipulating pages is the Ruby API called <a href="https://github.com/gollum/gollum-lib"  target="_blank" rel="noreferrer">Gollum-lib</a>. To install it, just run <code>gem install gollum-lib</code>.</p>
<p>Gollum-lib is nice because it abstracts away some of the nitty-gritty details. For example, the Perl script I wrote earlier could be modified to print out the contents of the file it&rsquo;s iterating through. Or we could just write a few lines of Ruby using gollum-lib.</p>
<p>Here&rsquo;s a short script that loads the local wiki <em>(use the relative path from your Ruby script)</em>, then loads a page from the wiki and calls the <code>raw_data</code> function to output the contents of the file. You can see the output in the terminal window in the bottom half of the screenshot.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/5-things-you-can-do-with-a-locally-cloned-github-wiki/query-data-about-gollum-wiki-page-using-gollum_lib.png"
    width="945"
      height="784"></figure>
<p>What else can we do with the API?</p>
<p>A one-liner change from <code>page.raw_data</code> to <code>page.formatted_data</code> renders the page in HTML output:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;&lt;</span><span class="nt">strong</span><span class="p">&gt;</span>Table of Contents<span class="p">&lt;/</span><span class="nt">strong</span><span class="p">&gt;</span>  <span class="p">&lt;</span><span class="nt">em</span><span class="p">&gt;</span>generated with <span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://github.com/thlorenz/doctoc&#34;</span><span class="p">&gt;</span>DocToc<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">em</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;#heading-one&#34;</span><span class="p">&gt;</span>Heading One<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;#heading-one-sub-blahlblah&#34;</span><span class="p">&gt;</span>Heading One Sub bLahlblah<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;&lt;</span><span class="nt">strong</span><span class="p">&gt;</span>TABLE OF CONTENTS<span class="p">&lt;/</span><span class="nt">strong</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">- <span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;#user-content-heading-one&#34;</span><span class="p">&gt;</span>Heading One<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  - <span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;#user-content-heading-one-sub-blahlblah&#34;</span><span class="p">&gt;</span>Heading One Sub bLahlblah<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">hr</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span># Heading One<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>Headin&#39; on...<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h2</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;anchor&#34;</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;heading-one-sub-blahlblah&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;#heading-one-sub-blahlblah&#34;</span><span class="p">&gt;&lt;</span><span class="nt">i</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;fa fa-link&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">i</span><span class="p">&gt;&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>Heading One Sub bLahlblah<span class="p">&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>Read <span class="p">&lt;</span><span class="nt">em</span><span class="p">&gt;</span>me<span class="p">&lt;/</span><span class="nt">em</span><span class="p">&gt;</span>.<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p>If you wanted a list of all versions of your pages, you could easily get to that information with the API:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;rubygems&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;gollum-lib&#39;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="n">wiki</span> <span class="o">=</span> <span class="no">Gollum</span><span class="o">::</span><span class="no">Wiki</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s1">&#39;pinboard-bookmarks-to-chrome.wiki&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="no">Dir</span><span class="o">.</span><span class="n">foreach</span><span class="p">(</span><span class="s1">&#39;pinboard-bookmarks-to-chrome.wiki&#39;</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="n">ext</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">extname</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">next</span> <span class="k">if</span> <span class="n">ext</span> <span class="o">!=</span> <span class="s1">&#39;.md&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">page</span> <span class="o">=</span> <span class="n">wiki</span><span class="o">.</span><span class="n">page</span><span class="p">(</span><span class="no">File</span><span class="o">.</span><span class="n">basename</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="n">ext</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="nb">puts</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">item</span><span class="si">}</span><span class="s2">: </span><span class="si">#{</span><span class="n">page</span><span class="o">.</span><span class="n">version</span><span class="o">.</span><span class="n">id</span><span class="si">}</span><span class="se">\n</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div>
<p>Here&rsquo;s what it outputs:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Another-test.md: dde4a0571de2a1791cdb8bf3575e957e45944873
</span></span><span class="line"><span class="cl">Home.md: dde4a0571de2a1791cdb8bf3575e957e45944873
</span></span><span class="line"><span class="cl">Manual-Test-Scenarios.md: dde4a0571de2a1791cdb8bf3575e957e45944873
</span></span><span class="line"><span class="cl">My-test-wiki-page.md: dde4a0571de2a1791cdb8bf3575e957e45944873
</span></span><span class="line"><span class="cl">Rate-Limiting-Retrieval-of-URLs-from-Pinboard.md: dde4a0571de2a1791cdb8bf3575e957e45944873
</span></span><span class="line"><span class="cl">Rationale-for-Not-Using-Sync-Storage.md: dde4a0571de2a1791cdb8bf3575e957e45944873</span></span></code></pre></div></div>
<p>Or maybe you want an outline of all pages, including a table-of-contents if headers are present, that links to the original wiki:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;rubygems&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;gollum-lib&#39;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="n">wiki</span> <span class="o">=</span> <span class="no">Gollum</span><span class="o">::</span><span class="no">Wiki</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s1">&#39;pinboard-bookmarks-to-chrome.wiki&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">html</span> <span class="o">=</span> <span class="s1">&#39;&lt;h1&gt;Wiki Contents&lt;/h1&gt;&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">html</span> <span class="o">&lt;&lt;</span> <span class="s1">&#39;&lt;style&gt;p { font-size:larger; font-weight:bold }&lt;/style&gt;&#39;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="no">Dir</span><span class="o">.</span><span class="n">foreach</span><span class="p">(</span><span class="s1">&#39;pinboard-bookmarks-to-chrome.wiki&#39;</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="n">ext</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">extname</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">next</span> <span class="k">if</span> <span class="n">ext</span> <span class="o">!=</span> <span class="s1">&#39;.md&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">basename</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">basename</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="n">ext</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">page</span> <span class="o">=</span> <span class="n">wiki</span><span class="o">.</span><span class="n">page</span><span class="p">(</span><span class="n">basename</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">html</span> <span class="o">&lt;&lt;</span> <span class="s2">&#34;&lt;hr&gt;&lt;p&gt;&lt;a href=</span><span class="se">\&#34;</span><span class="s2">https://github.com/grantwinney/pinboard-bookmarks-to-chrome/wiki/</span><span class="si">#{</span><span class="n">basename</span><span class="si">}</span><span class="se">\&#34;</span><span class="s2">&gt;</span><span class="si">#{</span><span class="n">page</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">&lt;/a&gt;&lt;/p&gt;&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">toc</span> <span class="o">=</span> <span class="n">page</span><span class="o">.</span><span class="n">toc_data</span>
</span></span><span class="line"><span class="cl">  <span class="k">next</span> <span class="k">if</span> <span class="n">toc</span><span class="o">.</span><span class="n">nil?</span>
</span></span><span class="line"><span class="cl">  <span class="n">html</span> <span class="o">&lt;&lt;</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">toc</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="no">File</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s1">&#39;outline.html&#39;</span><span class="p">,</span> <span class="n">html</span><span class="p">)</span></span></span></code></pre></div></div>
<p>That&rsquo;ll produce a small HTML page with a link to each wiki page and a table of contents if available. <em>(The missing header below was due to some wonky markdown in that file.)</em></p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/5-things-you-can-do-with-a-locally-cloned-github-wiki/master-contents-page-for-wiki-using-gollum_lib.png"
    width="1192"
      height="657"></figure>
<p>So far, all we&rsquo;ve seen is how to query data.</p>
<p>It&rsquo;s also possible to commit your changes from the API too. Here&rsquo;s a Ruby script that loops through your pages, inserts a copyright notice at the very top (if there isn&rsquo;t already one), and then commits the modified files to your wiki repository.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;rubygems&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;gollum-lib&#39;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="n">wiki</span> <span class="o">=</span> <span class="no">Gollum</span><span class="o">::</span><span class="no">Wiki</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s1">&#39;pinboard-bookmarks-to-chrome.wiki&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">create_commit</span><span class="p">(</span><span class="n">page</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span> <span class="ss">:message</span> <span class="o">=&gt;</span> <span class="s2">&#34;added copyright to </span><span class="si">#{</span><span class="n">page</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="ss">:name</span> <span class="o">=&gt;</span> <span class="s1">&#39;Grant Winney&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="ss">:email</span> <span class="o">=&gt;</span> <span class="s1">&#39;user@email.com&#39;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="n">copyright</span> <span class="o">=</span> <span class="s1">&#39;&lt;p&gt;&lt;em&gt;Copyright 2017 - Grant Winney - &lt;a href=&#34;https://opensource.org/licenses/MIT&#34;&gt;MIT License&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&#39;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="no">Dir</span><span class="o">.</span><span class="n">foreach</span><span class="p">(</span><span class="s1">&#39;pinboard-bookmarks-to-chrome.wiki&#39;</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="n">ext</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">extname</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">next</span> <span class="k">if</span> <span class="n">ext</span> <span class="o">!=</span> <span class="s1">&#39;.md&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">basename</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">basename</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="n">ext</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">page</span> <span class="o">=</span> <span class="n">wiki</span><span class="o">.</span><span class="n">page</span><span class="p">(</span><span class="n">basename</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">page</span><span class="o">.</span><span class="n">raw_data</span><span class="o">.</span><span class="n">start_with?</span><span class="p">(</span><span class="n">copyright</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="n">wiki</span><span class="o">.</span><span class="n">update_page</span><span class="p">(</span><span class="n">page</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                     <span class="n">page</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                     <span class="n">page</span><span class="o">.</span><span class="n">format</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                     <span class="s2">&#34;</span><span class="si">#{</span><span class="n">copyright</span><span class="si">}</span><span class="se">\n\n</span><span class="si">#{</span><span class="n">page</span><span class="o">.</span><span class="n">raw_data</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                     <span class="n">create_commit</span><span class="p">(</span><span class="n">page</span><span class="o">.</span><span class="n">name</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div>
<p>Here&rsquo;s the rendered output:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/5-things-you-can-do-with-a-locally-cloned-github-wiki/gollum-wiki-add-copyright-to-top.png"
    width="654"
      height="366"></figure>

<h2 class="relative group">What&rsquo;s Next..?
    <div id="whats-next" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whats-next" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There are still some shortcomings in Gollum, but far fewer of them when working locally rather than through GitHub.</p>
<p>I hope this helped you out, and that you learned something new! If you discover any other good uses of cloning your wiki locally, or create something cool with the Ruby API, let me know! I&rsquo;d love to check it out.</p>
<p>Good luck!</p>
]]></content:encoded><media:content url="https://grantwinney.com/5-things-you-can-do-with-a-locally-cloned-github-wiki/feature.webp" medium="image" type="image/webp"/></item><item><title>Comparing Two Objects for Equality in C#</title><link>https://grantwinney.com/csharp-compare-two-objects-for-equality/</link><pubDate>Mon, 31 Oct 2016 13:23:44 +0000</pubDate><guid>https://grantwinney.com/csharp-compare-two-objects-for-equality/</guid><description>It&amp;rsquo;s common to compare two objects in C# for equality, such as for a save operation. Let&amp;rsquo;s take a closer look at how we define what equal means.</description><content:encoded><![CDATA[<p>We compare values for equality all the time in C#, so frequently that we rarely think about it most of the time:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">name</span> <span class="p">=</span> <span class="s">&#34;Mike&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">name</span> <span class="p">==</span> <span class="s">&#34;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;Hi there, stranger.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Hi, {name}!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">age</span> <span class="p">=</span> <span class="m">17</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">isAdult</span> <span class="p">=</span> <span class="n">age</span> <span class="p">&gt;=</span> <span class="m">18</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;{name} {(isAdult ? &#34;</span><span class="k">is</span><span class="s">&#34; : &#34;</span><span class="k">is</span> <span class="n">not</span><span class="s">&#34;)} an adult.&#34;</span><span class="p">);</span></span></span></code></pre></div></div>
<p>Comparing numbers, strings, <code>DateTime</code>, and other out-of-the-box .NET types are the most typical examples, but what if we want to use our own type in a comparison?</p>

<h2 class="relative group">Default Equality Comparison
    <div id="default-equality-comparison" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#default-equality-comparison" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Before we dig into that, let&rsquo;s consider how testing for equality works by default, without us having to do anything extra.</p>
<p>With few exceptions, everything we use or define in C# derives from the base <code>Object</code> class. In fact, we could define a class like this, with <code>Object</code> explicitly included:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Person</span> <span class="p">:</span> <span class="n">Object</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">Age</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>That&rsquo;s unnecessary though, since it implicitly derives from <code>Object</code> anyway. It&rsquo;s enough to know that we get everything in the <code>Object</code> class by default, one of which is the <a href="https://msdn.microsoft.com/en-us/library/bsc2ak47"  target="_blank" rel="noreferrer">Equals()</a> method:</p>
<blockquote><p>[T]he Equals(Object) method tests for reference equality, and a call to the Equals(Object) method is equivalent to a call to the <a href="https://msdn.microsoft.com/en-us/library/system.object.referenceequals%5C%28v=vs.110%5C%29.aspx"  target="_blank" rel="noreferrer">ReferenceEquals</a> method. Reference equality means that the object variables that are compared refer to the same object. (<a href="https://msdn.microsoft.com/en-us/library/bsc2ak47%5C%28v=vs.110%5C%29.aspx"  target="_blank" rel="noreferrer">MSDN</a>)</p>
</blockquote><p>We can even delve into the <a href="https://source.dot.net/#System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/Object.cs,517682d5f6f4f8b4,references"  target="_blank" rel="noreferrer">source code</a> and see exactly how equality is defined in the <code>Object</code> class:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Default equality comparison implementation in the Object class"
    src="/csharp-compare-two-objects-for-equality/equals-source-code.png"
    width="978"
      height="156"></figure>
<p>Note that it&rsquo;s marked <code>virtual</code>, allowing us to override it – that&rsquo;ll be important in a moment. Also note that hovering over the <code>==</code> shows us a popup that there&rsquo;s another operation being implemented too. We can&rsquo;t see it in the source code, but we&rsquo;ll talk more about that in a minute as well.</p>
<p>The default test for &ldquo;equality&rdquo; is that two instances of a class are literally the same instance. This is true when two variables point to the same instance stored in memory. That&rsquo;s what we get out of the box, without doing anything else:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">person</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Person</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Jay&#34;</span><span class="p">,</span> <span class="n">Age</span> <span class="p">=</span> <span class="m">25</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">samePerson</span> <span class="p">=</span> <span class="n">person</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">newPerson</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Person</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">&#34;Jay&#34;</span><span class="p">,</span> <span class="n">Age</span> <span class="p">=</span> <span class="m">25</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">person</span><span class="p">.</span><span class="n">Equals</span><span class="p">(</span><span class="n">samePerson</span><span class="p">));</span>  <span class="c1">// true, same instance</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">person</span> <span class="p">==</span> <span class="n">samePerson</span><span class="p">);</span>       <span class="c1">// true, same instance</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">person</span><span class="p">.</span><span class="n">Equals</span><span class="p">(</span><span class="n">newPerson</span><span class="p">));</span>   <span class="c1">// false, different instances</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">person</span> <span class="p">==</span> <span class="n">newPerson</span><span class="p">);</span>        <span class="c1">// false, different instances</span></span></span></code></pre></div></div>
<p>Here, <code>samePerson</code> references the same instance as <code>person</code>, so they&rsquo;re considered equal. However, <code>newPerson</code> is a new instance and thus <em>not</em> equal, even though the values of its properties are all the same as <code>person</code>.</p>
<p>There are cases when we want this default behavior, but more often than not we want to define our own equality. After all, if all the properties of two separate <code>Person</code> instances are the same, then shouldn&rsquo;t that be the same person?</p>

<h2 class="relative group">Custom Equality Comparison
    <div id="custom-equality-comparison" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#custom-equality-comparison" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Let&rsquo;s create a <code>Vehicle</code> class now – something really simple that just stores a vehicle&rsquo;s make, model, and year of manufacture:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Vehicle</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Make</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Model</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">Year</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>If we create two instances of the class and then try comparing them, we end up comparing the <em>references</em> to those two instances, just like with the <code>Person</code> class before:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">vehicle1</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Vehicle</span> <span class="p">{</span> <span class="n">Make</span> <span class="p">=</span> <span class="s">&#34;Toyota&#34;</span><span class="p">,</span> <span class="n">Model</span> <span class="p">=</span> <span class="s">&#34;Camry&#34;</span><span class="p">,</span> <span class="n">Year</span> <span class="p">=</span> <span class="m">2024</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">vehicle2</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Vehicle</span> <span class="p">{</span> <span class="n">Make</span> <span class="p">=</span> <span class="s">&#34;Toyota&#34;</span><span class="p">,</span> <span class="n">Model</span> <span class="p">=</span> <span class="s">&#34;Camry&#34;</span><span class="p">,</span> <span class="n">Year</span> <span class="p">=</span> <span class="m">2024</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">vehicle1</span> <span class="p">==</span> <span class="n">vehicle2</span><span class="p">);</span>  <span class="c1">// false</span></span></span></code></pre></div></div>

<h3 class="relative group">Overriding the Equals Method
    <div id="overriding-the-equals-method" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#overriding-the-equals-method" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>We can easily expand our <code>Vehicle</code> class to <code>override</code> the <code>Equals()</code> method that&rsquo;s defined in the <code>Object</code> class, and redefine what makes two <code>Vehicle</code> instances equal:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Vehicle</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Make</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Model</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">Year</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">override</span> <span class="kt">bool</span> <span class="n">Equals</span><span class="p">(</span><span class="kt">object?</span> <span class="n">obj</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">obj</span> <span class="k">is</span> <span class="kc">null</span> <span class="p">||</span> <span class="n">obj</span> <span class="k">is</span> <span class="n">not</span> <span class="n">Vehicle</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">otherVehicle</span> <span class="p">=</span> <span class="p">(</span><span class="n">Vehicle</span><span class="p">)</span><span class="n">obj</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">Make</span> <span class="p">!=</span> <span class="n">otherVehicle</span><span class="p">.</span><span class="n">Make</span> <span class="p">||</span> <span class="n">Model</span> <span class="p">!=</span> <span class="n">otherVehicle</span><span class="p">.</span><span class="n">Model</span> <span class="p">||</span> <span class="n">Year</span> <span class="p">!=</span> <span class="n">otherVehicle</span><span class="p">.</span><span class="n">Year</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Now if we compare the two vehicles again, our code checks to make sure all three properties are the same. If they are, then the vehicles are equal:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">vehicle1</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Vehicle</span> <span class="p">{</span> <span class="n">Make</span> <span class="p">=</span> <span class="s">&#34;Toyota&#34;</span><span class="p">,</span> <span class="n">Model</span> <span class="p">=</span> <span class="s">&#34;Camry&#34;</span><span class="p">,</span> <span class="n">Year</span> <span class="p">=</span> <span class="m">2024</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">vehicle2</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Vehicle</span> <span class="p">{</span> <span class="n">Make</span> <span class="p">=</span> <span class="s">&#34;Toyota&#34;</span><span class="p">,</span> <span class="n">Model</span> <span class="p">=</span> <span class="s">&#34;Camry&#34;</span><span class="p">,</span> <span class="n">Year</span> <span class="p">=</span> <span class="m">2024</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">vehicle1</span><span class="p">.</span><span class="n">Equals</span><span class="p">(</span><span class="n">vehicle2</span><span class="p">));</span>  <span class="c1">// true</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">vehicle1</span> <span class="p">==</span> <span class="n">vehicle2</span><span class="p">);</span>       <span class="c1">// false .... why?</span></span></span></code></pre></div></div>
<p>Well, they&rsquo;re mostly equal. We&rsquo;ve overridden the <code>Equals()</code> method, but we have one more thing to do.</p>

<h3 class="relative group">Overloading the == and != Operators
    <div id="overloading-the--and--operators" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#overloading-the--and--operators" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Once we override <code>Object.Equals()</code>, most devs are reasonably going to expect that the <code>==</code> operator will perform the same way and not produce different results.</p>
<p>Let&rsquo;s update the <code>Vehicle</code> class one more time, to overload the <code>==</code> and <code>!=</code> operators so everything behaves consistently. In fact, to make life easier, let&rsquo;s just call <code>Equals()</code>. When possible, keep things DRY!</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Vehicle</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Make</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">Model</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">Year</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">override</span> <span class="kt">bool</span> <span class="n">Equals</span><span class="p">(</span><span class="kt">object?</span> <span class="n">obj</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">obj</span> <span class="k">is</span> <span class="kc">null</span> <span class="p">||</span> <span class="n">obj</span> <span class="k">is</span> <span class="n">not</span> <span class="n">Vehicle</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">otherVehicle</span> <span class="p">=</span> <span class="p">(</span><span class="n">Vehicle</span><span class="p">)</span><span class="n">obj</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">Make</span> <span class="p">!=</span> <span class="n">otherVehicle</span><span class="p">.</span><span class="n">Make</span> <span class="p">||</span> <span class="n">Model</span> <span class="p">!=</span> <span class="n">otherVehicle</span><span class="p">.</span><span class="n">Model</span> <span class="p">||</span> <span class="n">Year</span> <span class="p">!=</span> <span class="n">otherVehicle</span><span class="p">.</span><span class="n">Year</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">bool</span> <span class="kd">operator</span> <span class="p">==(</span><span class="n">Vehicle</span> <span class="n">x</span><span class="p">,</span> <span class="n">Vehicle</span> <span class="n">y</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">x</span><span class="p">.</span><span class="n">Equals</span><span class="p">(</span><span class="n">y</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">bool</span> <span class="kd">operator</span> <span class="p">!=(</span><span class="n">Vehicle</span> <span class="n">x</span><span class="p">,</span> <span class="n">Vehicle</span> <span class="n">y</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">!</span><span class="n">x</span><span class="p">.</span><span class="n">Equals</span><span class="p">(</span><span class="n">y</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Now everything should work as expected. Comparing two vehicles gives consistent results, whether using <code>Equals()</code> or <code>==</code>:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">camry</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Vehicle</span> <span class="p">{</span> <span class="n">Make</span> <span class="p">=</span> <span class="s">&#34;Toyota&#34;</span><span class="p">,</span> <span class="n">Model</span> <span class="p">=</span> <span class="s">&#34;Camry&#34;</span><span class="p">,</span> <span class="n">Year</span> <span class="p">=</span> <span class="m">2024</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">alsoCamry</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Vehicle</span> <span class="p">{</span> <span class="n">Make</span> <span class="p">=</span> <span class="s">&#34;Toyota&#34;</span><span class="p">,</span> <span class="n">Model</span> <span class="p">=</span> <span class="s">&#34;Camry&#34;</span><span class="p">,</span> <span class="n">Year</span> <span class="p">=</span> <span class="m">2024</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">bugatti</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Vehicle</span> <span class="p">{</span> <span class="n">Make</span> <span class="p">=</span> <span class="s">&#34;Bugatti&#34;</span><span class="p">,</span> <span class="n">Model</span> <span class="p">=</span> <span class="s">&#34;Chiron&#34;</span><span class="p">,</span> <span class="n">Year</span> <span class="p">=</span> <span class="m">2023</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">camry</span><span class="p">.</span><span class="n">Equals</span><span class="p">(</span><span class="n">alsoCamry</span><span class="p">));</span>  <span class="c1">// true, custom equality logic</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">camry</span> <span class="p">==</span> <span class="n">alsoCamry</span><span class="p">);</span>       <span class="c1">// true, custom equality logic</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">camry</span><span class="p">.</span><span class="n">Equals</span><span class="p">(</span><span class="n">bugatti</span><span class="p">));</span>    <span class="c1">// false, custom equality logic</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">camry</span> <span class="p">==</span> <span class="n">bugatti</span><span class="p">);</span>         <span class="c1">// false, custom equality logic</span></span></span></code></pre></div></div>

<h3 class="relative group">Overriding GetHashCode
    <div id="overriding-gethashcode" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#overriding-gethashcode" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Once we override the <code>Equals()</code> method, the compiler wants us to override the <code>GetHashCode()</code> method as well. Here&rsquo;s the tooltip that pops up over the <code>Vehicle</code> class:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Compiler warning to override Object.GetHashCode"
    src="/csharp-compare-two-objects-for-equality/override-gethashcode-warning.png"
    width="612"
      height="163"></figure>
<p>Microsoft has a lot more to say about it in the <a href="https://learn.microsoft.com/en-us/dotnet/api/system.object.gethashcode?view=net-8.0"  target="_blank" rel="noreferrer">Object.GetHashCode</a> docs, and I suggest checking that out, but here&rsquo;s a few highlights <em>(emphasis mine):</em></p>
<ul>
<li>A hash function is used to quickly generate a number (hash code) that <strong>corresponds to the value of an object</strong>.</li>
<li><strong>If two objects compare as equal</strong>, the <a href="https://learn.microsoft.com/en-us/dotnet/api/system.object.gethashcode?view=net-8.0#system-object-gethashcode"  target="_blank" rel="noreferrer">GetHashCode()</a> method for each object <strong>must return the same value</strong>.</li>
<li>Hash functions <strong>should be inexpensive to compute</strong>.</li>
<li>The <a href="https://learn.microsoft.com/en-us/dotnet/api/system.object.gethashcode?view=net-8.0#system-object-gethashcode"  target="_blank" rel="noreferrer">GetHashCode()</a> method <strong>should not throw exceptions</strong>.</li>
</ul>
<p><a href="https://ericlippert.com/2011/02/28/guidelines-and-rules-for-gethashcode/"  target="_blank" rel="noreferrer">Eric Lippert wrote about it too</a>. He worked on the C# language, compiler, tooling, and more at Microsoft, so he&rsquo;s quite an authoritative source. In a nutshell though, it&rsquo;s enough to know that once we override the other methods, we should override this one too.</p>
<p>Implementing the hash code isn&rsquo;t difficult, really. We just need to decide which fields make a <code>Vehicle</code> unique, which is likely to be the same fields used in the <code>Equal()</code> method. If we start typing out some code to compute the hash code, VS 2022 even helpfully offers an autocompletion:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="VS2022 autocompletion suggestion for GetHashCode"
    src="/csharp-compare-two-objects-for-equality/override-gethashcode-implementation.png"
    width="625"
      height="97"></figure>
<p>If it&rsquo;s possible that some fields could be <code>null</code>, given the last point about how <code>GetHashCode()</code> should never throw an exception, we might want to be a bit more robust:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">override</span> <span class="kt">int</span> <span class="n">GetHashCode</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="n">Make</span><span class="p">?.</span><span class="n">GetHashCode</span><span class="p">()</span> <span class="p">??</span> <span class="m">0</span><span class="p">)</span> <span class="p">^</span> <span class="p">(</span><span class="n">Model</span><span class="p">?.</span><span class="n">GetHashCode</span><span class="p">()</span> <span class="p">??</span> <span class="m">0</span><span class="p">)</span> <span class="p">^</span> <span class="n">Year</span><span class="p">.</span><span class="n">GetHashCode</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Instead of potentially throwing a <code>NullReferenceException</code>, it just uses the value <code>0</code> instead.</p>

<h2 class="relative group">Final Thoughts
    <div id="final-thoughts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#final-thoughts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>It&rsquo;s useful in C# to be able to define what makes two instances of our own classes &ldquo;equal&rdquo;. Thanks to how nearly everything derives from the <code>Object</code> class, and that it includes the <code>virtual bool Equals()</code> method for us to override, defining equality is incredibly easy!</p>
]]></content:encoded><media:content url="https://grantwinney.com/csharp-compare-two-objects-for-equality/feature.webp" medium="image" type="image/webp"/></item><item><title>Connecting an Analog Joystick to the Raspberry Pi</title><link>https://grantwinney.com/raspberry-pi-analog-joystick/</link><pubDate>Sat, 24 Sep 2016 13:30:49 +0000</pubDate><guid>https://grantwinney.com/raspberry-pi-analog-joystick/</guid><description/><content:encoded><![CDATA[<p>One of the best things about the Raspberry Pi is its GPIO pins. They’re just sitting there, waiting to be connected to all kinds of interesting peripherals so your Pi can interact with the world around it. We can <a href="https://grantwinney.com/raspberry-pi-flash-led-for-new-email/"  target="_blank" rel="noreferrer">send alerts</a>, attach sensors, and even plug cards like the <a href="https://www.raspberrypi.org/products/sense-hat/"  target="_blank" rel="noreferrer">Sense HAT</a> over top of the pins to do even more.</p>
<p>A few months ago, I bought a set of 37 sensor modules. I knew they wouldn’t directly interface with the Pi, but that it was entirely possible to do it, so they were set aside for later. Well, it&rsquo;s time to try one out, and I figure the mini-joystick might offer some interesting opportunities!</p>

<h2 class="relative group">Materials
    <div id="materials" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#materials" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There are a few things we need first:</p>
<ul>
<li><strong>Raspberry Pi Starter Kit</strong><br>
A decent starter kit includes the Pi, adapter, memory card, case, breadboard and cobbler, wires and LEDs, etc.</li>
<li><strong>Long Breadboard</strong><br>
Some of the kits come with a shorter breadboard, but a longer board gives us more space to work, allowing for more wires, LEDs, switches, etc.</li>
<li><strong>Kuman 37 Sensor Module Kit for Arduino</strong><br>
That&rsquo;s the one I got, but I&rsquo;m sure there&rsquo;s plenty of similar ones. Mine came with a joystick control (which I used for this post), and a load of other sensors and input devices. There was no documentation, but I found a <a href="https://mega.nz/#F!LElQwT6R!Tj6SclwUfajz1ZihF_s2Mw"  target="_blank" rel="noreferrer">link to (somewhat sparse) instructions for each module</a> on Amazon.</li>
<li><strong>Male to female jumper wires</strong><br>
We need wires to connect the joystick to the breadboard.</li>
<li><strong>Adafruit MCP3008 – 8-Channel 10-Bit ADC With SPI Interface [ADA856]</strong><br>
A tiny chip that bridges the gap between an analog control and the Pi. It’s cheaper directly from <a href="https://www.adafruit.com/products/856"  target="_blank" rel="noreferrer">Adafruit</a>, but watch out for shipping. If you’re buying several instead of just one like me, consider Adafruit’s site. <a href="http://www.microchip.com/wwwproducts/en/en010530"  target="_blank" rel="noreferrer">Here&rsquo;s the datasheet</a>.</li>
</ul>

<h2 class="relative group">Interfacing with Analog Controls
    <div id="interfacing-with-analog-controls" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#interfacing-with-analog-controls" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The joystick is an analog control, consisting of two potentiometers that send a variable voltage depending on the position of the joystick (here’s a <a href="https://www.youtube.com/watch?v=MXFvWLrpVSk"  target="_blank" rel="noreferrer">video that shows how they work</a>), and it won’t just connect directly to the GPIO pins on the Pi. If your joystick can be pressed down like mine can, then that bonus button just has an on/off state and can be connected directly to any regular GPIO pin. But I’ll wire it up same as the potentiometers, since that’s what the articles linked below do as well.</p>
<p>To get it to work, we need to learn a little about the SPI bus protocol and how to enable it on the Pi, then how to wire up a small chip that uses SPI to bridge the gap between analog controls and the Pi. Fortunately, there’s a set of helpful tutorials on the Raspberry Pi Spy website, which I&rsquo;d suggest checking out:</p>
<ul>
<li><a href="http://www.raspberrypi-spy.co.uk/2014/08/enabling-the-spi-interface-on-the-raspberry-pi/"  target="_blank" rel="noreferrer">Enabling The SPI Interface On The Raspberry Pi</a></li>
<li><a href="http://www.raspberrypi-spy.co.uk/2014/04/using-a-joystick-on-the-raspberry-pi-using-an-mcp3008/"  target="_blank" rel="noreferrer">Using A Joystick On The Raspberry Pi Using An MCP3008</a></li>
<li><a href="http://www.raspberrypi-spy.co.uk/2013/10/analogue-sensors-on-the-raspberry-pi-using-an-mcp3008/"  target="_blank" rel="noreferrer">Analogue Sensors On The Raspberry Pi Using An MCP3008</a></li>
</ul>
<p>The first link shows how to enable the Serial Peripheral Interface (SPI) bus on certain GPIO pins. Method 1 worked fine for me – just open up a config screen in Raspbian and select the SPI option.</p>
<p>The second link walks through wiring up the MCP3008 chip, providing the bridge between the joystick and Pi. The third link isn&rsquo;t necessary, but it’s got some helpful info in it that’s not in the other one. I suggest reading both.</p>
<p>Also, for changing colors on an RGB LED, it may help to read about <a href="https://grantwinney.com/raspberry-pi-pulse-width-modulation/"  target="_blank" rel="noreferrer">pulse-width modulation (PWM)</a>.</p>
<p>Here are some pictures and a diagram of my setup, although the author of the linked articles provides a good set of pics too. There’s some additional stuff in my circuit that’s not in his, namely the RGB LED and resistors/wires to make it work. I used a 100Ω resistor for red and 220Ω for green and blue, <a href="https://grantwinney.com/raspberry-pi-pulse-width-modulation/"  target="_blank" rel="noreferrer">same as here</a>.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/raspberry-pi-analog-joystick/joystick-color-wheel-setup-1.jpg"
    width="2048"
      height="841"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/raspberry-pi-analog-joystick/joystick-color-wheel-setup-2.jpg"
    width="1329"
      height="900"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="joystick-color-wheel-setup-3.jpg"
    ></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/raspberry-pi-analog-joystick/joystick-color-wheel-setup-4.jpg"
    width="1434"
      height="817"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/raspberry-pi-analog-joystick/joystick-color-wheel-setup-5.jpg"
    width="1151"
      height="687"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/raspberry-pi-analog-joystick/joystick-color-wheel-setup-6.jpg"
    width="1280"
      height="869"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/raspberry-pi-analog-joystick/mcp3008-in-package.jpg"
    width="672"
      height="762"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/raspberry-pi-analog-joystick/mcp3008.jpg"
    width="435"
      height="356"></figure>

<h3 class="relative group">Fritzing Diagram
    <div id="fritzing-diagram" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#fritzing-diagram" aria-label="Anchor">#</a>
    </span>
    
</h3>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="joystick-color-wheel"
    src="/raspberry-pi-analog-joystick/Joystick-Color-Wheel.png"
    width="2472"
      height="990"></figure>
<p>If you’d like, you can <a href="Joystick-Color-Wheel.fzz" >download the original Fritzing file</a> and play around with it.</p>

<h2 class="relative group">Reading Input
    <div id="reading-input" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#reading-input" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>You should’ve already verified that Python Spidev (<a href="https://github.com/doceme/py-spidev"  target="_blank" rel="noreferrer">pi-spydev</a>) was installed after you enabled SPI. We’ll need that for reading input from the analog device.</p>
<p>Since I’ve been <a href="https://grantwinney.com/raspberry-pi-pulse-width-modulation/"  target="_blank" rel="noreferrer">messing with an RGB LED</a> lately, I thought it’d be interesting to map the position of the joystick to the RGB color wheel and then light up the LED appropriately. Imagine the X-axis running horizontal above Blue and Green, and the Y-axis running vertical through Red and Cyan.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="rgb_color_wheel_400px"
    src="/raspberry-pi-analog-joystick/rgb_color_wheel_400px.jpg"
    width="400"
      height="400"></figure>
<p>Here’s the code in its entirety:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">math</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">RPi.GPIO</span> <span class="k">as</span> <span class="nn">GPIO</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">spidev</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="c1"># Open SPI bus</span>
</span></span><span class="line"><span class="cl"><span class="n">spi</span> <span class="o">=</span> <span class="n">spidev</span><span class="o">.</span><span class="n">SpiDev</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">spi</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="c1"># Define sensor channels (3 to 7 are unused)</span>
</span></span><span class="line"><span class="cl"><span class="n">mcp3008_switch_channel</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"><span class="n">mcp3008_x_voltage_channel</span> <span class="o">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="n">mcp3008_y_voltage_channel</span> <span class="o">=</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="c1"># Define RGB channels</span>
</span></span><span class="line"><span class="cl"><span class="n">red_led</span> <span class="o">=</span> <span class="mi">36</span>
</span></span><span class="line"><span class="cl"><span class="n">green_led</span> <span class="o">=</span> <span class="mi">31</span>
</span></span><span class="line"><span class="cl"><span class="n">blue_led</span> <span class="o">=</span> <span class="mi">37</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">read_spi_data_channel</span><span class="p">(</span><span class="n">channel</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Read in SPI data from the channel and return a coordinate position
</span></span></span><span class="line"><span class="cl"><span class="s2">    :param channel: integer, between 0-7
</span></span></span><span class="line"><span class="cl"><span class="s2">    :return: integer, between 0-1023 indicating joystick position
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="n">adc</span> <span class="o">=</span> <span class="n">spi</span><span class="o">.</span><span class="n">xfer2</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="p">(</span><span class="mi">8</span><span class="o">+</span><span class="n">channel</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">0</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">((</span><span class="n">adc</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">&amp;</span> <span class="mi">3</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">8</span><span class="p">)</span> <span class="o">+</span> <span class="n">adc</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">convert_coordinates_to_angle</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">center_x_pos</span><span class="p">,</span> <span class="n">center_y_pos</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Convert an x,y coordinate pair representing joystick position,
</span></span></span><span class="line"><span class="cl"><span class="s2">    and convert it to an angle relative to the joystick center (resting) position
</span></span></span><span class="line"><span class="cl"><span class="s2">    :param x: integer, between 0-1023 indicating position on x-axis
</span></span></span><span class="line"><span class="cl"><span class="s2">    :param y: integer, between 0-1023 indicating position on y-axis
</span></span></span><span class="line"><span class="cl"><span class="s2">    :param center_x_pos: integer, indicating resting position of joystick along x-axis
</span></span></span><span class="line"><span class="cl"><span class="s2">    :param center_y_pos: integer, indicating resting position of joystick along y-axis
</span></span></span><span class="line"><span class="cl"><span class="s2">    :return: integer, between 0-359 indicating angle in degrees
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="n">dx</span> <span class="o">=</span> <span class="n">x</span> <span class="o">-</span> <span class="n">center_x_pos</span>
</span></span><span class="line"><span class="cl">    <span class="n">dy</span> <span class="o">=</span> <span class="n">y</span> <span class="o">-</span> <span class="n">center_y_pos</span>
</span></span><span class="line"><span class="cl">    <span class="n">rads</span> <span class="o">=</span> <span class="n">math</span><span class="o">.</span><span class="n">atan2</span><span class="p">(</span><span class="o">-</span><span class="n">dy</span><span class="p">,</span> <span class="n">dx</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">rads</span> <span class="o">%=</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">math</span><span class="o">.</span><span class="n">pi</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">math</span><span class="o">.</span><span class="n">degrees</span><span class="p">(</span><span class="n">rads</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">adjust_angle_for_perspective_of_current_led</span><span class="p">(</span><span class="n">angle</span><span class="p">,</span> <span class="n">led</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Take the current LED into account, and rotate the coordinate plane 360 deg to make PWM calculations easier
</span></span></span><span class="line"><span class="cl"><span class="s2">    :param angle: integer, between 0-359 indicating current angle of joystick position
</span></span></span><span class="line"><span class="cl"><span class="s2">    :param led: &#39;R&#39;, &#39;G&#39;, &#39;B&#39;, indicating the LED we&#39;re interested in
</span></span></span><span class="line"><span class="cl"><span class="s2">    :return: integer, between 0-359 indicating new angle relative to the current LED under consideration
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="n">led_peak_angle</span> <span class="o">=</span> <span class="mi">90</span> <span class="k">if</span> <span class="n">led</span> <span class="o">==</span> <span class="s1">&#39;R&#39;</span> <span class="k">else</span> <span class="p">(</span><span class="mi">210</span> <span class="k">if</span> <span class="n">led</span> <span class="o">==</span> <span class="s1">&#39;B&#39;</span> <span class="k">else</span> <span class="mi">330</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">((</span><span class="n">angle</span> <span class="o">-</span> <span class="n">led_peak_angle</span><span class="p">)</span> <span class="o">+</span> <span class="mi">360</span><span class="p">)</span> <span class="o">%</span> <span class="mi">360</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">calculate_next_pwm_duty_cycle_for_led</span><span class="p">(</span><span class="n">angle</span><span class="p">,</span> <span class="n">led</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Calculate the next PWM duty cycle value for the current LED and joystick position (angle)
</span></span></span><span class="line"><span class="cl"><span class="s2">    :param angle: integer, between 0-359 indicating current angle of joystick position
</span></span></span><span class="line"><span class="cl"><span class="s2">    :param led: &#39;R&#39;, &#39;G&#39;, &#39;B&#39;, indicating the LED we&#39;re interested in
</span></span></span><span class="line"><span class="cl"><span class="s2">    :return: integer, between 0-100 indicating the next PWM duty cycle value for the LED
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="n">angle</span> <span class="o">=</span> <span class="n">adjust_angle_for_perspective_of_current_led</span><span class="p">(</span><span class="n">angle</span><span class="p">,</span> <span class="n">led</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="mi">120</span> <span class="o">&lt;</span> <span class="n">angle</span> <span class="o">&lt;</span> <span class="mi">240</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="n">angle</span> <span class="o">&lt;=</span> <span class="mi">120</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="mi">100</span> <span class="o">-</span> <span class="p">(</span><span class="n">angle</span> <span class="o">*</span> <span class="p">(</span><span class="mi">100</span> <span class="o">/</span> <span class="mf">120.0</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="mi">100</span> <span class="o">-</span> <span class="p">((</span><span class="mi">360</span> <span class="o">-</span> <span class="n">angle</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="mi">100</span> <span class="o">/</span> <span class="mf">120.0</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">is_joystick_near_center</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">center_x_pos</span><span class="p">,</span> <span class="n">center_y_pos</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Compare the current joystick position to resting position and decide if it&#39;s close enough to be considered &#34;center&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    :param x: integer, between 0-1023 indicating position on x-axis
</span></span></span><span class="line"><span class="cl"><span class="s2">    :param y: integer, between 0-1023 indicating position on y-axis
</span></span></span><span class="line"><span class="cl"><span class="s2">    :param center_x_pos: integer, indicating resting position of joystick along x-axis
</span></span></span><span class="line"><span class="cl"><span class="s2">    :param center_y_pos: integer, indicating resting position of joystick along y-axis
</span></span></span><span class="line"><span class="cl"><span class="s2">    :return: boolean, indicating whether or not the joystick is near the center (resting) position
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="n">dx</span> <span class="o">=</span> <span class="n">math</span><span class="o">.</span><span class="n">fabs</span><span class="p">(</span><span class="n">x</span> <span class="o">-</span> <span class="n">center_x_pos</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">dy</span> <span class="o">=</span> <span class="n">math</span><span class="o">.</span><span class="n">fabs</span><span class="p">(</span><span class="n">y</span> <span class="o">-</span> <span class="n">center_y_pos</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">dx</span> <span class="o">&lt;</span> <span class="mi">20</span> <span class="ow">and</span> <span class="n">dy</span> <span class="o">&lt;</span> <span class="mi">20</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Initializes GPIO and PWM, then sets up a loop to continually read the joystick position and calculate the next set
</span></span></span><span class="line"><span class="cl"><span class="s2">    of PWM value for the RGB LED. When user hits ctrl^c, everything is cleaned up (see &#39;finally&#39; block)
</span></span></span><span class="line"><span class="cl"><span class="s2">    :return: None
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="c1"># Center positions when joystick is at rest</span>
</span></span><span class="line"><span class="cl">    <span class="n">center_x_pos</span> <span class="o">=</span> <span class="mi">530</span>
</span></span><span class="line"><span class="cl">    <span class="n">center_y_pos</span> <span class="o">=</span> <span class="mi">504</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">setmode</span><span class="p">(</span><span class="n">GPIO</span><span class="o">.</span><span class="n">BOARD</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">setup</span><span class="p">([</span><span class="n">red_led</span><span class="p">,</span> <span class="n">green_led</span><span class="p">,</span> <span class="n">blue_led</span><span class="p">],</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">OUT</span><span class="p">,</span> <span class="n">initial</span><span class="o">=</span><span class="n">GPIO</span><span class="o">.</span><span class="n">LOW</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="n">pwm_r</span> <span class="o">=</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">PWM</span><span class="p">(</span><span class="n">red_led</span><span class="p">,</span> <span class="mi">300</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">pwm_g</span> <span class="o">=</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">PWM</span><span class="p">(</span><span class="n">green_led</span><span class="p">,</span> <span class="mi">300</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">pwm_b</span> <span class="o">=</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">PWM</span><span class="p">(</span><span class="n">blue_led</span><span class="p">,</span> <span class="mi">300</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="n">pwm_instances</span> <span class="o">=</span> <span class="p">[</span><span class="n">pwm_r</span><span class="p">,</span> <span class="n">pwm_g</span><span class="p">,</span> <span class="n">pwm_b</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">pwm_instances</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">p</span><span class="o">.</span><span class="n">start</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># If joystick switch is pressed down, turn off LEDs</span>
</span></span><span class="line"><span class="cl">            <span class="n">switch</span> <span class="o">=</span> <span class="n">read_spi_data_channel</span><span class="p">(</span><span class="n">mcp3008_switch_channel</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">switch</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">pwm_instances</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                    <span class="n">p</span><span class="o">.</span><span class="n">ChangeDutyCycle</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="k">continue</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">            <span class="c1"># Read the joystick position data</span>
</span></span><span class="line"><span class="cl">            <span class="n">x_pos</span> <span class="o">=</span> <span class="n">read_spi_data_channel</span><span class="p">(</span><span class="n">mcp3008_x_voltage_channel</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">y_pos</span> <span class="o">=</span> <span class="n">read_spi_data_channel</span><span class="p">(</span><span class="n">mcp3008_y_voltage_channel</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">            <span class="c1"># If joystick is at rest in center, turn on all LEDs at max</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">is_joystick_near_center</span><span class="p">(</span><span class="n">x_pos</span><span class="p">,</span> <span class="n">y_pos</span><span class="p">,</span> <span class="n">center_x_pos</span><span class="p">,</span> <span class="n">center_y_pos</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">                <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">pwm_instances</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                    <span class="n">p</span><span class="o">.</span><span class="n">ChangeDutyCycle</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="k">continue</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">            <span class="c1"># Adjust duty cycle of LEDs based on joystick position</span>
</span></span><span class="line"><span class="cl">            <span class="n">angle</span> <span class="o">=</span> <span class="n">convert_coordinates_to_angle</span><span class="p">(</span><span class="n">x_pos</span><span class="p">,</span> <span class="n">y_pos</span><span class="p">,</span> <span class="n">center_x_pos</span><span class="p">,</span> <span class="n">center_y_pos</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">pwm_r</span><span class="o">.</span><span class="n">ChangeDutyCycle</span><span class="p">(</span><span class="n">calculate_next_pwm_duty_cycle_for_led</span><span class="p">(</span><span class="n">angle</span><span class="p">,</span> <span class="s1">&#39;R&#39;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">            <span class="n">pwm_g</span><span class="o">.</span><span class="n">ChangeDutyCycle</span><span class="p">(</span><span class="n">calculate_next_pwm_duty_cycle_for_led</span><span class="p">(</span><span class="n">angle</span><span class="p">,</span> <span class="s1">&#39;G&#39;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">            <span class="n">pwm_b</span><span class="o">.</span><span class="n">ChangeDutyCycle</span><span class="p">(</span><span class="n">calculate_next_pwm_duty_cycle_for_led</span><span class="p">(</span><span class="n">angle</span><span class="p">,</span> <span class="s1">&#39;B&#39;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">            <span class="c1"># print(&#34;Position : ({},{})  --  Angle : {}&#34;.format(x_pos, y_pos, round(angle, 2)))</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">pass</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="k">finally</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">pwm_instances</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">p</span><span class="o">.</span><span class="n">stop</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="n">spi</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="n">GPIO</span><span class="o">.</span><span class="n">cleanup</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">_name_</span> <span class="o">==</span> <span class="s1">&#39;_main_&#39;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">main</span><span class="p">()</span></span></span></code></pre></div></div>
<p>I wrote the <code>adjust_angle_for_perspective_of_current_led</code> function to make calculations easier. Imagine a 360 degree circle overlaying the color wheel. Each color (red, blue, green) is separated by 120 degrees. So if red is at 90 (the top), then blue is at 210 and green is at 330. That function rotates the imaginary circle, placing the LED we’re concerned about at 0 degrees.</p>
<p>The <code>is_joystick_near_center</code> function was necessary because the joystick is not that accurate. Even when it’s sitting still, the readings coming off it fluctuate a bit. That’s not a huge deal when the joystick is positioned far away from the center, but imagine what happens when the position is near center and the X and Y coordinates keep jumping around the vertex of our “angle”. The angle varies wildly, so that when the joystick is “at rest”, the color flickers all over the place on the LED. So instead, I just display white if you’re near center.</p>
<p>If you clone the repo, you&rsquo;ll see a separate file with tests in it. To run the tests in this project, first install the <a href="https://technomilk.wordpress.com/2012/02/12/multiplying-python-unit-test-cases-with-different-sets-of-data/"  target="_blank" rel="noreferrer">DDT (Data-Driven Tests) package for Python unit testing</a> via pip.</p>

<h2 class="relative group">See it in Action
    <div id="see-it-in-action" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#see-it-in-action" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://res.cloudinary.com/dxm4riq52/video/upload/v1583294278/Raspberry%20Pi/Connecting_an_Analog_Joystick_to_the_Raspberry_Pi_nylfzo.mp4"  target="_blank" rel="noreferrer">You can see it in action here</a>.</p>
<p>If you have a question about any of the code, leave a comment below and I’ll try to clarify. There’s one tricky piece in the <code>read_spi_data_channel()</code> function, and that’s the call to <code>spi.xfer2()</code>. Suffice to say, that’s the pi-spydev module doing work.</p>
<p>If you want, check out the <a href="https://github.com/doceme/py-spidev/blob/master/spidev_module.c"  target="_blank" rel="noreferrer">spidev_module.c</a> file and do a search for “xfer2″. It’s roughly 100 lines of C code.</p>
]]></content:encoded><media:content url="https://grantwinney.com/raspberry-pi-analog-joystick/feature.webp" medium="image" type="image/webp"/></item><item><title>Creating a Flickering Candle Using an RGB LED on the Raspberry Pi</title><link>https://grantwinney.com/raspberry-pi-flickering-candle/</link><pubDate>Mon, 29 Aug 2016 22:34:44 +0000</pubDate><guid>https://grantwinney.com/raspberry-pi-flickering-candle/</guid><description/><content:encoded><![CDATA[<p>After <a href="https://grantwinney.com/raspberry-pi-pulse-width-modulation/"  target="_blank" rel="noreferrer">getting PWM (pulse-width modulation) to work with an RGB LED</a> last week, I was trying to think of what else I could do with an LED that demonstrated changes in color as well as intensity.</p>
<p>I’m not sure why – maybe it was because we lost power in our neighborhood recently – but I thought a flickering candle could be an interesting little challenge…</p>

<h2 class="relative group">Materials
    <div id="materials" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#materials" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>In order to test this out, you’ll need a few things.</p>
<ul>
<li>An RGB LED</li>
<li>A button</li>
<li>A breadboard</li>
<li>A T-cobbler (optional, but makes life easier when wiring up to GPIO pins)</li>
<li>A range of resistors</li>
<li>A few jumper wires (male-to-male if using a T-cobbler, otherwise male-to-female)</li>
</ul>
<p>If you don’t have the above, you can buy kits on Amazon. Personally, after I bought the Pi by itself, I purchased a kit by CanaKit on Amazon, and haven’t had any problems with it. I don&rsquo;t see much from CanaKit now, but there&rsquo;s others, or the <a href="https://thepihut.com/collections/raspberry-pi-store/products/camjam-edukit?ref=grant-winney"  target="_blank" rel="noreferrer">CamJam EduKit</a> <em>(note that none of these comes with a Pi).</em></p>
<p>If you need the Raspberry Pi unit itself, the Pi is more expensive than it used to be on Amazon, apparently due to component shortages. It might be worth checking out <a href="https://rpilocator.com/?ref=grant-winney"  target="_blank" rel="noreferrer">rpilocator</a> for a better price with other resellers, although you may have to wait awhile to get your Pi then, as many of the resellers seem to be sold out.</p>

<h2 class="relative group">Concepts
    <div id="concepts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#concepts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There’s a few concepts to cover before getting to the circuit and Python code.</p>

<h3 class="relative group">Pulse-Width Modulation
    <div id="pulse-width-modulation" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#pulse-width-modulation" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Once you have an LED wired up with a resistor, it’s either on at its current brightness, or it’s off. You can’t dim it or brighten it without changing the resistor. But you can make it <em>appear</em> to be dimmer or brighter, by telling the Pi to quickly flash it on and off many times a second (frequency), along with telling it <em>how long</em> to keep it on and off each time it flashes (duty cycle).</p>
<p>That’s called pulse-width modulation, or PWM. The <a href="https://pypi.python.org/pypi/RPi.GPIO"  target="_blank" rel="noreferrer">RPi.GPIO library</a> can simulate PWM with any of the GPIO pins you’d normally use to power an LED. <a href="https://sourceforge.net/p/raspberry-gpio-python/wiki/PWM/"  target="_blank" rel="noreferrer">Here’s a sample implementation from their documentation</a>. And here’s a short snippet of my own:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">p</span> <span class="o">=</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">PWM</span><span class="p">(</span><span class="mi">37</span><span class="p">,</span> <span class="mi">300</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">p</span><span class="o">.</span><span class="n">start</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">p</span><span class="o">.</span><span class="n">ChangeDutyCycle</span><span class="p">(</span><span class="mi">75</span><span class="p">)</span></span></span></code></pre></div></div>
<p>The first thing you have to do is set the channel and frequency. So here, we’ll say channel 37 is connected to one of the colors on our RGB LED, and it’ll be pulsed on and off 300 times a second. I don’t know what the practical limit is… I honestly didn’t see much difference between 100 and 300, and definitely didn’t see any difference above 300. If you set it too low (try setting it to 10), the pulsing will be slow enough for your eye to pick up on, and the effect is that it looks choppy.</p>
<p>The second adjustment is the duty cycle. By specifying 75, we’re telling it to keep the red LED on for 75% of each pulse, then off for 25%. That’s going to result in a relatively bright LED. Changing that setting to 25 will cause it to be dimmer. A setting of 100 is full on, as if you weren’t using PWM, and 0 is off.</p>
<p>Let&rsquo;s look at another short example.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">pRed</span> <span class="o">=</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">PWM</span><span class="p">(</span><span class="mi">37</span><span class="p">,</span> <span class="mi">300</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">pRed</span><span class="o">.</span><span class="n">start</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">pRed</span><span class="o">.</span><span class="n">ChangeDutyCycle</span><span class="p">(</span><span class="mi">75</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="n">pGreen</span> <span class="o">=</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">PWM</span><span class="p">(</span><span class="mi">33</span><span class="p">,</span> <span class="mi">300</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">pGreen</span><span class="o">.</span><span class="n">start</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">pGreen</span><span class="o">.</span><span class="n">ChangeDutyCycle</span><span class="p">(</span><span class="mi">25</span><span class="p">)</span></span></span></code></pre></div></div>
<p>The PWM for each color is set separately. Assuming red is connected to pin 37, and green to pin 33, the above code is telling the Pi to show red 75% of the time and green 25% of the time, resulting in a color that’s a mixture of red and green, but more red than green. It’s also initializing the duty cycle for red and green to 100 and 0, respectively, which turns the red LED fully on and the green LED off.</p>
<p>Now let’s talk about how we’re going to get the color we’re looking for out of the RGB LED.</p>

<h3 class="relative group">The RGB Color Wheel
    <div id="the-rgb-color-wheel" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-rgb-color-wheel" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Grab some milk and cookies and join me on the floor for a lesson on the color wheel. Our LED consists of red, green and blue, so we need the RGB wheel.</p>
<p>To create the yellow/orange of a candle, we’ll start with lots of red. Then we’ll add enough green to pull the balance towards orange or yellow. We don’t need any blue at all. As the candle “burns down”, we can remove some of the red (to make it look dimmer) and a lot more of the green (to change it from yellow to orange and finally red).</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/raspberry-pi-flickering-candle/rgb_color_wheel_400px.webp"
    width="400"
      height="400"></figure>

<h3 class="relative group">Algorithms
    <div id="algorithms" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#algorithms" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>That code snippet up above would give us a nice orange color. But it won’t look very natural if it’s always the same brightness and color. We need some sort of randomness or fluctuation in color and intensity over time, to make the candle “flicker”.</p>
<p>Here’s a very simple implementation. All it does is, 20x a second, randomly change the duty cycle for the LEDs. Red is lit up between 75% and 100% of the time, and green is lit between 15% and 25% of the time. The result should be an orangish color that flickers slightly, since the amount of red and green is changing.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">red_light</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">p</span> <span class="o">=</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">PWM</span><span class="p">(</span><span class="n">R</span><span class="p">,</span> <span class="mi">300</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">p</span><span class="o">.</span><span class="n">start</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">p</span><span class="o">.</span><span class="n">ChangeDutyCycle</span><span class="p">(</span><span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">75</span><span class="p">,</span> <span class="mi">100</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">.05</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">green_light</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">p</span> <span class="o">=</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">PWM</span><span class="p">(</span><span class="n">G</span><span class="p">,</span> <span class="mi">300</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">p</span><span class="o">.</span><span class="n">start</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">p</span><span class="o">.</span><span class="n">ChangeDutyCycle</span><span class="p">(</span><span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">15</span><span class="p">,</span> <span class="mi">25</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">.05</span><span class="p">)</span></span></span></code></pre></div></div>
<p>My first actual attempt was something like that; then I just kept hacking away at it until I had something that sorta worked, but not well. As the LEDs got dimmer, there was too much green, making the flame look odd. I needed to see what the algorithm actually <em>looked</em> like, and for that I turned to an <a href="https://www.desmos.com/calculator"  target="_blank" rel="noreferrer">online graphing calculator</a> by <a href="https://twitter.com/Desmos"  target="_blank" rel="noreferrer">Desmos</a>.</p>
<p>I wanted an algorithm that would adjust the brightness (intensity) and color, in order to attain a flickering effect. What’s more, I wanted the candle to “burn down”, getting dimmer and more red as time goes on.</p>
<p>In the following graph (from the Desmos site), the x-axis represents how long the candle has been burning. It starts at 1, and should get dimmer and deeper red as it moves towards 0. The green LED should fall randomly within the range of the blue-green lines, while the red LED should fall between the red-orange lines.</p>
<p>While red is always significantly higher than green (when the LEDs first turn on and are at a burn intensity of 1, green is between 33-44 duty cycle and red is between 75-100), as the candle burns down and burn intensity decreases to 0, red maintains a strong presence while green is fazed out more quickly. That should result in a strong reddish color as it burns down.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="flickering candle algorithm"
    src="/raspberry-pi-flickering-candle/flickering-candle-algorithm.png"
    width="1878"
      height="1362"></figure>
<p>But what happens when the candle burns down completely? We’ll add a way to “fan the flame” and make it get brighter again, by adding a button. More on that below…</p>

<h2 class="relative group">Circuit Design
    <div id="circuit-design" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#circuit-design" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Using an LED with multiple colors is a little more complicated than a single-color LED. You need to connect a separate GPIO pin to each color (and connect the common cathode to ground); then, by enabling or disabling each individual pin, you can create any color you need. What’s more, by using PWM to dim or brighten the LEDs, we’ll be able to create any shade we want on the color wheel. Since we won’t be using blue, we won’t even bother to connect a wire to the anode for the blue LED.</p>
<p>The RGB LED in the diagram below is oriented with the flat side on the left. If you take a close look at your own LED, there should be a flat side too. The pins, from left to right, are <strong>red</strong>, ground, <strong>green</strong>, <strong>blue</strong>.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Flickering Candle with RGB LED and PWM_bb"
    src="/raspberry-pi-flickering-candle/fritz-diagram.png"
    width="1647"
      height="990"></figure>
<p>The red LED is connected through a 100Ω resistor to pin 37. The green LED is connected through a 470Ω resistor to pin 33. Both pins are set to output, in order to light the LED. Since we don’t need the blue LED, there’s nothing connected to it.</p>
<p><em>I originally had a 220Ω resistor connected to the green LED, but at low intensities (when the duty cycle was low), green overpowered red and the candle took on a distinctly green hue. I switched to a larger resistor, which made green less intense, and allowed the “flame” to be more on the red end. Depending on your exact LED, you may have to play with resistors to get the right mix of colors as well.</em></p>
<p>The button is connected to 3.3v and pin 22, which is set to input. When the button is pressed, it’ll change a value that makes the flame brighter and more yellow again.</p>

<h2 class="relative group">Python Code
    <div id="python-code" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#python-code" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The “x” in the equations up above is represented by the “intensity” variable in the code below. It starts at 1.0, and drops every quarter-second. The algorithm takes that variable into account, adjusting the red and green LEDs, and making the candle appear to burn down.</p>
<p>There’s an event for the button on pin 22, which “fans the flame” by increasing the “intensity” variable towards 1.0 again. That has the effect of making the candle appear brighter and more yellow.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">RPi.GPIO</span> <span class="k">as</span> <span class="nn">GPIO</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">threading</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">time</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">random</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">math</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="n">R</span> <span class="o">=</span> <span class="mi">37</span>
</span></span><span class="line"><span class="cl"><span class="n">G</span> <span class="o">=</span> <span class="mi">33</span>
</span></span><span class="line"><span class="cl"><span class="n">BUTTON</span> <span class="o">=</span> <span class="mi">22</span>
</span></span><span class="line"><span class="cl"><span class="n">pwms</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"><span class="n">intensity</span> <span class="o">=</span> <span class="mf">1.0</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">initialize_gpio</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">setmode</span><span class="p">(</span><span class="n">GPIO</span><span class="o">.</span><span class="n">BOARD</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">setup</span><span class="p">([</span><span class="n">R</span><span class="p">,</span> <span class="n">G</span><span class="p">],</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">OUT</span><span class="p">,</span> <span class="n">initial</span><span class="o">=</span><span class="n">GPIO</span><span class="o">.</span><span class="n">LOW</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">setup</span><span class="p">(</span><span class="n">BUTTON</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">IN</span><span class="p">,</span> <span class="n">pull_up_down</span><span class="o">=</span><span class="n">GPIO</span><span class="o">.</span><span class="n">PUD_DOWN</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">add_event_detect</span><span class="p">(</span><span class="n">BUTTON</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">FALLING</span><span class="p">,</span> <span class="n">fan_the_flame</span><span class="p">,</span> <span class="mi">250</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">red_light</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">p</span> <span class="o">=</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">PWM</span><span class="p">(</span><span class="n">R</span><span class="p">,</span> <span class="mi">300</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">p</span><span class="o">.</span><span class="n">start</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">pwms</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">p</span><span class="o">.</span><span class="n">ChangeDutyCycle</span><span class="p">(</span><span class="nb">min</span><span class="p">(</span><span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">75</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span> <span class="o">*</span> <span class="n">math</span><span class="o">.</span><span class="n">pow</span><span class="p">(</span><span class="n">intensity</span> <span class="o">+</span> <span class="mf">0.1</span><span class="p">,</span> <span class="mf">0.75</span><span class="p">),</span> <span class="mi">100</span><span class="p">)</span> <span class="k">if</span> <span class="n">intensity</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="k">else</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">rand_flicker_sleep</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">green_light</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">p</span> <span class="o">=</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">PWM</span><span class="p">(</span><span class="n">G</span><span class="p">,</span> <span class="mi">300</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">p</span><span class="o">.</span><span class="n">start</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">pwms</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">p</span><span class="o">.</span><span class="n">ChangeDutyCycle</span><span class="p">(</span><span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">33</span><span class="p">,</span> <span class="mi">44</span><span class="p">)</span> <span class="o">*</span> <span class="n">math</span><span class="o">.</span><span class="n">pow</span><span class="p">(</span><span class="n">intensity</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="k">if</span> <span class="n">intensity</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="k">else</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">rand_flicker_sleep</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">rand_flicker_sleep</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span> <span class="o">/</span> <span class="mf">100.0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">burning_down</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">global</span> <span class="n">intensity</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">intensity</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="n">intensity</span> <span class="o">-</span> <span class="mf">.01</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">.25</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">fan_the_flame</span><span class="p">(</span><span class="n">_</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">global</span> <span class="n">intensity</span>
</span></span><span class="line"><span class="cl">    <span class="n">intensity</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="n">intensity</span> <span class="o">+</span> <span class="mf">0.25</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">light_candle</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">threads</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">red_light</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">green_light</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">burning_down</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">threads</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">t</span><span class="o">.</span><span class="n">daemon</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">        <span class="n">t</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">threads</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">t</span><span class="o">.</span><span class="n">join</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">initialize_gpio</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">Press ^C (control-C) to exit the program.</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">light_candle</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">pass</span>
</span></span><span class="line"><span class="cl">    <span class="k">finally</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">pwms</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">p</span><span class="o">.</span><span class="n">stop</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="n">GPIO</span><span class="o">.</span><span class="n">cleanup</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">_name_</span> <span class="o">==</span> <span class="s1">&#39;_main_&#39;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">main</span><span class="p">()</span></span></span></code></pre></div></div>

<h2 class="relative group">Demo
    <div id="demo" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#demo" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://res.cloudinary.com/dxm4riq52/video/upload/v1583296495/Raspberry%20Pi/Candle_Simulation_Using_an_RGB_LED_and_PWM_zutojp.mp4"  target="_blank" rel="noreferrer">Here’s a short demo of it working</a>. The effect is much better with the lights off, but unfortunately that makes everything somewhat grainy. The flickering effect doesn’t carry over quite as well as I wanted in the video, but you get the idea.</p>

<h2 class="relative group">Final Thoughts
    <div id="final-thoughts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#final-thoughts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>What do you think? Did you come up with a better solution? Maybe resistors that worked better, or a slightly adjusted algorithm?</p>
<p>Maybe you could use a 10mm LED instead of a 5mm, and see if the larger bulb changes the effect. Or place the LED inside a plastic cube so that the whole thing looks like it’s glowing and flickering…</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="blue led cube"
    src="/raspberry-pi-flickering-candle/blue-led-cube.jpg"
    width="600"
      height="450"></figure>
<p>If this inspires you on some project you’re working on, or you find a way to do something better with it, or I left out some detail and you have questions, let me know in the comments below. Good luck!</p>
]]></content:encoded><media:content url="https://grantwinney.com/raspberry-pi-flickering-candle/feature.webp" medium="image" type="image/webp"/></item><item><title>How to Use Pulse Width Modulation (PWM) on an RGB LED and the Raspberry Pi</title><link>https://grantwinney.com/raspberry-pi-pulse-width-modulation/</link><pubDate>Mon, 22 Aug 2016 07:28:52 +0000</pubDate><guid>https://grantwinney.com/raspberry-pi-pulse-width-modulation/</guid><description/><content:encoded><![CDATA[<p>If you buy a kit with random LEDs, wires, switches, etc, you’re likely to end up with one or two of those funky little LEDs that appears to be white, and has 4 wires instead of 2. I had set mine aside and made a mental note to figure it out later – well, that time has come!</p>
<blockquote><p>The code in this article is available on <a href="https://github.com/grantwinney/52-Weeks-of-Pi/tree/master/06-RGB-LED-Experiment"  target="_blank" rel="noreferrer">GitHub</a>, if you&rsquo;d like to use it or just follow along.</p>
</blockquote><p>It’s a special kind of LED that consists of 3 separate LEDs – red, green and blue. By adjusting each color independently, you can create any color (similar to how a TV works). By lighting all 3 in the right proportions, you can even create white.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="RGB_Light-emitting_diode"
    src="/raspberry-pi-pulse-width-modulation/RGB_Light-emitting_diode.png"
    width="225"
      height="225"></figure>
<p>If you haven’t played around with single-color LEDs yet, <a href="https://grantwinney.com/raspberry-pi-making-an-led-blink/"  target="_blank" rel="noreferrer">you may want to try that first</a>, although the process for an RGB LED is hardly more complicated.</p>

<h2 class="relative group">Laying Out the Circuit
    <div id="laying-out-the-circuit" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#laying-out-the-circuit" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>While it’d be quickest to just plug an LED into a breadboard and connect the wires directly to 3.3v and ground, that’s the kind of fun that’s short-lived and usually ends in tears.</p>
<p>So let’s take a few minutes to collect some information about the RGB LED, calculate the necessary resistors, and then lay it all out on a breadboard the right way. After all, nobody likes burnt Pi.</p>

<h3 class="relative group">Ohm’s Law, and Selecting the Right Resistors
    <div id="ohms-law-and-selecting-the-right-resistors" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#ohms-law-and-selecting-the-right-resistors" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Placing a resistor in your circuit when you’re using an LED is always a good idea, but how do we know which one to use? Too much resistance, and the LED will appear very dim or won’t light at all. Too little resistance, and the LED burns out and possibly damages the GPIO pin (or worse).</p>
<p>When you bought the LED, did it come with a data sheet? It’ll look something like the image below, which I pulled out of one of the <a href="https://www.sparkfun.com/datasheets/Components/YSL-R596CR3G4B5C-C10.pdf"  target="_blank" rel="noreferrer">SparkFun data sheets for an RGB LED</a>. If it didn’t come with one (mine didn&rsquo;t), the values below are pretty typical and safe to use, at least as a starting point <em>(more on that later)</em>.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="RGB Triple Color LED Specs"
    src="/raspberry-pi-pulse-width-modulation/RGB-Triple-Color-LED-Specs.png"
    width="1430"
      height="1366"></figure>
<p>There’s a lot of info there, but the values we’re interested in are <em>“forward current”</em> and <em>“forward voltage”</em>. Oh, and you know the Raspberry Pi outputs 3.3v, right? That’s important too.</p>
<p>There’s a handy little equation called Ohm’s law that’s going to help us out. You might want to read this <a href="https://learn.sparkfun.com/tutorials/voltage-current-resistance-and-ohms-law"  target="_blank" rel="noreferrer">basic overview of voltage, current and resistance from SparkFun</a>. (They forgot to mention subtracting the LED’s <em>forward voltage</em> from the incoming voltage, which results in selecting a higher value resistor than necessary. That’s better than a <em>lower</em> resistance than necessary, but still not optimal.)</p>
<p><strong>Resistance = Voltage / Amperage</strong></p>
<ul>
<li>The total voltage is our incoming voltage minus typical forward voltage</li>
<li>The amperage is the forward current divided by 1000 (since the value in the chart is in mA, but we need amps)</li>
</ul>
<p>Using that, we can calculate our resistors:</p>
<ul>
<li>Resistor for Red: (3.3v – 2.0v) / .02A = 65Ω</li>
<li>Resistor for Blue: (3.3v – 3.2v) / .02A = 5Ω</li>
<li>Resistor for Green: (3.3v – 3.2v) / .02A = 5Ω</li>
</ul>
<p>If you don’t want to memorize Ohm’s Law, bookmark one of the many calculators out there, like <a href="http://ledcalculator.net/"  target="_blank" rel="noreferrer">this one</a> or <a href="http://www.ohmslawcalculator.com/led-resistor-calculator"  target="_blank" rel="noreferrer">this other one</a>.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="led calculator result"
    src="/raspberry-pi-pulse-width-modulation/led-calculator-result.png"
    width="676"
      height="874"></figure>
<p>Hopefully you’ve got a nice variety of resistors. If not, Amazon&rsquo;s got some nice little sets. The closest matches I have that don’t go under the amounts calculated above is 100Ω and 10Ω, so I started with those.</p>

<h3 class="relative group">Designing the Breadboard
    <div id="designing-the-breadboard" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#designing-the-breadboard" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Take a close look at that LED – notice the four wires are not all the same length. The longest is the singular cathode, and it connects to ground. The other three (anodes) connect to red, green and blue, assuming you have your RGB LED oriented as in the image below.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="rgb multicolor led"
    src="/raspberry-pi-pulse-width-modulation/rgb-multicolor-led.png"
    width="84"
      height="231"></figure>
<p>Now we can lay everything out on a breadboard. Here’s the layout I chose, using GPIO18, 12 and 13 for red, blue and green respectively.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="RGB LED PWM_bb"
    src="/raspberry-pi-pulse-width-modulation/RGB-LED-PWM_bb.png"
    width="1317"
      height="714"></figure>
<p>And a couple photos, if that makes it easier to see…</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="rgb led pwm 1"
    src="/raspberry-pi-pulse-width-modulation/rgb-led-pwm-1.jpg"
    width="1083"
      height="707"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="rgb led pwm 2"
    src="/raspberry-pi-pulse-width-modulation/rgb-led-pwm-2.jpg"
    width="1298"
      height="743"></figure>
<p>I like using the <a href="http://amzn.to/1WkhpD1"  target="_blank" rel="noreferrer">T-cobbler that comes from the Canakit package</a>, which takes up some of the board but is more convenient than a bunch of wires leading directly back to the GPIO pins. Plus, I have a half-dozen breadboards, so I can unplug the cobbler from one project and set it aside, and connect it to another breadboard holding a different project. Like I said, convenient.</p>

<h2 class="relative group">Running the Python Scripts
    <div id="running-the-python-scripts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#running-the-python-scripts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Here are a couple of quick scripts to demonstrate the RGB LED.</p>

<h3 class="relative group">Script 1: Flash Different Colors
    <div id="script-1-flash-different-colors" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#script-1-flash-different-colors" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>This first script just turns on and off different colors randomly, so you can see some of the different mixing going on.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">RPi.GPIO</span> <span class="k">as</span> <span class="nn">GPIO</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">threading</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">time</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">random</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="n">PINS</span> <span class="o">=</span> <span class="p">[</span><span class="mi">12</span><span class="p">,</span><span class="mi">33</span><span class="p">,</span><span class="mi">32</span><span class="p">]</span>  <span class="c1"># R,G,B</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">GPIO</span><span class="o">.</span><span class="n">setmode</span><span class="p">(</span><span class="n">GPIO</span><span class="o">.</span><span class="n">BOARD</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">GPIO</span><span class="o">.</span><span class="n">setup</span><span class="p">(</span><span class="n">PINS</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">OUT</span><span class="p">,</span> <span class="n">initial</span><span class="o">=</span><span class="n">GPIO</span><span class="o">.</span><span class="n">LOW</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">Press ^C (control-C) to exit the program.</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">select_and_set_next_pin</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="nb">all</span><span class="p">(</span><span class="n">GPIO</span><span class="o">.</span><span class="n">input</span><span class="p">(</span><span class="n">pin</span><span class="p">)</span> <span class="o">==</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">LOW</span> <span class="k">for</span> <span class="n">pin</span> <span class="ow">in</span> <span class="n">PINS</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">                <span class="n">select_and_set_next_pin</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.75</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">pass</span>
</span></span><span class="line"><span class="cl">    <span class="k">finally</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">GPIO</span><span class="o">.</span><span class="n">cleanup</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">select_and_set_next_pin</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">next_pin</span> <span class="o">=</span> <span class="n">PINS</span><span class="p">[</span><span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">output</span><span class="p">(</span><span class="n">next_pin</span><span class="p">,</span> <span class="ow">not</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">input</span><span class="p">(</span><span class="n">next_pin</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">_name_</span> <span class="o">==</span> <span class="s1">&#39;_main_&#39;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">main</span><span class="p">()</span></span></span></code></pre></div></div>

<h3 class="relative group">Script 2: Use PWM for Smooth Color Transition
    <div id="script-2-use-pwm-for-smooth-color-transition" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#script-2-use-pwm-for-smooth-color-transition" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Once you have an LED wired up with a resistor, it’s either on or off – you can’t dim it or brighten it without changing the resistor. But you can make it <em>appear</em> to be dimmer or brighter, by telling the Pi to quickly blink it on and off hundreds of times a second (frequency), and further by telling it how long to keep it on and off each time it blinks (duty cycle).</p>
<p>That’s called pulse-width modulation, or PWM. The <a href="https://pypi.python.org/pypi/RPi.GPIO"  target="_blank" rel="noreferrer">RPi.GPIO library</a> can simulate PWM with any of the GPIO pins we’d normally use to power an LED. <a href="https://sourceforge.net/p/raspberry-gpio-python/wiki/PWM/"  target="_blank" rel="noreferrer">Here’s a sample implementation from their documentation</a>.</p>
<p>The following script is more interesting than the first one. It leaves all the colors enabled, but adjusts their duty cycle separately, in separate threads. The effect is that all 3 colors fade in and out independently, creating all possible colors.</p>
<p>First I set the pins to use for red, green and blue (R,G,B) and set them LOW by default. When they’re set to HIGH, they’ll send power to the LED and light it up. There are three separate threads running infinite loops, each exercising PWM on a separate color at a slightly different speed.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">RPi.GPIO</span> <span class="k">as</span> <span class="nn">GPIO</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">threading</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">time</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">random</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="n">R</span> <span class="o">=</span> <span class="mi">12</span>
</span></span><span class="line"><span class="cl"><span class="n">G</span> <span class="o">=</span> <span class="mi">33</span>
</span></span><span class="line"><span class="cl"><span class="n">B</span> <span class="o">=</span> <span class="mi">32</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="n">PINS</span> <span class="o">=</span> <span class="p">[</span><span class="n">R</span><span class="p">,</span><span class="n">G</span><span class="p">,</span><span class="n">B</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">initialize_gpio</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">setmode</span><span class="p">(</span><span class="n">GPIO</span><span class="o">.</span><span class="n">BOARD</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">setup</span><span class="p">(</span><span class="n">PINS</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">OUT</span><span class="p">,</span> <span class="n">initial</span><span class="o">=</span><span class="n">GPIO</span><span class="o">.</span><span class="n">LOW</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">color_test</span><span class="p">(</span><span class="n">channel</span><span class="p">,</span> <span class="n">frequency</span><span class="p">,</span> <span class="n">speed</span><span class="p">,</span> <span class="n">step</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">p</span> <span class="o">=</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">PWM</span><span class="p">(</span><span class="n">channel</span><span class="p">,</span> <span class="n">frequency</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">p</span><span class="o">.</span><span class="n">start</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">dutyCycle</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">101</span><span class="p">,</span> <span class="n">step</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="n">p</span><span class="o">.</span><span class="n">ChangeDutyCycle</span><span class="p">(</span><span class="n">dutyCycle</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="n">speed</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">dutyCycle</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="n">step</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="n">p</span><span class="o">.</span><span class="n">ChangeDutyCycle</span><span class="p">(</span><span class="n">dutyCycle</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="n">speed</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">color_test_thread</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">threads</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">    <span class="n">threads</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">color_test</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="p">(</span><span class="n">R</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mf">0.02</span><span class="p">,</span> <span class="mi">5</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">    <span class="n">threads</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">color_test</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="p">(</span><span class="n">G</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mf">0.035</span><span class="p">,</span> <span class="mi">5</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">    <span class="n">threads</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">color_test</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="p">(</span><span class="n">B</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mf">0.045</span><span class="p">,</span> <span class="mi">5</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">threads</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">t</span><span class="o">.</span><span class="n">daemon</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">        <span class="n">t</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">threads</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">t</span><span class="o">.</span><span class="n">join</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">initialize_gpio</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">Press ^C (control-C) to exit the program.</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">color_test_thread</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">pass</span>
</span></span><span class="line"><span class="cl">    <span class="k">finally</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">GPIO</span><span class="o">.</span><span class="n">cleanup</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">_name_</span> <span class="o">==</span> <span class="s1">&#39;_main_&#39;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">main</span><span class="p">()</span></span></span></code></pre></div></div>
<p>Apparently there is one pin (GPIO18) that allows hardware PWM, and maybe GPIO12 too, but I’m unclear on that. Then you’d need to use something like the <a href="https://www.adafruit.com/products/815"  target="_blank" rel="noreferrer">Adafruit 16-Channel 12-bit PWM/Servo Driver – I2C interface</a> to turn the pin into 16 pins, and the board can even be chained together.</p>
<p>Other libraries, like <a href="https://github.com/WiringPi/WiringPi-Python"  target="_blank" rel="noreferrer">WiringPi for Python</a> and the <a href="https://pypi.org/project/pigpio/"  target="_blank" rel="noreferrer">pigpio module</a>, can make use of PWM using the dedicated hardware on the Pi (again, I’m not completely clear on this yet), but RPi.GPIO doesn’t support it yet. That’s okay though… for what we’re doing here, the software PWM performs just fine.</p>
<p>Note that the current release does not support SPI, I2C, hardware PWM or serial functionality on the RPi yet. This is planned for the near future – watch this space! One-wire functionality is also planned.</p>
<p>Although hardware PWM is not available yet, software PWM is available to use on all channels.</p>

<h2 class="relative group">Fine-Tuning the Resistors
    <div id="fine-tuning-the-resistors" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#fine-tuning-the-resistors" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I mentioned earlier that the values in the chart were good starting points. Unless you’ve got the correct data sheet <em>(and even if you do…)</em> you might find the resistors need a bit of tweaking. I used Ohm’s Law to calculate the ideal resistors for my LED <em>(as close as I could),</em> and they worked okay. I let the above script run for about a half-hour, and neither the LED nor the resistors started heating up.</p>
<p>But I noticed something. When all 3 colors were on, the “white” light that should have produced had a distinctly blue hue to it, like one color was slightly overpowering the others. I played around with a few different resistors, trying to increase the resistance slightly, and ended up putting a 220Ω resistor on blue and green, while leaving the 100Ω resistor on red. Now the color looks white.</p>
<p>You may find you have to do the same. Here’s <a href="http://www.henryleach.com/2013/05/controlling-rgb-led-with-raspberry-pi.html"  target="_blank" rel="noreferrer">someone else who had to adjust his resistors</a>, calculating 70 for red and 0 for green, but then finding that 0 for red and 220 for green worked better. I would’ve been hesitant to decrease resistance below the calculated amount for fear of burning something out, but he was using a multi-meter to test the voltage so he probably knows what he’s doing better than me. :)</p>

<h3 class="relative group">Quick Note About Reverse Polarity
    <div id="quick-note-about-reverse-polarity" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#quick-note-about-reverse-polarity" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Apparently (from what I’ve read) it’s possible for some RGB LEDs to have a single anode and one cathode per color, which is the opposite from everything I’ve described so far. If that’s the case, your LED won’t light up when you lay things out like I’ve got them. You may need to connect the single anode to 3.3v (instead of ground), then adjust the above scripts to set the pins HIGH initially, then set them LOW to make them light up.</p>
<p>I just looked up RGB LEDs on Amazon, and came across a set that appears to have this issue. Be sure the read the comments, as people will probably note when they have an issue with them.</p>

<h2 class="relative group">Demo
    <div id="demo" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#demo" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>And finally, a video showing it in practice:</p>
<p><a href="https://res.cloudinary.com/dxm4riq52/video/upload/v1583296511/Raspberry%20Pi/Using_an_RGB_multi-color_LED_with_Pulse_Width_Modulation_PWM_bjftre.mp4"  target="_blank" rel="noreferrer">Using_an_RGB_multi-color_LED_with_Pulse_Width_Modulation_PWM.mp4</a></p>

<h2 class="relative group">Further Reading
    <div id="further-reading" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#further-reading" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>For those that just can’t get enough, here’s some resources I stumbled on while researching LEDs and all their related goodness…</p>
<ul>
<li><a href="https://learn.sparkfun.com/tutorials/voltage-current-resistance-and-ohms-law"  target="_blank" rel="noreferrer">Voltage, Current, Resistance, and Ohm’s Law</a>, I’d recommend reading the entire article – it’s very helpful.</li>
<li><a href="http://www.evilmadscientist.com/2012/resistors-for-leds/"  target="_blank" rel="noreferrer">Basics: Picking Resistors for LEDs</a></li>
<li><a href="http://raspberrypi.stackexchange.com/q/298/44926"  target="_blank" rel="noreferrer">Can I use the GPIO for pulse width modulation (PWM)?</a></li>
<li><a href="https://www.youtube.com/watch?v=uUn0KWwwkq8"  target="_blank" rel="noreferrer">Dim an LED using Pulse-Width Modulation with the Raspberry Pi</a>, a YouTube video demonstrating PWM</li>
</ul>
<p>That’s it! Good luck with your own RGB LED experiment!</p>
<p>Questions? Suggestions on how to improve this? If you found this useful, I’d love to hear from you… leave a comment below. If you write about your own LED experience, leave a link too so I can check it out!</p>
]]></content:encoded><media:content url="https://grantwinney.com/raspberry-pi-pulse-width-modulation/feature.webp" medium="image" type="image/webp"/></item><item><title>A Simon Game Clone for the Raspberry Pi</title><link>https://grantwinney.com/raspberry-pi-simon-game-clone/</link><pubDate>Thu, 28 Jul 2016 08:09:41 +0000</pubDate><guid>https://grantwinney.com/raspberry-pi-simon-game-clone/</guid><description>Let&amp;rsquo;s recreate the Simon game of the 1980s using a Raspberry Pi and Sonic Pi!</description><content:encoded><![CDATA[<p>Have you been around long enough to remember the popular <em>Simon</em> game from the 70s and 80s? There’ve been plenty of remakes over the years, but I had one of the originals when I was younger.</p>
<p>It’s a game of patterns that tests your memory. It flashes a color and sounds a corresponding tone, which you repeat. Then it repeats the same color/tone and adds a new one. The pattern keeps getting longer and longer. Technically, it could go on forever, but I think you “won” after 20 or 30 colors… not that I ever got close!</p>

<h2 class="relative group">Initial Setup of a Simon Clone
    <div id="initial-setup-of-a-simon-clone" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#initial-setup-of-a-simon-clone" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Let&rsquo;s recreate the Simon game on the Raspberry Pi, in spirit at least, if not in exact gameplay and aesthetics!</p>

<h3 class="relative group">Configuring the GPIO Pins
    <div id="configuring-the-gpio-pins" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#configuring-the-gpio-pins" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>I like to have a function that’s run once at the beginning, and contains all the GPIO-related setup stuff, like which pins will read input vs which will output a signal, etc.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">initialize_gpio</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">setmode</span><span class="p">(</span><span class="n">GPIO</span><span class="o">.</span><span class="n">BOARD</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">setup</span><span class="p">(</span><span class="n">LIGHTS</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">OUT</span><span class="p">,</span> <span class="n">initial</span><span class="o">=</span><span class="n">GPIO</span><span class="o">.</span><span class="n">LOW</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">setup</span><span class="p">(</span><span class="n">BUTTONS</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">IN</span><span class="p">,</span> <span class="n">pull_up_down</span><span class="o">=</span><span class="n">GPIO</span><span class="o">.</span><span class="n">PUD_DOWN</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">4</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">GPIO</span><span class="o">.</span><span class="n">add_event_detect</span><span class="p">(</span><span class="n">BUTTONS</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">FALLING</span><span class="p">,</span> <span class="n">verify_player_selection</span><span class="p">,</span> <span class="mi">400</span> <span class="k">if</span> <span class="n">use_sounds</span> <span class="k">else</span> <span class="mi">250</span><span class="p">)</span></span></span></code></pre></div></div>
<p>We’re giving the buttons a pull-down resistor to make sure we get a clear “False / 0” reading while the button <em>isn’t</em> pressed, and the circuit is open. <a href="https://grantwinney.com/raspberry-pi-using-pullup-and-pulldown-resistors/"  target="_blank" rel="noreferrer">Read more about the importance of pull-down (and pull-up) resistors</a>.</p>
<p>The loop just attaches each button click to a function. We can get away with using the same function because it passes in the channel that triggered it. Actually, we’re only concerned with the button release (<code>GPIO.FALLING</code>) – I didn’t want it firing every time a button was pressed <em>and</em> released.</p>
<p>That last number is just a “bouncetime” in milliseconds, which ignores multiple clicks in under whatever time we specify. Ideally it wouldn’t be necessary, but buttons (esp the cheap buttons I have) can register several clicks when you only click them once, due to parts wiggling around and a flaky connection inside the button.</p>

<h3 class="relative group">Using Arrays for Easy Reference
    <div id="using-arrays-for-easy-reference" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#using-arrays-for-easy-reference" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>I stored the GPIO pin numbers for the lights and buttons, and the related musical notes, in arrays. It makes it easier to reference them from the code. When the player presses the “red” button, I detect <code>BUTTONS[1]</code> and then light up <code>LIGHTS[1]</code> and play <code>NOTES[1]</code>.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># green, red, blue, yellow</span>
</span></span><span class="line"><span class="cl"><span class="n">LIGHTS</span> <span class="o">=</span> <span class="p">[</span><span class="mi">33</span><span class="p">,</span> <span class="mi">37</span><span class="p">,</span> <span class="mi">35</span><span class="p">,</span> <span class="mi">31</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="n">BUTTONS</span> <span class="o">=</span> <span class="p">[</span><span class="mi">11</span><span class="p">,</span> <span class="mi">15</span><span class="p">,</span> <span class="mi">13</span><span class="p">,</span> <span class="mi">7</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="n">NOTES</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;E3&#34;</span><span class="p">,</span> <span class="s2">&#34;A4&#34;</span><span class="p">,</span> <span class="s2">&#34;E4&#34;</span><span class="p">,</span> <span class="s2">&#34;Cs4&#34;</span><span class="p">]</span></span></span></code></pre></div></div>
<p>And here’s how I used them. What I ended up actually doing was detecting which button was pressed, and at what index that button was located in the BUTTONS array, and then using the same index in the other arrays, to find the right light and musical note.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">play_note</span><span class="p">(</span><span class="n">NOTES</span><span class="p">[</span><span class="n">BUTTONS</span><span class="o">.</span><span class="n">index</span><span class="p">(</span><span class="n">channel</span><span class="p">)])</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="n">led</span> <span class="o">=</span> <span class="n">LIGHTS</span><span class="p">[</span><span class="n">BUTTONS</span><span class="o">.</span><span class="n">index</span><span class="p">(</span><span class="n">button_channel</span><span class="p">)]</span></span></span></code></pre></div></div>

<h3 class="relative group">Threading
    <div id="threading" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#threading" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Threads are your friend, so get used to the concept. The programs we use are capable of running multiple streams of logic in multiple threads, essentially doing many things at once. That’s how everything from operating systems like Raspbian, down to tiny programs like this one, work.</p>
<p>In this program, I split off the main game play into a separate thread. The call to “join” just makes sure the main thread doesn’t proceed (which would end the program) until the other thread ends. That leaves me open in the future to having the main thread do something apart from the rest of the program. Or I could fire up more threads to do additional work.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">start_game_monitor</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">start_game</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span><span class="o">.</span><span class="n">daemon</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span><span class="o">.</span><span class="n">join</span><span class="p">()</span></span></span></code></pre></div></div>
<p>And here’s what the other thread is doing. Until the player loses and decides <em>not</em> to play again, it’ll add colors to the pattern, show them to the player, and then wait for the player to repeat the pattern. If the player loses and decides to play again, the variables are reset and the loop continu</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">add_new_color_to_pattern</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">display_pattern_to_player</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">wait_for_player_to_repeat_pattern</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">is_game_over</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Game Over! Your max score was </span><span class="si">{}</span><span class="s2"> colors!</span><span class="se">\n</span><span class="s2">&#34;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">current_level</span><span class="o">-</span><span class="mi">1</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="n">play_again</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s2">&#34;Enter &#39;Y&#39; to play again, or just press [ENTER] to exit.</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">play_again</span> <span class="o">==</span> <span class="s2">&#34;Y&#34;</span> <span class="ow">or</span> <span class="n">play_again</span> <span class="o">==</span> <span class="s2">&#34;y&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">reset_board_for_new_game</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Begin new round!</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Thanks for playing!</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">break</span>
</span></span><span class="line"><span class="cl">    <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span></span></span></code></pre></div></div>
<p>When we finally wait for the player to repeat a pattern, all we do is the following loop – check a couple variables, then sleep for a tenth of a second and check again.</p>
<p>This effectively pauses execution of the current thread (not really, it’s still running but it’s not doing much), preventing anything else from happening until one of those variables is set to True (either the player wins the level or the game is over). We can get away with it because the functions we attached to the button click events earlier run in their own thread too.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">wait_for_player_to_repeat_pattern</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="ow">not</span> <span class="n">is_won_current_level</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">is_game_over</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span></span></span></code></pre></div></div>
<p>We end up with a few threads running. One at the start of the program, which fires off a second thread that runs the rest of the game, and a third that intercepts button clicks and calls the function we specified.</p>

<h3 class="relative group">Building a pattern
    <div id="building-a-pattern" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#building-a-pattern" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>In order to build up the pattern, we choose a random number from 0 to 3. If, for example, the number 2 is chosen, then blue is the newest color added to the pattern. We can use the selected number with our arrays, to light the correct LED and play the correct sound.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">add_new_color_to_pattern</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">global</span> <span class="n">is_won_current_level</span><span class="p">,</span> <span class="n">current_step_of_level</span>
</span></span><span class="line"><span class="cl">    <span class="n">is_won_current_level</span> <span class="o">=</span> <span class="kc">False</span>
</span></span><span class="line"><span class="cl">    <span class="n">current_step_of_level</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="n">next_color</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">pattern</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">next_color</span><span class="p">)</span></span></span></code></pre></div></div>

<h2 class="relative group">Musical notes, Sonic Pi and the sonic-pi-cli Gem
    <div id="musical-notes-sonic-pi-and-the-sonic-pi-cli-gem" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#musical-notes-sonic-pi-and-the-sonic-pi-cli-gem" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you haven’t heard of it before, <a href="http://sonic-pi.net"  target="_blank" rel="noreferrer">Sonic Pi</a> is a synthesizer that lets you program music. It was written by <a href="https://twitter.com/samaaron"  target="_blank" rel="noreferrer">Sam Aaron</a>, with the Pi in mind (it’s in its name after all), but you can run it on Windows or OS X too. It’s capable of amazing stuff, and I’ve only very, <em>very</em> lightly scratched the surface. I used it previously with the Pi to create a <a href="https://grantwinney.com/creating-music-with-sonic-pi-on-raspberry-pi/"  target="_blank" rel="noreferrer">grandfather clock that played the westminster chimes</a>.</p>
<p>At the very least, read the section entitled <em>What is OSC?,</em> install the ruby gem and try playing some notes <em>(make sure Sonic Pi is open).</em></p>

<h3 class="relative group">Recreating Simon’s notes
    <div id="recreating-simons-notes" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#recreating-simons-notes" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Once that’s setup and working, it should work from the Python script too. The notes, which I grabbed from <a href="https://en.wikipedia.org/wiki/Simon_%28game%29"  target="_blank" rel="noreferrer">Wikipedia</a>, don’t sound exactly the same as the original game, from what I could find of it on YouTube. Not sure the octave is right… probably missing several things. Whatever the reason, the effect is the same – four colors, four notes.</p>
<p>Simon’s tones … were designed to always be harmonic, no matter what order they were played in, and consisted of an A major triad in second inversion which resembles a Trumpet fanfare:</p>
<ul>
<li>E-note (blue, lower right)</li>
<li>C# note (yellow, lower left)</li>
<li>A-note (red, upper right)</li>
<li>E-note (green, upper left, an octave lower than blue)</li>
</ul>

<h3 class="relative group">Passing notes to Sonic Pi
    <div id="passing-notes-to-sonic-pi" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#passing-notes-to-sonic-pi" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Once everything else is working, it’s a one-liner to pass commands to Sonic Pi, like <em><code>_play :A4_</code></em> or similar.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">play_note</span><span class="p">(</span><span class="n">note</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">use_sounds</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">call</span><span class="p">([</span><span class="s2">&#34;sonic_pi&#34;</span><span class="p">,</span> <span class="s2">&#34;play :&#34;</span> <span class="o">+</span> <span class="n">note</span><span class="p">])</span></span></span></code></pre></div></div>
<p>Making calls to Sonic Pi takes up resources, and doing it as rapidly as I am (with each button click) makes things a little unstable. I don’t fully understand why yet, but I hope to figure it out eventually. In the meantime, I included a “use_sounds” flag you can set to enable/disable sound. Try leaving it True to test everything out with musical notes, but then set it to False in order to experience better (and more reliable) game play.</p>
<p>One of the things you’ll hear in programming, if you haven’t already, is the DRY principle. That stands for “don’t repeat yourself”. I didn’t copy the “call” line above into multiple places in the code. If I needed to make a change to the process call later, it’d be tougher if I had to hunt down everywhere I called it (especially across multiple files and projects, in a large program). So I created a single method that simply takes in the note, and then only write that line of code once. That turned out to be helpful when I added the <code>use_sounds</code> flag later, and I only had to update one place.</p>

<h3 class="relative group">Optimizing for Sonic Pi
    <div id="optimizing-for-sonic-pi" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#optimizing-for-sonic-pi" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The first four lines of the program make calls to the “sonic_pi” process.</p>
<p>The first two affect performance. When we request Sonic Pi to play a note for us, there’s a slight delay of about 200ms or so. It makes the Python program respond faster, but there’s a slight disconnect between when a button is pressed and when the note is actually played. By setting <code>set_sched_ahead_time</code> to 0, we tell it to play the note as soon as possible to when we called it.</p>
<p>The Pi isn’t all that powerful though, and it’s running an OS and other programs in the background, in addition to this program and having Sonic Pi open, at the very least. So even though the note plays closer to when the button is clicked, it causes the program to be a bit more sluggish. That’s why I added the flag that disables sound, so you can try it out both ways.</p>
<p>Setting “debug” mode to false just gives a slight perf boost.</p>
<p>The last two calls affect how the notes sound. Setting <code>use_synth</code> to “pulse” makes the notes a bit more clipped and louder. The BPM (beats per minute) is usually 60, so setting <code>use_bpm</code> to 100 causes the notes to finish quicker too.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">call</span><span class="p">([</span><span class="s2">&#34;sonic_pi&#34;</span><span class="p">,</span> <span class="s2">&#34;set_sched_ahead_time! 0&#34;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">        <span class="n">call</span><span class="p">([</span><span class="s2">&#34;sonic_pi&#34;</span><span class="p">,</span> <span class="s2">&#34;use_debug false&#34;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">        <span class="n">call</span><span class="p">([</span><span class="s2">&#34;sonic_pi&#34;</span><span class="p">,</span> <span class="s2">&#34;use_synth :pulse&#34;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">        <span class="n">call</span><span class="p">([</span><span class="s2">&#34;sonic_pi&#34;</span><span class="p">,</span> <span class="s2">&#34;use_bpm 100&#34;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">        <span class="n">os</span><span class="o">.</span><span class="n">system</span><span class="p">(</span><span class="s1">&#39;cls&#39;</span> <span class="k">if</span> <span class="n">os</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s1">&#39;nt&#39;</span> <span class="k">else</span> <span class="s1">&#39;clear&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Begin new round!</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">initialize_gpio</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="n">start_game_monitor</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">finally</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">GPIO</span><span class="o">.</span><span class="n">cleanup</span><span class="p">()</span></span></span></code></pre></div></div>
<p>Finally, you may have noticed a commented-out import statement.</p>
<p>I created a GPIOmock.py script that contains all the same calls as RPi.GPIO, but instead of doing anything with the GPIO pins it just prints a line of text describing what the call <em>would</em> do. That way, I can develop on a machine that’s not the Pi, and doesn’t have the RPi.GPIO package installed. I created it by referencing the comments available in the <code>source/py_gpio.c</code> file in the <a href="https://pypi.python.org/pypi/RPi.GPIO"  target="_blank" rel="noreferrer">RPi.GPIO module</a> available on <a href="https://github.com/Tieske/rpi-gpio/blob/master/source/py_gpio.c"  target="_blank" rel="noreferrer">GitHub</a>.</p>
<p>Copy the file into the same directory as this script, then comment out the <code>import RPi.GPIO</code> line and uncomment this one:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># import GPIOmock as GPIO</span></span></span></code></pre></div></div>
<p>If you’re interested in seeing a much cooler script running in Sonic Pi, <a href="https://gist.github.com/xavriley/87ef7548039d1ee301bb"  target="_blank" rel="noreferrer">someone recreated the NES Mario theme</a>! Copy lines 18-21 into a tab in Sonic Pi, and then all that stuff in the <code>theme_a</code>, <code>theme_b</code>, etc blocks.</p>

<h3 class="relative group">Caveats
    <div id="caveats" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#caveats" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>There are definitely some issues with sound. There’s a lag, which leads to ghost button clicks that mess up the gameplay. Disabling sound fixes it, and makes the game work just fine. So it’s definitely some issue with me passing a command to Sonic Pi to play each note.</p>
<p>There are other options for playing sounds too, which I won’t cover here. One of them is <a href="https://pypi.python.org/pypi/PyAudio"  target="_blank" rel="noreferrer">PyAudio</a>, and I found some examples easily with a quick search.</p>

<h2 class="relative group">The Circuit
    <div id="the-circuit" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-circuit" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Here are images and diagrams of the circuit I created. It connects four GPIO pins to the buttons (to receive input when they’re clicked), and four more pins to the LEDs (to send out a signal to light them up).</p>

<h3 class="relative group">Pictures
    <div id="pictures" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#pictures" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The resistors connecting the bottom button to 3.3v and the GPIO pin are just there because wires would’ve been too large, and in the way of my fingers.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="simon clone 1"
    src="/raspberry-pi-simon-game-clone/simon-clone-1.jpg"
    width="1167"
      height="1188"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="simon clone 2"
    src="/raspberry-pi-simon-game-clone/simon-clone-2.jpg"
    width="1353"
      height="1155"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="simon clone 3"
    src="/raspberry-pi-simon-game-clone/simon-clone-3.jpg"
    width="2011"
      height="1188"></figure>

<h3 class="relative group">Diagram
    <div id="diagram" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#diagram" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Here&rsquo;s the <a href="http://fritzing.org/"  target="_blank" rel="noreferrer">fritzing</a> diagram for the circuit:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="simon clone diagram"
    src="/raspberry-pi-simon-game-clone/simon-clone-diagram-1.png"
    width="1647"
      height="1254"></figure>

<h3 class="relative group">Demonstration
    <div id="demonstration" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#demonstration" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p><a href="https://res.cloudinary.com/dxm4riq52/video/upload/v1583332659/Raspberry%20Pi/Creating_a_Simon_Clone_on_the_Raspberry_Pi_ejdi8v.mp4"  target="_blank" rel="noreferrer">Watch a demo here</a>.</p>

<h2 class="relative group">Materials
    <div id="materials" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#materials" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><em><strong>You’ll need 4 buttons.</strong></em> That doesn’t seem like much, but many of these kits only come with two. I’m not sure why they don’t throw more in. Unless you’ve got extras laying around, you’ll need to find some - I grabbed a 50 pk off eBay for about $4.</p>
<p>I usually use the T-Cobbler that comes with the CanaKit sets, which makes it much easier to connect specific GPIO pins where they need to go. If you’re interested, check out their <a href="https://www.amazon.com/gp/product/B00G1PNG54/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B00G1PNG54&amp;linkCode=as2&amp;tag=gwin04-20&amp;linkId=d3b9704a7ee049261493e9113d01675c"  target="_blank" rel="noreferrer">Pi 2 Ultimate Starter Kit</a> or <a href="https://www.amazon.com/gp/product/B01C6Q4GLE/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B01C6Q4GLE&amp;linkCode=as2&amp;tag=gwin04-20&amp;linkId=5e06cbdecd6e1411799d573a3306ab01"  target="_blank" rel="noreferrer">Pi 3 Ultimate Starter Kit</a>, and look for the black T shaped board in the images. Those kits include the Pi, an adapter and case, and other items you’ll need like LEDs, wires and resistors.</p>
<p>In this case though, space was tight with all the LEDs and buttons, and I have some unfinished projects setup on my larger bread boards, so I used jumper wires to connect the pins instead of the T-Cobbler. I had about a million wires that could connect one point on the breadboard to another (male-to-male), but none that could connect the GPIO pins to the board (male-to-female). I purchased a pack of <a href="https://www.amazon.com/gp/product/B00AC4NQYG/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B00AC4NQYG&amp;linkCode=as2&amp;tag=gwin04-20&amp;linkId=a8a976ac13290414e33f1bb4ba21b67a"  target="_blank" rel="noreferrer">Phantom YoYo Jumper wires, male to female</a>, and haven’t had an issue with any of them so far.</p>
<p>If you try it, let me know how it goes, especially if you make improvements or run into problems you had to solve!</p>

<h2 class="relative group">Helpful Links
    <div id="helpful-links" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#helpful-links" aria-label="Anchor">#</a>
    </span>
    
</h2>
<ul>
<li><a href="http://www.cl.cam.ac.uk/projects/raspberrypi/sonicpi/media/sonic-pi-cheatsheet.pdf"  target="_blank" rel="noreferrer">Sonic Pi Cheat Sheet</a></li>
<li><a href="https://sourceforge.net/p/raspberry-gpio-python/wiki/Examples/"  target="_blank" rel="noreferrer">RPi.GPIO Usage Examples</a></li>
<li><a href="https://gist.github.com/rbnpi/2c6d2da3246f64f4d97e"  target="_blank" rel="noreferrer">Tuning Sonic Pi for best performance</a></li>
</ul>
]]></content:encoded><media:content url="https://grantwinney.com/raspberry-pi-simon-game-clone/feature.webp" medium="image" type="image/webp"/></item><item><title>Creating Music with Sonic Pi on a Raspberry Pi</title><link>https://grantwinney.com/raspberry-pi-create-music-with-sonic-pi/</link><pubDate>Tue, 21 Jun 2016 13:02:00 +0000</pubDate><guid>https://grantwinney.com/raspberry-pi-create-music-with-sonic-pi/</guid><description>After watching Scott Fradkin live-code Sonic Pi for an hour at a conference, it inspired me to make a little music of my own.</description><content:encoded><![CDATA[<p>Back in May, at the <a href="http://stirtrek.com/"  target="_blank" rel="noreferrer">Stir Trek conference</a> in Columbus OH, I got to watch Scott Fradkin live-code using Sonic Pi for nearly an hour, not only explaining what it was capable of, but showing it too.
He kept building it up as the session went on, and everyone in the room had a chance to see <em>and</em> hear what he was creating. By the end of the session, he had a good beat going! If you want to see the script he used, it&rsquo;s on <a href="https://github.com/sfradkin/presentations/tree/master/sonic-pi-programming-fun-profit"  target="_blank" rel="noreferrer">GitHub</a>.</p>
<p>If you&rsquo;ve never heard of it before though, you&rsquo;re probably wondering.. what <em>is</em> Sonic Pi?</p>

<h2 class="relative group">What is Sonic Pi?
    <div id="what-is-sonic-pi" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-is-sonic-pi" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="http://sonic-pi.net/"  target="_blank" rel="noreferrer">Sonic Pi</a> is an environment for programming sounds, where one can learn about music and coding at the same time. <a href="https://linktr.ee/samaaron"  target="_blank" rel="noreferrer">Sam Aaron</a> started on it to help students in the UK learn computer programming:</p>
<blockquote><p>Sonic Pi encourages you to learn about both computing and music through play and experimentation. The most important thing is that you’re having fun, and before you know it you’ll have accidentally learned how to code, compose and perform.<br>
<em>–</em> Sam Aaron_, “Welcome to Sonic Pi” tutorial_</p>
</blockquote><p>It&rsquo;s written in a way that&rsquo;s very forgiving to newcomers, using a Ruby-like syntax that reads more like English and is more forgiving with formatting than other languages.</p>

<h3 class="relative group">Installing
    <div id="installing" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#installing" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Everything you need to get started is on the <a href="https://sonic-pi.net/"  target="_blank" rel="noreferrer">sonic-pi</a> site - downloads for every major system, an <a href="https://sonic-pi.net/tutorial.html"  target="_blank" rel="noreferrer">online tutorial</a>, a <a href="https://magpi.raspberrypi.org/books/essentials-sonic-pi-v1"  target="_blank" rel="noreferrer">free book</a>, their <a href="https://in-thread.sonic-pi.net/"  target="_blank" rel="noreferrer">forums</a> (which seem very welcoming), and more.</p>
<p>If you&rsquo;re working on the Pi (as I will be in the example later on), the Jessie version of Raspbian comes preinstalled with Sonic Pi, so you don’t need to do anything else. However, you can run Sonic Pi on Mac, Windows, different *nix environments and more, so you can create music without being on the Raspberry Pi. There are <a href="https://github.com/sonic-pi-net/sonic-pi/releases"  target="_blank" rel="noreferrer">builds for all the major OS’s</a>.</p>

<h3 class="relative group">Trying it out
    <div id="trying-it-out" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#trying-it-out" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>When you start Sonic Pi for the first time, it opens a tutorial in the lower-left corner. I’d highly recommend following the first section, “Welcome to Sonic Pi”, and probably the second section too, “Synths”.</p>
<p>Here’s the equivalent of “Hello World” in Sonic Pi, taken from the tutorial. It just plays a steady beat like a metronome, in an infinite loop until you stop it.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">live_loop</span> <span class="p">:</span><span class="n">flibble</span> <span class="n">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">sample</span> <span class="p">:</span><span class="n">bd_haus</span><span class="p">,</span> <span class="n">rate</span><span class="p">:</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">  <span class="n">sleep</span> <span class="mf">0.5</span>
</span></span><span class="line"><span class="cl"><span class="n">end</span></span></span></code></pre></div></div>
<p>One of the cooler features of it is that you don&rsquo;t need to wait for the code to compile to try it. You can run a complex sound in a loop, change it on the fly, and listen to your changes immediately, which he refers to as &ldquo;live coding&rdquo;. I saw some of this in action at a conference a few years ago, and there&rsquo;s plenty of examples on <a href="https://www.youtube.com/c/SamAaron/videos"  target="_blank" rel="noreferrer">Sam&rsquo;s YouTube channel</a>.</p>

<h2 class="relative group">What is OSC?
    <div id="what-is-osc" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-is-osc" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Sonic Pi can record the music we create and generate a wav file, which can then be copied over to a Raspberry Pi (or any other) project and used as-is. But that&rsquo;s so.. static!</p>
<p>Alternatively, we can take advantage of the OSC protocol:</p>
<blockquote><p>OpenSound Control (OSC) is “a protocol for communication among computers, sound synthesizers, and other multimedia devices that is optimized for modern networking technology.” – <a href="http://osw.sourceforge.net/html/osc.htm"  target="_blank" rel="noreferrer"><em>OpenSound Control User Guide</em></a></p>
</blockquote><p>There’s a lot more on that page, but the important part is that it enables the Raspberry Pi to communicate with Sonic Pi, sending it the notes to play. It&rsquo;s more dynamic, allowing us to make changes without having to record a new wav file.</p>

<h3 class="relative group">Trying it out - the command line
    <div id="trying-it-out---the-command-line" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#trying-it-out---the-command-line" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Sonic Pi was designed with OSC in mind, and is configured to listen for OSC messages on localhost port 4557, so that’s where we send our messages. <a href="http://widdersh.in/controlling-sonic-pi-from-vim-or-anywhere-else/"  target="_blank" rel="noreferrer">Nick Johnstone</a> created a ruby gem called <a href="https://github.com/Widdershin/sonic-pi-cli"  target="_blank" rel="noreferrer">sonic-pi-cli</a>, which allows us to send messages via the command line, so let&rsquo;s take advantage of that.</p>
<p>After <a href="https://www.ruby-lang.org/en/documentation/installation/"  target="_blank" rel="noreferrer">installing Ruby</a> <em>(not necessary on the Pi),</em> install the gem:</p>
<p><code>sudo gem install sonic-pi-cli</code></p>
<p>Make sure Sonic Pi is running, then send it a note to play:</p>
<p><code>sonic_pi play :E4</code></p>
<p>If you don’t have Sonic Pi running, sonic-pi-cli won’t be able to communicate, you won’t hear any sounds, and you’ll get a helpful little reminder to start it up:</p>
<p><code>ERROR: Sonic Pi is not listening on 4557 – is it running?</code></p>

<h3 class="relative group">Trying it out - a Python script
    <div id="trying-it-out---a-python-script" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#trying-it-out---a-python-script" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Assuming that worked, we still need to find a way to communicate with Sonic Pi from Python. There are <a href="http://stackoverflow.com/q/22135511/301857"  target="_blank" rel="noreferrer">packages written for Python</a> that do just that, including python-osc, pyOSC and pyliblo, but let&rsquo;s just build on top of what we’ve already got.</p>
<p>Raspbian has Ruby and Python installed by default, so we can run commands from Python, to be intercepted by the Ruby gem, and forwarded on to Sonic Pi. Either place the following two lines in a script and run them, or type “python” in the terminal to open the python shell and then paste them in one at a time:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">subprocess</span> <span class="kn">import</span> <span class="n">call</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="n">call</span><span class="p">([</span><span class="s2">&#34;sonic_pi&#34;</span><span class="p">,</span> <span class="s2">&#34;play :E5&#34;</span><span class="p">])</span></span></span></code></pre></div></div>

<h2 class="relative group">Big Ben Chimes on a Raspberry Pi
    <div id="big-ben-chimes-on-a-raspberry-pi" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#big-ben-chimes-on-a-raspberry-pi" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you&rsquo;ve got enough to go on now, then good luck and have fun! If you&rsquo;re interested in seeing an example using the Raspberry Pi, then read on&hellip;</p>
<p>The idea for this project started when I was reading through the Sonic Pi tutorial, and somehow found myself on the wikipedia page for the <a href="https://en.wikipedia.org/wiki/Westminster_Quarters"  target="_blank" rel="noreferrer">Westminster Quarters</a>. That’s a melody used by some churches, grandfather clocks, Big Ben, etc., on each quarter hour.</p>
<p>I figured it might make a fun project that tied in the Pi, Python and Sonic Pi, in a unique way. I added the LEDs because the visual feedback is nice.</p>

<h3 class="relative group">The Circuit
    <div id="the-circuit" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-circuit" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>First, here’s the circuit I designed. It’s really basic, just connecting a series of LEDs to board pins 12, 16, 22, 36 and 40, which can be turned on or off in sequence with the notes being played.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Grandfather Clock Sonic Pi_bb"
    src="/raspberry-pi-create-music-with-sonic-pi/Grandfather-Clock-Sonic-Pi_bb.png"
    width="1593"
      height="996"></figure>

<h3 class="relative group">The Script
    <div id="the-script" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-script" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Make sure Sonic Pi is running, and then <a href="https://github.com/grantwinney/52-Weeks-of-Pi/tree/master/04-Sonic-Pi-Grandfather-Clock"  target="_blank" rel="noreferrer">check out and run the script</a>, or just copy the code below:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">subprocess</span> <span class="kn">import</span> <span class="n">call</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">threading</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">time</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">RPi.GPIO</span> <span class="k">as</span> <span class="nn">GPIO</span>
</span></span><span class="line"><span class="cl"><span class="c1"># import GPIOmock as GPIO</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="n">PAUSE_BETWEEN_NOTES</span> <span class="o">=</span> <span class="mf">0.75</span>
</span></span><span class="line"><span class="cl"><span class="n">CHECK_TIME_INTERVAL</span> <span class="o">=</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="c1"># LEDs to blink with each 15 min</span>
</span></span><span class="line"><span class="cl"><span class="n">QUARTER_LED_PINS</span> <span class="o">=</span> <span class="p">[</span><span class="mi">12</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">22</span><span class="p">,</span> <span class="mi">36</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="n">CHIME_LED_PIN</span> <span class="o">=</span> <span class="mi">40</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="c1"># Sets of notes for Westminster Quarters</span>
</span></span><span class="line"><span class="cl"><span class="c1"># https://en.wikipedia.org/wiki/Westminster_Quarters</span>
</span></span><span class="line"><span class="cl"><span class="n">NOTE_SET_1</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;Gs4&#34;</span><span class="p">,</span> <span class="s2">&#34;Fs4&#34;</span><span class="p">,</span> <span class="s2">&#34;E4&#34;</span><span class="p">,</span> <span class="s2">&#34;B3&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="n">NOTE_SET_2</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;E4&#34;</span><span class="p">,</span> <span class="s2">&#34;Gs4&#34;</span><span class="p">,</span> <span class="s2">&#34;Fs4&#34;</span><span class="p">,</span> <span class="s2">&#34;B3&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="n">NOTE_SET_3</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;E4&#34;</span><span class="p">,</span> <span class="s2">&#34;Fs4&#34;</span><span class="p">,</span> <span class="s2">&#34;Gs4&#34;</span><span class="p">,</span> <span class="s2">&#34;E4&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="n">NOTE_SET_4</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;Gs4&#34;</span><span class="p">,</span> <span class="s2">&#34;E4&#34;</span><span class="p">,</span> <span class="s2">&#34;Fs4&#34;</span><span class="p">,</span> <span class="s2">&#34;B3&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="n">NOTE_SET_5</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;B3&#34;</span><span class="p">,</span> <span class="s2">&#34;Fs4&#34;</span><span class="p">,</span> <span class="s2">&#34;Gs4&#34;</span><span class="p">,</span> <span class="s2">&#34;E4&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="n">NOTE_PERM_1</span> <span class="o">=</span> <span class="p">[</span><span class="n">NOTE_SET_1</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="n">NOTE_PERM_2</span> <span class="o">=</span> <span class="p">[</span><span class="n">NOTE_SET_2</span><span class="p">,</span> <span class="n">NOTE_SET_3</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="n">NOTE_PERM_3</span> <span class="o">=</span> <span class="p">[</span><span class="n">NOTE_SET_4</span><span class="p">,</span> <span class="n">NOTE_SET_5</span><span class="p">,</span> <span class="n">NOTE_SET_1</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="n">NOTE_PERM_4</span> <span class="o">=</span> <span class="p">[</span><span class="n">NOTE_SET_2</span><span class="p">,</span> <span class="n">NOTE_SET_3</span><span class="p">,</span> <span class="n">NOTE_SET_4</span><span class="p">,</span> <span class="n">NOTE_SET_5</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">play_note</span><span class="p">(</span><span class="n">note</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">call</span><span class="p">([</span><span class="s2">&#34;sonic_pi&#34;</span><span class="p">,</span> <span class="s2">&#34;play :&#34;</span> <span class="o">+</span> <span class="n">note</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">play_perm</span><span class="p">(</span><span class="n">note_perm</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">set_num</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">note_set</span> <span class="ow">in</span> <span class="n">note_perm</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">GPIO</span><span class="o">.</span><span class="n">output</span><span class="p">(</span><span class="n">QUARTER_LED_PINS</span><span class="p">[</span><span class="n">set_num</span><span class="p">],</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">HIGH</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">num</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">4</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="n">play_note</span><span class="p">(</span><span class="n">note_set</span><span class="p">[</span><span class="n">num</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">            <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="n">PAUSE_BETWEEN_NOTES</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">GPIO</span><span class="o">.</span><span class="n">output</span><span class="p">(</span><span class="n">QUARTER_LED_PINS</span><span class="p">[</span><span class="n">set_num</span><span class="p">],</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">LOW</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="n">PAUSE_BETWEEN_NOTES</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">set_num</span> <span class="o">+=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">play_hour_chimes</span><span class="p">(</span><span class="n">hour</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">num</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">hour</span> <span class="k">if</span> <span class="mi">0</span> <span class="o">&lt;</span> <span class="n">hour</span> <span class="o">&lt;</span> <span class="mi">13</span> <span class="k">else</span> <span class="nb">abs</span><span class="p">(</span><span class="n">hour</span> <span class="o">-</span> <span class="mi">12</span><span class="p">)):</span>
</span></span><span class="line"><span class="cl">        <span class="n">call</span><span class="p">([</span><span class="s2">&#34;sonic_pi&#34;</span><span class="p">,</span> <span class="s2">&#34;play :E3&#34;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">        <span class="n">GPIO</span><span class="o">.</span><span class="n">output</span><span class="p">(</span><span class="n">CHIME_LED_PIN</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">HIGH</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="n">PAUSE_BETWEEN_NOTES</span> <span class="o">+</span> <span class="mf">0.25</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">GPIO</span><span class="o">.</span><span class="n">output</span><span class="p">(</span><span class="n">CHIME_LED_PIN</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">LOW</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="n">PAUSE_BETWEEN_NOTES</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">sleep_to_next_minute</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">60</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">monitor</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">curr_time</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">time</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="n">curr_minute</span> <span class="o">=</span> <span class="n">curr_time</span><span class="o">.</span><span class="n">minute</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">curr_minute</span> <span class="o">==</span> <span class="mi">15</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">play_perm</span><span class="p">(</span><span class="n">NOTE_PERM_1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">sleep_to_next_minute</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="k">elif</span> <span class="n">curr_minute</span> <span class="o">==</span> <span class="mi">30</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">play_perm</span><span class="p">(</span><span class="n">NOTE_PERM_2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">sleep_to_next_minute</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="k">elif</span> <span class="n">curr_minute</span> <span class="o">==</span> <span class="mi">45</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">play_perm</span><span class="p">(</span><span class="n">NOTE_PERM_3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">sleep_to_next_minute</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="k">elif</span> <span class="n">curr_minute</span> <span class="o">==</span> <span class="mi">00</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">play_perm</span><span class="p">(</span><span class="n">NOTE_PERM_4</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">play_hour_chimes</span><span class="p">(</span><span class="n">curr_time</span><span class="o">.</span><span class="n">hour</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">sleep_to_next_minute</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="n">CHECK_TIME_INTERVAL</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">start_monitor</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">monitor</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span><span class="o">.</span><span class="n">daemon</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">initialize_gpio</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">setmode</span><span class="p">(</span><span class="n">GPIO</span><span class="o">.</span><span class="n">BOARD</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">setup</span><span class="p">(</span><span class="n">QUARTER_LED_PINS</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">OUT</span><span class="p">,</span> <span class="n">initial</span><span class="o">=</span><span class="n">GPIO</span><span class="o">.</span><span class="n">LOW</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">setup</span><span class="p">(</span><span class="n">CHIME_LED_PIN</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">OUT</span><span class="p">,</span> <span class="n">initial</span><span class="o">=</span><span class="n">GPIO</span><span class="o">.</span><span class="n">LOW</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">initialize_gpio</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="n">start_monitor</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="n">raw_input</span><span class="p">(</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">Press any key to exit.</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">finally</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">GPIO</span><span class="o">.</span><span class="n">cleanup</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">_name_</span> <span class="o">==</span> <span class="s1">&#39;_main_&#39;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">main</span><span class="p">()</span></span></span></code></pre></div></div>
<p>You may want to adjust the minutes in the <code>monitor</code> function, changing “00” to whatever the current minute is, so you can see feedback immediately when you run the script. Or just wait until the nearest quarter-hour!</p>

<h3 class="relative group">The Demo
    <div id="the-demo" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-demo" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Here’s a short video showing it in action.</p>

<h2 class="relative group">What&rsquo;s Next?
    <div id="whats-next" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whats-next" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you&rsquo;re interested in learning more, a few links to other resources:</p>
<ul>
<li><a href="https://magpi.raspberrypi.com/books/essentials-sonic-pi-v1"  target="_blank" rel="noreferrer">Code Music with Sonic Pi</a>, an e-book by Sam Aaron</li>
<li><a href="http://overtone.github.io/"  target="_blank" rel="noreferrer">Overtone</a>, Sonic-Pi’s “big brother”</li>
<li><a href="https://pypi.org/project/python-osc/"  target="_blank" rel="noreferrer">Open Sound Control server and client implementations in pure Python</a></li>
<li>A <a href="https://github.com/Widdershin/sonic-pi-cli"  target="_blank" rel="noreferrer">Ruby CLI for Sonic Pi</a>, created by Nick Johnstone</li>
</ul>
]]></content:encoded><media:content url="https://grantwinney.com/raspberry-pi-create-music-with-sonic-pi/feature.webp" medium="image" type="image/webp"/></item><item><title>Create a Raspberry Pi Virtual Machine (VM) in VirtualBox</title><link>https://grantwinney.com/raspberry-pi-virtual-machine-in-virtualbox/</link><pubDate>Wed, 08 Jun 2016 06:01:18 +0000</pubDate><guid>https://grantwinney.com/raspberry-pi-virtual-machine-in-virtualbox/</guid><description>I was flipping through The MagPi back-issues and came across an article about setting up a virtual Raspberry Pi environment. It got me thinking&amp;hellip; I’ve been playing around a lot on the Pi itself, but it’d be nice to experiment with code even when I don’t have access to a physical Pi.</description><content:encoded><![CDATA[<p>I recently started flipping through <a href="https://www.raspberrypi.org/magpi/issues/"  target="_blank" rel="noreferrer">The MagPi back-issues</a>, and came across an article where someone talked about setting up a virtual Raspberry Pi environment. At the time he wrote his article, I don’t think the Pi was even really available to the public yet.</p>
<p>It got me thinking though. I’ve been playing around a lot on the Pi itself, but it’d be convenient to have an environment setup where I could experiment with code even when I don’t have access to the Pi.</p>
<p><strong>Goal:</strong> Setup a virtual machine with <a href="https://www.raspberrypi.org/downloads/raspbian/"  target="_blank" rel="noreferrer">Debian</a> (from which Raspbian is derived). Install Python and other libraries and installers, so I can code solutions that can be migrated to the Pi.</p>
<p><strong>Limitations:</strong> The RPi.GPIO library expects the GPIO pins to be available. If you’re not on the Pi, you can’t execute code that directly accesses them. It’ll complain loudly that you can only run your code on an actual Pi.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="debian - gpio exception msg"
    src="/raspberry-pi-virtual-machine-in-virtualbox/debian-gpio-exception-msg.png"
    width="1603"
      height="319"></figure>
<p>We’ll contemplate work-arounds later. Just wanted to state this upfront though – this solution is not a complete virtual Pi.</p>
<p>Also, this is an attempt to closely emulate Raspbian, not the ARM processor found on the Pi. If you’re interested in trying to emulate the processor, check out <a href="http://wiki.qemu.org/Main_Page"  target="_blank" rel="noreferrer">QEMU</a>. It looked a little complicated to tackle, so I’m holding off for now, but I’d like to revisit it.</p>
<p><em>(Even better, reader</em> <a href="http://wetgenes.com/"  target="_blank" rel="noreferrer"><em>kriss</em></a> <em>seems to have gotten QEMU to work, and left a helpful comment below.)</em></p>

<h2 class="relative group">Install VirtualBox
    <div id="install-virtualbox" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#install-virtualbox" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I already use <a href="https://www.virtualbox.org/"  target="_blank" rel="noreferrer">VirtualBox</a> for emulating Windows and Ubuntu on a Mac, so I wanted to stick with that if possible.</p>

<h2 class="relative group">Why not Raspbian?
    <div id="why-not-raspbian" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#why-not-raspbian" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Unfortunately, although <a href="https://www.raspberrypi.org/downloads/raspbian/"  target="_blank" rel="noreferrer">Raspbian images</a> are available for download, you can’t use them to create a VM in VirtualBox. From what I could find, it’s apparently because the Pi runs on an ARM processor, but VirtualBox is designed to emulate OS’s that support X86 processors.</p>
<p>Whatever the reason, all I could get was a black screen with the message:</p>
<blockquote><p><em>“FATAL: No bootable medium found! System halted.”</em></p>
</blockquote><p>Raspbian is based off of Debian though, so let’s use Debian.</p>

<h2 class="relative group">Install Debian
    <div id="install-debian" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#install-debian" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Go to <a href="https://www.debian.org/distrib/"  target="_blank" rel="noreferrer">https://www.debian.org/distrib</a> and download the <a href="http://cdimage.debian.org/debian-cd/8.4.0/amd64/iso-cd/debian-8.4.0-amd64-netinst.iso"  target="_blank" rel="noreferrer">64-bit PC netinst iso</a>.</p>
<p>Create a new VirtualBox instance. Type the name “Debian” and it should auto-select type Linux and version Debian (64-bit) – if not, select them.</p>
<p>Set the settings according to the minimum requirements, which should pretty closely mirror the Pi. As of this writing, the current release is jessie. Using the graphical desktop, the recommended RAM and hard drive space is currently 1GB and 10GB, respectively.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="debian - virtualbox config 1"
    src="/raspberry-pi-virtual-machine-in-virtualbox/debian-virtualbox-config-1.png"
    width="1239"
      height="896"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="debian - virtualbox config 2"
    src="/raspberry-pi-virtual-machine-in-virtualbox/debian-virtualbox-config-2.png"
    width="1230"
      height="887"></figure>
<p>Choose the ISO file you downloaded earlier and install in VirtualBox, keeping all the defaults.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="debian - select image"
    src="/raspberry-pi-virtual-machine-in-virtualbox/debian-select-image.png"
    width="1283"
      height="788"></figure>
<p>Leave the network stuff the same, keeping the default hostname as-is and the domain name empty. Create passwords when prompted, and choose ‘yes’ when it asks you “write the changes to disks?”. Go get a cup of coffee. And a sandwich. It’s going to unpackage and install a lot of files, which can take 10-15 minutes.</p>
<p>When you finally get to the “device for boot loader installation” prompt, select the “/dev/sda” option from the list and continue. It’ll reboot and finally let you login.</p>
<p>When you finally get into Debian, click “Activities” in the upper-left and type in “term” to find the Terminal application.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="debian - find terminal"
    src="debian-find-terminal.png"
    ></figure>

<h3 class="relative group">Verify Python is Installed
    <div id="verify-python-is-installed" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#verify-python-is-installed" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>You can verify that python2 and python3 are already installed by starting each shell.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="debian - python 2 and 3 installed"
    src="/raspberry-pi-virtual-machine-in-virtualbox/debian-python-2-and-3-installed.png"
    width="1274"
      height="406"></figure>

<h3 class="relative group">Upgrading Packages
    <div id="upgrading-packages" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#upgrading-packages" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>From time to time, you may want to upgrade the packages installed on your new VM. To do that, you’ll need to be logged in as root, but “sudo” isn’t installed by default. You can <a href="https://unix.stackexchange.com/a/333062"  target="_blank" rel="noreferrer">install sudo easily enough</a>, which I’d recommend doing since most examples you find are going to tell you to use it.</p>
<p>To upgrade installed packages, run:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">sudo apt-get update
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">sudo apt-get upgrade</span></span></code></pre></div></div>

<h3 class="relative group">Get Git
    <div id="get-git" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#get-git" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Once you start developing code, the easiest way to migrate it to the Pi will be to commit it to GitHub in your VM, and then pull it from GitHub on the Pi. For that, you’ll need to install git:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">sudo apt-get install git</span></span></code></pre></div></div>

<h3 class="relative group">Try Out a Script
    <div id="try-out-a-script" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#try-out-a-script" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Let’s clone a project from GitHub and try out a script. There’s a repo on GitHub that’s full of “hello world” scripts in different languages. Let’s use that.</p>
<p>Create a new directory and clone &ldquo;<a href="https://github.com/leachim6/hello-world.git"  target="_blank" rel="noreferrer">https://github.com/leachim6/hello-world.git</a>&rdquo;. Run the “p/python.py” file.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="debian - hello world test"
    src="/raspberry-pi-virtual-machine-in-virtualbox/debian-hello-world-test.png"
    width="1528"
      height="554"></figure>

<h3 class="relative group">Installing Other Dependencies
    <div id="installing-other-dependencies" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#installing-other-dependencies" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>You can install whatever else you’d normally install on Raspbian too.</p>
<p>Do you use Pibrella? Follow “<a href="http://dbakevlar.com/2015/08/emulating-a-raspberry-pi-on-virtualbox"  target="_blank" rel="noreferrer">Emulating a Raspberry Pi on Virtualbox</a>”, starting half-way down under the header “Pibrella Module Installation” and “Python 3 Addition”.</p>
<p>If you typically use the <a href="https://en.wikipedia.org/wiki/Pip_%28package_manager%29"  target="_blank" rel="noreferrer">pip package manager</a>, you can easily install that too.</p>

<h3 class="relative group">What about the GPIO pins and RPi.GPIO?
    <div id="what-about-the-gpio-pins-and-rpigpio" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-about-the-gpio-pins-and-rpigpio" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>I use the RPi.GPIO library for easily communicating with the GPIO pins from my Python scripts.</p>
<p>You can install the RPi.GPIO library using <code>pip install RPi.GPIO.</code></p>
<p>If you get the following error message, it can’t find some file. Run <code>apt-get install python-dev</code> to make it happy.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="debian - install pibrella failed"
    src="/raspberry-pi-virtual-machine-in-virtualbox/debian-install-pibrella-failed.png"
    width="2046"
      height="1484"></figure>
<p>It should install okay, but if you try to run a script that takes advantage of it, you’ll get the loud error message I posted up at the top of this post. It wants the GPIO pins to be present.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="debian - install pibrella"
    src="/raspberry-pi-virtual-machine-in-virtualbox/debian-install-pibrella.png"
    width="1644"
      height="1154"></figure>
<p>I can think of an easy fix and a hard fix.</p>
<p>The hard fix would be to somehow trick Debian into thinking the hardware is present. I have no idea how to do that, but I’d love to find a way to trick it, and have an app that shows a Raspberry Pi. And every time RPi.GPIO tries to enable or disable a pin, the app intercepted it and indicated what was going on.</p>
<p>The easy(ier) fix is to write a GPIO script that mirrors all the functions in the official RPi.GPIO library, but just outputs a message to the console saying that a pin has been turned on, or off, or adjusted somehow. Tedious, but doable. <a href="https://github.com/grantwinney/52-Weeks-of-Pi/blob/master/GPIOmock.py"  target="_blank" rel="noreferrer">I already made one with just a few functions</a> for one of my scripts.</p>
<p>Make sure you include a line like this at the top of your scripts:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">import RPi.GPIO as GPIO</span></span></code></pre></div></div>
<p>Then create a mock script called MyMockedGpio or whatever you want. When you want to test things without the Pi, just replace the above line with:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">import MyMockedGpio as GPIO</span></span></code></pre></div></div>
<p>And the rest of your code should be none the wiser.</p>
<p>If you didn’t know where to start, hopefully this helps you out. And if you manage to take this to the next level, I’d love to hear from you! Feel free to leave a comment below&hellip;</p>
]]></content:encoded><media:content url="https://grantwinney.com/raspberry-pi-virtual-machine-in-virtualbox/feature.webp" medium="image" type="image/webp"/></item><item><title>Flash an LED on Your Raspberry Pi When You Get New Email</title><link>https://grantwinney.com/raspberry-pi-flash-led-for-new-email/</link><pubDate>Sat, 28 May 2016 21:19:25 +0000</pubDate><guid>https://grantwinney.com/raspberry-pi-flash-led-for-new-email/</guid><description>Let&amp;rsquo;s learn how to flash an LED on the Raspberry Pi when someone sends us a new email.</description><content:encoded><![CDATA[<p>Let&rsquo;s create an email notification system using the Raspberry Pi. It&rsquo;ll check for new email, and flash an LED when we get one.</p>

<h2 class="relative group">Connecting to Gmail
    <div id="connecting-to-gmail" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#connecting-to-gmail" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The circuit will be extremely straight-forward, so let’s focus on the more difficult part first – connecting to an email service.</p>
<p>We need to create a secure connection to our email provider, so we can find out when new mail arrives. Do a quick search, and you’ll likely find scripts <a href="http://stackoverflow.com/a/642988"  target="_blank" rel="noreferrer">like this one</a> where you just connect with your username, password and a few other pieces of info depending on who the provider is. But what you can do will be extremely limited, and the code will be fragile.</p>

<h3 class="relative group">Find the Official API
    <div id="find-the-official-api" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#find-the-official-api" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The preferred option is to check whether your email provider has already provided an “official” way to connect to their system and retrieve data from it. These are commonly referred to as APIs, or application programming interfaces.</p>
<p>When a company provides an API, that means they’ve put real time and effort into exposing certain areas of their system to you (although you’ll still have to do some work of your own, as we’ll see), and you can access those systems in relative confidence that how you’re connecting won’t just change or break.</p>
<p>Most of the major email services provide an API, including <a href="https://learn.microsoft.com/en-us/outlook/rest/get-started"  target="_blank" rel="noreferrer">MS Office</a>, <a href="https://developer.yahoo.com/sign-in-with-yahoo"  target="_blank" rel="noreferrer">Yahoo Mail</a>, and <a href="https://developers.google.com/gmail/api/quickstart/python"  target="_blank" rel="noreferrer">Gmail</a>. Gmail is the only service I use, so that’s the one I focused on here.</p>

<h3 class="relative group">Authenticating
    <div id="authenticating" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#authenticating" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>With Gmail, the API was my only option. Every time I tried a Python script I found, I’d get the following error:</p>
<blockquote><p><em>imaplib.error: [ALERT] Application-specific password required: <a href="https://support.google.com/accounts/answer/185833"  target="_blank" rel="noreferrer">https://support.google.com/accounts/answer/185833</a> (Failure)</em></p>
</blockquote><p>The problem is that <a href="https://support.google.com/accounts/answer/185833"  target="_blank" rel="noreferrer">I have 2-Step Verification enabled</a>, and the script can’t get past that. That’s okay… 2FA is a good thing, and we don’t want to disable that. There’s another, more secure and stable, way to access a Gmail account.</p>
<p>Google provides an API for connecting to most of their systems (including Gmail), along with tutorials to implement it in multiple languages (including Python). We can use the API to access messages or pretty much any other aspect of our email account.</p>
<p>This process involves entering a few details on their side, and then they assign you some special numbers (a “client id” and a “client secret”). You download those numbers in a special file called “client_secret.json” and include it with your script, which in turn helps prove that the script is authorized to access your account.</p>
<p>Follow their <a href="https://developers.google.com/gmail/api/quickstart/python"  target="_blank" rel="noreferrer">Python Quickstart</a>. Note the other languages on the left too, if you’d rather try one of those. After you authenticate, their sample script prints a list of your email tags:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="gmail api setup 4"
    src="/raspberry-pi-flash-led-for-new-email/gmail-api-setup-4.png"
    width="1874"
      height="1232"></figure>

<h3 class="relative group">Getting the Unread Mail Count
    <div id="getting-the-unread-mail-count" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#getting-the-unread-mail-count" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Once authenticated, their API gives you access to all kinds of useful info about your account. We’ll just modify the <a href="https://developers.google.com/gmail/api/quickstart/python#step_3_set_up_the_sample"  target="_blank" rel="noreferrer">sample script</a> they provided.</p>
<p>Most of their script is just about authenticating to Gmail, so don’t touch any of that. The following two lines got the list of labels above, and those are the ones we’ll change.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">results</span> <span class="o">=</span> <span class="n">service</span><span class="o">.</span><span class="n">users</span><span class="p">()</span><span class="o">.</span><span class="n">labels</span><span class="p">()</span><span class="o">.</span><span class="n">list</span><span class="p">(</span><span class="n">userId</span><span class="o">=</span><span class="s1">&#39;me&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">execute</span><span class="p">()</span> 
</span></span><span class="line"><span class="cl"><span class="n">labels</span> <span class="o">=</span> <span class="n">results</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;labels&#39;</span><span class="p">,</span> <span class="p">[])</span></span></span></code></pre></div></div>
<p>But what do we change them to? To find out which API calls we need to make, we’ll delve into the <a href="https://developers.google.com/apis-explorer/#p/gmail/v1/"  target="_blank" rel="noreferrer">APIs Explorer for the Gmail API</a>. It’s a nice tool, where you can browse through all the available API calls, and even try them out, all from within your browser.</p>
<p>The one we need is <a href="https://developers.google.com/apis-explorer/#p/gmail/v1/gmail.users.messages.list"  target="_blank" rel="noreferrer">gmail.users.messages.list</a>, so we can get a list of messages (and then count them). But we’re not interested in *all *email, just a subset. How do we do that?</p>
<p>Check out that screen capture above again. There’s a hidden label called “UNREAD” that’ll work nicely. The API allows you to place filters (such as <code>userId='me'</code>) inside the call to <code>list()</code>, and one of them is a query string (denoted as <code>q=...</code> below).</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">messages</span> <span class="o">=</span> <span class="n">service</span><span class="o">.</span><span class="n">users</span><span class="p">()</span><span class="o">.</span><span class="n">messages</span><span class="p">()</span><span class="o">.</span><span class="n">list</span><span class="p">(</span><span class="n">userId</span><span class="o">=</span><span class="s1">&#39;me&#39;</span><span class="p">,</span> <span class="n">q</span><span class="o">=</span><span class="s1">&#39;is:inbox + is:unread&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">execute</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">unread_count</span> <span class="o">=</span> <span class="n">messages</span><span class="p">[</span><span class="s1">&#39;resultSizeEstimate&#39;</span><span class="p">]</span></span></span></code></pre></div></div>
<p>By using <code>is:inbox + ``is:unread</code> we can get emails with <em>both</em> of those labels. The <a href="http://developers.squarespace.com/what-is-json/"  target="_blank" rel="noreferrer">json</a> response we get back includes two keys – one is a list of messages, while the other is the number of messages. We can use the latter to get our message count.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;xxxxxxxxxxxxxxxx&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">   <span class="nt">&#34;threadId&#34;</span><span class="p">:</span> <span class="s2">&#34;xxxxxxxxxxxxxxxx&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">],</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;resultSizeEstimate&#34;</span><span class="p">:</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Here’s the main portion of the script. I split most of the Gmail stuff into a separate file. The code that does the actual authorization to Gmail is in yet another file, which you can find along with the rest of the code.</p>
<p>Gmail.py:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">apiclient</span> <span class="kn">import</span> <span class="n">errors</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">threading</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">time</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">RPi.GPIO</span> <span class="k">as</span> <span class="nn">GPIO</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">GmailAuthorization</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="n">PIN</span> <span class="o">=</span> <span class="mi">35</span>
</span></span><span class="line"><span class="cl"><span class="n">CHECK_INTERVAL</span> <span class="o">=</span> <span class="mi">30</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="n">service</span> <span class="o">=</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl"><span class="n">unread_count</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">refresh</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">global</span> <span class="n">unread_count</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">messages</span> <span class="o">=</span> <span class="n">service</span><span class="o">.</span><span class="n">users</span><span class="p">()</span><span class="o">.</span><span class="n">messages</span><span class="p">()</span><span class="o">.</span><span class="n">list</span><span class="p">(</span><span class="n">userId</span><span class="o">=</span><span class="s1">&#39;me&#39;</span><span class="p">,</span> <span class="n">q</span><span class="o">=</span><span class="s1">&#39;is:inbox + is:unread&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">execute</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="n">unread_count</span> <span class="o">=</span> <span class="n">messages</span><span class="p">[</span><span class="s1">&#39;resultSizeEstimate&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="k">except</span> <span class="n">errors</span><span class="o">.</span><span class="n">HttpError</span> <span class="k">as</span> <span class="n">error</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;An error occurred: </span><span class="si">{0}</span><span class="s1">&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">error</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">indicator</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">unread_count</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">GPIO</span><span class="o">.</span><span class="n">output</span><span class="p">(</span><span class="n">PIN</span><span class="p">,</span> <span class="ow">not</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">input</span><span class="p">(</span><span class="n">PIN</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">GPIO</span><span class="o">.</span><span class="n">output</span><span class="p">(</span><span class="n">PIN</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">LOW</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.5</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">monitor</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">refresh</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="n">CHECK_INTERVAL</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">start_indicator</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">indicator</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span><span class="o">.</span><span class="n">daemon</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">start_monitor</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">monitor</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span><span class="o">.</span><span class="n">daemon</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">load_service</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">global</span> <span class="n">service</span>
</span></span><span class="line"><span class="cl">    <span class="n">service</span> <span class="o">=</span> <span class="n">GmailAuthorization</span><span class="o">.</span><span class="n">get_service</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">start</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">load_service</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">start_indicator</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">start_monitor</span><span class="p">()</span></span></span></code></pre></div></div>
<p>NewEmailIndicator.py:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">RPi.GPIO</span> <span class="k">as</span> <span class="nn">GPIO</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">Gmail</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="n">CHECK_NOW_PIN</span> <span class="o">=</span> <span class="mi">12</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">initialize_gpio</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">setmode</span><span class="p">(</span><span class="n">GPIO</span><span class="o">.</span><span class="n">BOARD</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">setup</span><span class="p">(</span><span class="n">Gmail</span><span class="o">.</span><span class="n">PIN</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">OUT</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">setup</span><span class="p">(</span><span class="n">CHECK_NOW_PIN</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">IN</span><span class="p">,</span> <span class="n">pull_up_down</span><span class="o">=</span><span class="n">GPIO</span><span class="o">.</span><span class="n">PUD_DOWN</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">add_event_detect</span><span class="p">(</span><span class="n">CHECK_NOW_PIN</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">RISING</span><span class="p">,</span> <span class="n">callback</span><span class="o">=</span><span class="n">check_mail_now</span><span class="p">,</span> <span class="n">bouncetime</span><span class="o">=</span><span class="mi">1000</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">check_mail_now</span><span class="p">(</span><span class="n">_</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">Gmail</span><span class="o">.</span><span class="n">refresh</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">initialize_gpio</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="n">Gmail</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="n">raw_input</span><span class="p">(</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">Press any key to exit.</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">finally</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">GPIO</span><span class="o">.</span><span class="n">cleanup</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">_name_</span> <span class="o">==</span> <span class="s1">&#39;_main_&#39;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">main</span><span class="p">()</span></span></span></code></pre></div></div>

<h2 class="relative group">Designing the Circuit
    <div id="designing-the-circuit" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#designing-the-circuit" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Once we’ve got a script that’ll connect to an email account, retrieve the data we’re interested in, and turn a GPIO pin on or off based on what we find, the next step is to create the circuit.</p>
<p>It’s a fairly simple one, limited to just an LED and a resistor, connecting board pin 35 to ground. I’ve also added a button (and second resistor) to the circuit, connected to board pin 12, that allows us to immediately check for new email without waiting for the interval. That way, we can change <code>CHECK_INTERVAL</code> to some less-frequent number like 60 (a minute), but then press the button if we don’t feel like waiting. That’s what the <code>add_event_detect</code> line is for in the above code.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/raspberry-pi-flash-led-for-new-email/fritz-diagram.png"
    width="1584"
      height="990"></figure>
<p>Here are some photos of the actual circuit.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/raspberry-pi-flash-led-for-new-email/breadboard1.webp"
    width="1650"
      height="1058"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/raspberry-pi-flash-led-for-new-email/breadboard2.webp"
    width="1700"
      height="1095"></figure>
<p>Questions? Comments? Hit me up in the comments section below.</p>

<h2 class="relative group"><strong>More Reading</strong>
    <div id="more-reading" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#more-reading" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Some extra reading if you’re interested. Just some stuff I came across while doing research for this project:</p>
<p><strong>Gmail</strong></p>
<ul>
<li><a href="https://developers.google.com/gmail/api/quickstart/python"  target="_blank" rel="noreferrer">Python Quickstart</a></li>
<li><a href="https://developers.google.com/api-client-library/python/apis/gmail/v1"  target="_blank" rel="noreferrer">Gmail API Client Library for Python</a></li>
<li><a href="https://developers.google.com/gmail/api/v1/reference/"  target="_blank" rel="noreferrer">Gmail API Reference</a></li>
<li><a href="https://developers.google.com/gmail/api/v1/reference/users/messages/list"  target="_blank" rel="noreferrer">Gmail API: Users.messages:list</a></li>
<li><a href="https://developers.google.com/apis-explorer/#p/gmail/v1/"  target="_blank" rel="noreferrer">Google APIs Explorer: Gmail</a></li>
<li><a href="https://developers.google.com/apis-explorer/#p/gmail/v1/gmail.users.messages.list"  target="_blank" rel="noreferrer">Google APIs Explorer (gmail.users.messages.list)</a></li>
<li><a href="https://github.com/google/oauth2client"  target="_blank" rel="noreferrer">Python library for accessing resources protected by OAuth 2.0</a></li>
<li><a href="https://github.com/google/google-api-python-client"  target="_blank" rel="noreferrer">Python client library for Google’s discovery based APIs</a></li>
</ul>
<p><strong>Raspberry Pi</strong></p>
<ul>
<li><a href="http://www.tweaking4all.com/hardware/breadboard"  target="_blank" rel="noreferrer">What is a Breadboard and How to use it</a></li>
<li><a href="http://raspi.tv/2014/rpi-gpio-update-and-detecting-both-rising-and-falling-edges"  target="_blank" rel="noreferrer">RPi.GPIO update and detecting BOTH rising and falling edges</a></li>
</ul>
<p><strong>Python</strong></p>
<ul>
<li><a href="https://docs.python.org/2/tutorial/errors.html"  target="_blank" rel="noreferrer">Errors and Exceptions</a></li>
<li><a href="https://sourceforge.net/projects/raspberry-gpio-python/"  target="_blank" rel="noreferrer">A Python module to control the GPIO on a Raspberry Pi</a></li>
</ul>
]]></content:encoded><media:content url="https://grantwinney.com/raspberry-pi-flash-led-for-new-email/feature.webp" medium="image" type="image/webp"/></item><item><title>Building a Morse Code Transmitter on a Raspberry Pi (version 2)</title><link>https://grantwinney.com/raspberry-pi-morse-code-transmitter-v2/</link><pubDate>Thu, 19 May 2016 06:10:28 +0000</pubDate><guid>https://grantwinney.com/raspberry-pi-morse-code-transmitter-v2/</guid><description>I created a morse code generator before based on entering a string at the console. Now I extended it to generate a message by clicking a button.</description><content:encoded><![CDATA[<p>Last month, <a href="https://grantwinney.com/raspberry-pi-morse-code-transmitter/"  target="_blank" rel="noreferrer">I created a morse code generator</a>. It accepts user input from the console, translates it into morse code, and blinks an LED to “transmit” the message.</p>
<p>I decided to build on that a bit, adding a button to the circuit that allows me to generate morse code from a button click. The clicks are read in by a GPIO pin, and interpreted by a Python script.</p>

<h2 class="relative group">Defining the Rules
    <div id="defining-the-rules" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#defining-the-rules" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>We should always figure out what a program is going to do <em>before</em> we start writing it, so here are a few rules to guide us:</p>
<ul>
<li>Dots and dashes will be entered using the rules on timing outlined in “<a href="https://grantwinney.com/raspberry-pi-morse-code-transmitter/#What_isMorse_Code"  target="_blank" rel="noreferrer">What is Morse Code?</a>“</li>
<li>An acceptable tolerance will be built into the timing, since it’s difficult to keep an exact rhythm.</li>
<li>Blink a blue LED to the rhythm of the “base time”, to help with timing.</li>
<li>Interpret dots and dashes using International Morse Code (IMC)</li>
<li>The <em>message separator</em> <a href="https://en.wikipedia.org/wiki/Prosigns_for_Morse_code"  target="_blank" rel="noreferrer">prosign</a> AR ·-·-· will indicate the end of the message, after which the script will display its interpretation.</li>
<li>When a dot or dash is timed correctly, blink green LED 3x; otherwise, blink red 3x</li>
<li>When a dot/dash sequence (indicated by a gap equal to 3 dots) is interpreted</li>
<li>
<ul>
<li>as a valid letter/number, blink a green LED 3x,
<ul>
<li>as invalid/unrecognized, blink a red LED 3x and discard the sequence.</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>That might not be everything, but it does give us a general direction to run.</p>

<h2 class="relative group">Designing the Circuit
    <div id="designing-the-circuit" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#designing-the-circuit" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Now let’s decide what we need in a circuit, based on the rules we just laid out.</p>
<ul>
<li>A button to “transmit” dots and dashes.</li>
<li>A line from 3.3v, thru a 220Ω resistor, to one side of the button (let’s call it side 1).</li>
<li>A line from the other side of the button (side 2) to pin 31 (GPIO 6).</li>
<li>A 10kΩ pulldown resistor from pin 31 to ground. <em>(</em><a href="https://grantwinney.com/raspberry-pi-using-pullup-and-pulldown-resistors/"  target="_blank" rel="noreferrer"><em>more on pulldown resistors</em></a><em>)</em></li>
<li>A yellow LED and 220Ω resistor from side 2 of the button, to ground.</li>
<li>A red LED and 220Ω resistor connecting pin 36 (GPIO 16) to ground.</li>
<li>A green LED and 220Ω resistor connecting pin 32 (GPIO 12) to ground.</li>
<li>A blue LED and 220Ω resistor connecting pin 11 (GPIO 17) to ground.</li>
</ul>
<p>Here’s the kind of layout I planned out.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Morse Code Button Click Fritzing Diagram"
    src="/raspberry-pi-morse-code-transmitter-v2/frtiz-diagram.png"
    width="1554"
      height="990"></figure>
<p>In retrospect, that resistor connecting the cathode side of the yellow LED to ground won’t hurt, but it’s unnecessary, since there’s already a resistor connecting 3.3v to the button.</p>
<p>And here are a few pictures of the actual board after I wired it up:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/raspberry-pi-morse-code-transmitter-v2/morse-code-button-click-3.webp"
    width="1424"
      height="1145"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/raspberry-pi-morse-code-transmitter-v2/morse-code-button-click-2.webp"
    width="1580"
      height="1030"></figure>

<h2 class="relative group">Writing the Script
    <div id="writing-the-script" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#writing-the-script" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Here’s the python script. Make sure you copy <a href="https://github.com/grantwinney/52-Weeks-of-Pi/blob/master/02-Send-Morse-Code-Via-Button-Click/InternationalMorseCode.py"  target="_blank" rel="noreferrer">InternationalMorseCode.py</a> from GitHub too, and place it in the same directory you&rsquo;re running this script from.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">datetime</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">threading</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">time</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">RPi.GPIO</span> <span class="k">as</span> <span class="nn">GPIO</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">InternationalMorseCode</span> <span class="k">as</span> <span class="nn">ICM</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="n">BASE_TIME_SECONDS</span> <span class="o">=</span> <span class="mf">1.0</span>
</span></span><span class="line"><span class="cl"><span class="n">TOLERANCE</span> <span class="o">=</span> <span class="n">BASE_TIME_SECONDS</span> <span class="o">/</span> <span class="mf">2.0</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="c1"># Initialize GPIO settings</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">initialize_gpio</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">setmode</span><span class="p">(</span><span class="n">GPIO</span><span class="o">.</span><span class="n">BOARD</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">setup</span><span class="p">([</span><span class="mi">11</span><span class="p">,</span> <span class="mi">32</span><span class="p">,</span> <span class="mi">36</span><span class="p">],</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">OUT</span><span class="p">)</span>  <span class="c1"># LEDs: Blue (metronome), Green (ok), Red (error)</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">setup</span><span class="p">(</span><span class="mi">31</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">IN</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">output</span><span class="p">([</span><span class="mi">32</span><span class="p">,</span> <span class="mi">36</span><span class="p">],</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">LOW</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">add_event_detect</span><span class="p">(</span><span class="mi">31</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">BOTH</span><span class="p">,</span> <span class="n">callback</span><span class="o">=</span><span class="n">intercept_morse_code</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="c1"># Blink a blue LED on/off (one full cycle per BASE_TIME_SECONDS)</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">metronome</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">GPIO</span><span class="o">.</span><span class="n">output</span><span class="p">(</span><span class="mi">11</span><span class="p">,</span> <span class="ow">not</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">input</span><span class="p">(</span><span class="mi">11</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="n">BASE_TIME_SECONDS</span> <span class="o">/</span> <span class="mf">2.0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">initialize_metronome</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">metronome</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span><span class="o">.</span><span class="n">daemon</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="c1"># Blink an LED on and off a few times rapidly, to signal success or failure</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">signal_to_user</span><span class="p">(</span><span class="n">channel</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">num</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">GPIO</span><span class="o">.</span><span class="n">output</span><span class="p">(</span><span class="n">channel</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">HIGH</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">GPIO</span><span class="o">.</span><span class="n">output</span><span class="p">(</span><span class="n">channel</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">LOW</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">initialize_signal</span><span class="p">(</span><span class="n">channel</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">signal_to_user</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="p">(</span><span class="n">channel</span><span class="p">,))</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="n">last_edge</span> <span class="o">=</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">LOW</span>
</span></span><span class="line"><span class="cl"><span class="n">press</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">release</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="c1"># Intercept a rise or fall on pin 31 (button press/release)</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">intercept_morse_code</span><span class="p">(</span><span class="n">channel</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">global</span> <span class="n">last_edge</span><span class="p">,</span> <span class="n">press</span><span class="p">,</span> <span class="n">release</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="c1"># Button pressed - determine if start of new letter/word</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">input</span><span class="p">(</span><span class="n">channel</span><span class="p">)</span> <span class="o">==</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">HIGH</span> <span class="ow">and</span> <span class="n">last_edge</span> <span class="o">==</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">LOW</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">last_edge</span> <span class="o">=</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">HIGH</span>
</span></span><span class="line"><span class="cl">        <span class="n">press</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="n">detect_termination</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="c1"># Button released - determine what the input is</span>
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">input</span><span class="p">(</span><span class="n">channel</span><span class="p">)</span> <span class="o">==</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">LOW</span> <span class="ow">and</span> <span class="n">last_edge</span> <span class="o">==</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">HIGH</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">last_edge</span> <span class="o">=</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">LOW</span>
</span></span><span class="line"><span class="cl">        <span class="n">release</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="n">interpret_input</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="n">sequence</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">letters</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"><span class="n">words</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="c1"># Detect whether most recent button press is start of new letter or word</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">detect_termination</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">global</span> <span class="n">sequence</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">sequence</span> <span class="o">==</span> <span class="s2">&#34;&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="n">delta</span> <span class="o">=</span> <span class="n">calc_delta_in_sec</span><span class="p">(</span><span class="n">release</span><span class="p">,</span> <span class="n">press</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="c1"># Check for start of new letter (gap equal to 3 dots)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">delta</span> <span class="o">&gt;=</span> <span class="p">((</span><span class="n">BASE_TIME_SECONDS</span> <span class="o">*</span> <span class="mi">3</span><span class="p">)</span> <span class="o">-</span> <span class="n">TOLERANCE</span><span class="p">))</span> <span class="ow">and</span> <span class="p">(</span><span class="n">delta</span> <span class="o">&lt;=</span> <span class="p">((</span><span class="n">BASE_TIME_SECONDS</span> <span class="o">*</span> <span class="mi">4</span><span class="p">)</span> <span class="o">+</span> <span class="n">TOLERANCE</span><span class="p">)):</span>
</span></span><span class="line"><span class="cl">        <span class="n">process_letter</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="c1"># Check for start of new word (gap equal to 7 dots - but assume anything &gt; 7 dots is valid too)</span>
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="n">delta</span> <span class="o">&gt;=</span> <span class="p">((</span><span class="n">BASE_TIME_SECONDS</span> <span class="o">*</span> <span class="mi">7</span><span class="p">)</span> <span class="o">-</span> <span class="n">TOLERANCE</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">process_word</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="c1"># If it&#39;s not a new letter or word, and it&#39;s a gap greater than a single dot, tell the user</span>
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="n">delta</span> <span class="o">&gt;</span> <span class="p">(</span><span class="n">BASE_TIME_SECONDS</span> <span class="o">+</span> <span class="n">TOLERANCE</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="c1"># Process letter</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">process_letter</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">global</span> <span class="n">sequence</span>
</span></span><span class="line"><span class="cl">    <span class="n">character</span> <span class="o">=</span> <span class="n">ICM</span><span class="o">.</span><span class="n">symbols</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">sequence</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">character</span> <span class="o">!=</span> <span class="s1">&#39;&#39;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Interpreted sequence &#34;</span> <span class="o">+</span> <span class="n">sequence</span> <span class="o">+</span> <span class="s2">&#34; as the letter: &#34;</span> <span class="o">+</span> <span class="n">character</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">letters</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">character</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">sequence</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">initialize_signal</span><span class="p">(</span><span class="mi">32</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;Invalid sequence: &#39;</span> <span class="o">+</span> <span class="n">sequence</span> <span class="o">+</span> <span class="s2">&#34; (deleting current sequence)&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">sequence</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">initialize_signal</span><span class="p">(</span><span class="mi">36</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">False</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="c1"># Process word</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">process_word</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">process_letter</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="n">word</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">letters</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">letters</span><span class="p">[:]</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">word</span> <span class="o">==</span> <span class="s2">&#34;AR&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;End of transmission. Here&#39;s your message: &#34;</span> <span class="o">+</span> <span class="s1">&#39; &#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">words</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\n</span><span class="s1">Clearing previous transmission. Start a new one now...</span><span class="se">\n</span><span class="s1">&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">words</span><span class="p">[:]</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">words</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">word</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="c1"># Interpret button click (press/release) as dot, dash or unrecognized</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">interpret_input</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">global</span> <span class="n">sequence</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="n">delta</span> <span class="o">=</span> <span class="n">calc_delta_in_sec</span><span class="p">(</span><span class="n">press</span><span class="p">,</span> <span class="n">release</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">delta</span> <span class="o">&gt;=</span> <span class="p">(</span><span class="n">BASE_TIME_SECONDS</span> <span class="o">-</span> <span class="n">TOLERANCE</span><span class="p">))</span> <span class="ow">and</span> <span class="p">(</span><span class="n">delta</span> <span class="o">&lt;=</span> <span class="p">(</span><span class="n">BASE_TIME_SECONDS</span> <span class="o">+</span> <span class="n">TOLERANCE</span><span class="p">)):</span>
</span></span><span class="line"><span class="cl">        <span class="n">sequence</span> <span class="o">+=</span> <span class="s1">&#39;.&#39;</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">delta</span><span class="p">)</span> <span class="o">+</span> <span class="s2">&#34; : Added dot to sequence:  &#34;</span> <span class="o">+</span> <span class="n">sequence</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">initialize_signal</span><span class="p">(</span><span class="mi">32</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="p">(</span><span class="n">delta</span> <span class="o">&gt;=</span> <span class="p">((</span><span class="n">BASE_TIME_SECONDS</span> <span class="o">*</span> <span class="mi">3</span><span class="p">)</span> <span class="o">-</span> <span class="n">TOLERANCE</span><span class="p">))</span> <span class="ow">and</span> <span class="p">(</span><span class="n">delta</span> <span class="o">&lt;=</span> <span class="p">((</span><span class="n">BASE_TIME_SECONDS</span> <span class="o">*</span> <span class="mi">3</span><span class="p">)</span> <span class="o">+</span> <span class="n">TOLERANCE</span><span class="p">)):</span>
</span></span><span class="line"><span class="cl">        <span class="n">sequence</span> <span class="o">+=</span> <span class="s1">&#39;-&#39;</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">delta</span><span class="p">)</span> <span class="o">+</span> <span class="s2">&#34; : Added dash to sequence: &#34;</span> <span class="o">+</span> <span class="n">sequence</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">initialize_signal</span><span class="p">(</span><span class="mi">32</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">delta</span><span class="p">)</span> <span class="o">+</span> <span class="s2">&#34; : Unrecognized input!&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">initialize_signal</span><span class="p">(</span><span class="mi">36</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">calc_delta_in_sec</span><span class="p">(</span><span class="n">time1</span><span class="p">,</span> <span class="n">time2</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">delta</span> <span class="o">=</span> <span class="n">time2</span> <span class="o">-</span> <span class="n">time1</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">delta</span><span class="o">.</span><span class="n">seconds</span> <span class="o">+</span> <span class="p">(</span><span class="n">delta</span><span class="o">.</span><span class="n">microseconds</span> <span class="o">/</span> <span class="mf">1000000.0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">initialize_gpio</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">initialize_metronome</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">message</span> <span class="o">=</span> <span class="n">raw_input</span><span class="p">(</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">Press any key to exit.</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">finally</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">cleanup</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Goodbye!&#34;</span><span class="p">)</span></span></span></code></pre></div></div>
<p>Hopefully most of this is self-explanatory, maybe with a little bit of studying the code. I’ll address a few points though. If you have questions about the rest of it, leave a comment and I’ll try to clarify it.</p>

<h3 class="relative group">Metronome
    <div id="metronome" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#metronome" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>If you’re unfamiliar with a metronome, it’s just a device that makes a regular beat or sound, to mark rhythm. I added an LED that blinks one cycle (on and off) per “base time”. All the code does is look at the current state of the LED (on or off), and flips it.</p>
<p><code>GPIO.output(11, not GPIO.input(11))</code></p>
<p>The <code>t.daemon</code> piece causes it stop running the thread when the program ends. Otherwise, the light just keeps on blinking!</p>

<h3 class="relative group">Success or Failure
    <div id="success-or-failure" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#success-or-failure" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The <code>signal_to_user</code> method simply takes a pin and turns it on and off a few times, rapidly. That gives us the flashing green and red LEDs.</p>
<p>Note that both of these methods run in a separate thread, so as not to freeze up the main thread that our program is running on. You can <a href="https://pymotw.com/2/threading/"  target="_blank" rel="noreferrer">read more about threading in Python here</a>.</p>

<h3 class="relative group">Detecting Button Clicks
    <div id="detecting-button-clicks" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#detecting-button-clicks" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The only thing we’re interested in is when a button was pressed and is then released, or vice versa. It’s possible, even with a pulldown resistor, to occasionally detect two presses or two releases in a row. Some of that is even due to the button itself… I’ve seen the duplicate events more often when I don’t push the button as forcefully, probably causing something inside to float between connected and disconnected a few times really quickly.</p>
<p>You can apply a “bouncetime” when you setup the pin, which tells it to ignore duplicate button presses that are really close together. But I preferred to just detect it and correct it myself, which is what I’m doing in <code>intercept_morse_code</code> with the <code>last_edge</code> stuff.</p>

<h2 class="relative group">More Resources
    <div id="more-resources" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#more-resources" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><strong>Courses</strong></p>
<p>If you’re interested, Coursera has a series of courses on Python, two of which are called <a href="https://www.coursera.org/learn/python"  target="_blank" rel="noreferrer">Getting Started with Python</a> and <a href="https://www.coursera.org/learn/python-data"  target="_blank" rel="noreferrer">Python Data Structures</a>. There’s another course I’ve started working through too, called <a href="https://www.coursera.org/learn/raspberry-pi-interface"  target="_blank" rel="noreferrer">Interfacing with the Raspberry Pi</a>.</p>
<p><strong>Reference Card</strong></p>
<p>Here’s a reference card from <a href="http://www.toptechboy.com/raspberry-pi/raspberry-pi-with-lesson-26-controlling-gpio-pins-in-python/"  target="_blank" rel="noreferrer">toptechboy.com</a> that shows what the pins do on the Pi. If you don’t have a cobbler that plugs into your breadboard, and you have to wire up individual GPIO pins on the Pi to your breadboard, you’ll want to keep this handy.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="raspberry-pi-2-pinout"
    src="/raspberry-pi-morse-code-transmitter-v2/raspberry-pi-2-pinout.jpg"
    width="1076"
      height="754"></figure>
<p><strong>Hardware</strong></p>
<p>If you want any of the accessory hardware you saw here, including the T-shaped cobbler and cable <em>(it’s very handy to not have to wire up all the individual GPIO pins!),</em> you can pick up the same set I did, called the <a href="https://amzn.to/2TwfVcL"  target="_blank" rel="noreferrer">CanaKit Raspberry Pi GPIO Breakout Board Bundle</a>.</p>
<p>I wouldn’t suggest it if I didn’t like it. It’s affordable <em>(in keeping with the spirit of the whole Pi movement),</em> has good reviews, and I’ve used most of the parts in it now and haven’t had a problem with a single one.</p>
<p><strong>Helpful Links</strong></p>
<ul>
<li><a href="http://www.raspberrypi-spy.co.uk/2012/06/simple-guide-to-the-rpi-gpio-header-and-pins/"  target="_blank" rel="noreferrer">Simple Guide to the RPi GPIO Header and Pins</a></li>
<li><a href="https://morsecode.world/international/morse.html"  target="_blank" rel="noreferrer">International Morse Code | Morse Code World</a></li>
<li><a href="https://www.geeksforgeeks.org/python/python-lists/"  target="_blank" rel="noreferrer">Python Lists | GeeksforGeeks</a></li>
<li><a href="https://pymotw.com/2/threading/"  target="_blank" rel="noreferrer">Manage Concurrent Threads</a> (Python)</li>
</ul>

<h2 class="relative group">Final Thoughts
    <div id="final-thoughts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#final-thoughts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>This project led me down the path of detecting the pin edges (whether the button is pressed or not, 1 or 0, on or off, high or low), and other pin-related concepts like bounce time. <a href="https://grantwinney.com/raspberry-pi-using-pullup-and-pulldown-resistors/"  target="_blank" rel="noreferrer">I wrote more about what I learned</a>.</p>
<p>Quick note about buttons. When you use one on your breadboard for the first time, it might feel like it only goes so far. Be sure to give it a good firm push so it’s flush with the breadboard, otherwise it won’t come into contact like it should. Mine looked like it was in at first, but wasn’t registering clicks very well.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Inserting the Button"
    src="/raspberry-pi-morse-code-transmitter-v2/P1020697.jpg"
    width="903"
      height="239"></figure>

<h2 class="relative group">Demo
    <div id="demo" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#demo" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://res.cloudinary.com/dxm4riq52/video/upload/v1583296485/Raspberry%20Pi/Generating_Morse_Code_on_the_Raspberry_Pi_Using_a_Button_on_a_Breadboard_aezrwv.mp4"  target="_blank" rel="noreferrer">Here&rsquo;s a demo if you want to see it working</a>.</p>
]]></content:encoded><media:content url="https://grantwinney.com/raspberry-pi-morse-code-transmitter-v2/feature.webp" medium="image" type="image/webp"/></item><item><title>Using PullUp and PullDown Resistors on the Raspberry Pi</title><link>https://grantwinney.com/raspberry-pi-using-pullup-and-pulldown-resistors/</link><pubDate>Mon, 09 May 2016 07:30:30 +0000</pubDate><guid>https://grantwinney.com/raspberry-pi-using-pullup-and-pulldown-resistors/</guid><description>When you start out creating circuits with the Raspberry Pi and its GPIO pins, there&amp;rsquo;s an unexpected but important concept to understand, called &amp;ldquo;floating&amp;rdquo;. To adjust for it, you need to understand how to use pullup and pulldown resistors.</description><content:encoded><![CDATA[<p>When you start out creating circuits with the Raspberry Pi and its GPIO pins, there&rsquo;s an unexpected but important concept to understand, called &ldquo;floating&rdquo;.</p>

<h2 class="relative group">Shopping List
    <div id="shopping-list" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#shopping-list" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If all you&rsquo;ve got right now is the Raspberry Pi, you&rsquo;ll want a kit with all the basics to get you up and running with simple projects - assuming it&rsquo;s in your budget. There are a lot of options. I&rsquo;ve had good luck with kits from Vilros and CanaKit before, but I don&rsquo;t see much from them on Amazon anymore.</p>
<p>If you&rsquo;re on a tighter budget, you&rsquo;ll need at least:</p>
<ul>
<li>raspberry-pi with micro USB adapter (~$50-75 ideally, although prices have shot up on Amazon - more on that below)</li>
<li>A few resistors, wires and a button. A basic starter kit should have these, or get the <a href="https://thepihut.com/collections/raspberry-pi-store/products/camjam-edukit"  target="_blank" rel="noreferrer">CamJam EduKit</a> - £5 (~$7)</li>
<li>A breadboard and either some wires to connect it to the Pi, or a T-Cobbler and ribbon (for a much easier and cleaner connection) - $5 - $15</li>
<li><em>&hellip;. a decent kit is much less headache the first time around!</em></li>
</ul>
<p>If you need the Raspberry Pi unit itself, the Pi is more expensive than it used to be on Amazon, apparently due to component shortages. It might be worth checking out <a href="https://rpilocator.com/"  target="_blank" rel="noreferrer">rpilocator</a> for a better price with other resellers, although you may have to wait a little longer to get your Pi then.</p>

<h2 class="relative group">A Simple Circuit
    <div id="a-simple-circuit" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#a-simple-circuit" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Imagine you&rsquo;re creating a circuit using a breadboard. Something very simple – a button, some wire and a power source (like the 3.3v pin on the Pi). You just want to be able to click a button to complete the circuit. Maybe it looks something like this.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Simple Button Circuit"
    src="/raspberry-pi-using-pullup-and-pulldown-resistors/Simple-Button-Circuit.png"
    width="1257"
      height="990"></figure>
<p>The above circuit connects 3.3v through a switch and <del>220Ω resistor</del>, to pin #6.</p>
<p><em><strong>NOTE: One reader left a comment about my use of a 220Ω resistor</strong>__, with a link to an authoritative site that has an article on the</em> <a href="http://www.mosaic-industries.com/embedded-systems/microcontroller-projects/raspberry-pi/gpio-pin-electrical-specifications#rpi-gpio-input-voltage-and-output-current-limitations"  target="_blank" rel="noreferrer"><em>electrical specifications</em></a> <em>for the Pi&rsquo;s GPIO pins. In it, the author states that one should &ldquo;never source or sink more than 0.5 mA into an input pin&rdquo;, although they don&rsquo;t explain why. According to an</em> <a href="https://www.rapidtables.com/calc/electric/watt-volt-amp-calculator.html"  target="_blank" rel="noreferrer"><em>electrical calculator</em></a><em>,</em> <em><strong>you&rsquo;d need at least a 7kΩ resistor to drop below the recommended 0.5 mA.</strong></em> <em>A 10kΩ resistor should work fine too, which is what I&rsquo;ve seen used in other examples. (Thanks Randall Stevens.)</em></p>
<p>That won&rsquo;t be very useful though, without a script to read the state of the circuit and take some action, even if it&rsquo;s just displaying a message. So here&rsquo;s a small Python script that does that for us. It detects when the circuit is opened or closed, and displays a message with a timestamp.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># coding=utf-8</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">RPi.GPIO</span> <span class="k">as</span> <span class="nn">GPIO</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">datetime</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">my_callback</span><span class="p">(</span><span class="n">channel</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">input</span><span class="p">(</span><span class="n">channel</span><span class="p">)</span> <span class="o">==</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">HIGH</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\n</span><span class="s1">▼  at &#39;</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()))</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\n</span><span class="s1"> ▲ at &#39;</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()))</span> 
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">setmode</span><span class="p">(</span><span class="n">GPIO</span><span class="o">.</span><span class="n">BCM</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">setup</span><span class="p">(</span><span class="mi">6</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">IN</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">add_event_detect</span><span class="p">(</span><span class="mi">6</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">BOTH</span><span class="p">,</span> <span class="n">callback</span><span class="o">=</span><span class="n">my_callback</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="n">message</span> <span class="o">=</span> <span class="n">raw_input</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\n</span><span class="s1">Press any key to exit.</span><span class="se">\n</span><span class="s1">&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">finally</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">cleanup</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Goodbye!&#34;</span><span class="p">)</span></span></span></code></pre></div></div>
<p>Here we specify the board numbering system, and then setup a pin to read input. Next we attach a <code>my_callback</code> function to the pin, so that some code runs whenever the circuit is closed or opened (the button is pressed or released). The code just displays a simple message with the current date and time.</p>
<p>Running the above script, I&rsquo;d expect to see a pattern of output like this, showing the timestamp for each time I press and then release the button.</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">▼  at 2016-04-30 12:26:44.124712
 ▲ at 2016-04-30 12:26:44.399541
▼  at 2016-04-30 12:26:44.857414
 ▲ at 2016-04-30 12:26:45.032816
▼  at 2016-04-30 12:26:45.397896
 ▲ at 2016-04-30 12:26:45.666379
▼  at 2016-04-30 12:26:46.015800</code></pre></div>
<p>Instead_,_ I see this, with 10 more screens just like it, in about 5 seconds:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="no bouncetime, no pulldown"
    src="/raspberry-pi-using-pullup-and-pulldown-resistors/no-bouncetime-no-pulldown.png"
    width="1476"
      height="1166"></figure>
<p>The circuit keeps bouncing up and down, all over the place, only stopping when I press the button and close the circuit. That behavior has a name – floating.</p>
<p>When the circuit isn’t closed, it’s not simply &ldquo;off&rdquo;. Instead, it’s said to be &ldquo;floating&rdquo;. When the circuit is open, the GPIO pin is still accepting input, and it picks up on all kinds of stuff in the environment, even static electricity. If you’re using wires in your circuit, they act like antennas, amplifying what the pin would pick up by itself.</p>

<h2 class="relative group">Defining a Few Terms
    <div id="defining-a-few-terms" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#defining-a-few-terms" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Let&rsquo;s define a few other terms too.</p>
<p><strong>Circuit</strong></p>
<p>Your circuit is the collection of all the connections you’ve made, using wires, resistors, LEDs, buttons, GPIO and other pins, etc.</p>
<p>It can be closed (like when you press a button, and a signal is able to traverse from one end to the other), or it can be open (a button is not pressed, or there’s some other break in the circuit).</p>
<p>An open circuit is like a long train of dominos, where you&rsquo;ve removed 4 or 5 from the middle. You can try sending a signal from one end, but it&rsquo;s never going to bridge the gap.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="domino-665547_1280"
    src="/raspberry-pi-using-pullup-and-pulldown-resistors/domino-665547_1280.jpg"
    width="1280"
      height="832"></figure>
<p><strong>High / Low</strong></p>
<p>Each GPIO pin has two states. You can call them on or off, high or low, 1 or 0, etc. A pin is set &ldquo;high&rdquo; when it&rsquo;s outputting 3.3v or reading in 3.3v, and &ldquo;low&rdquo; when it&rsquo;s off. The GPIO library calls these two states <code>GPIO.HIGH</code> and <code>GPIO.LOW</code>, and the library also helps you determine which state a pin is in.</p>
<p><strong>Rising Edge</strong></p>
<p>The moment a GPIO pin changes to a HIGH state.</p>
<p><strong>Falling Edge</strong></p>
<p>The moment a GPIO pin changes to a LOW state.</p>
<p><strong>Bouncetime</strong></p>
<p>You can specify a time during which repeat events will be ignored. For example, you can specify a bouncetime of 500 ms. That means that if you press a button multiple times in under half a second (or maybe the button&rsquo;s a bit loose and registers a click multiple times), subsequent clicks after the first one will be ignored for a brief time.</p>
<p><strong>Output</strong></p>
<p>Pins can be set to read input or send output, but not both at once. If a pin is set to output, the Pi can either send out 3.3 volts (<code>HIGH</code>), or not (<code>LOW</code> or 0 volts).</p>
<p><strong>Input</strong></p>
<p>If a pin is set to input, then the circuit must be closed for it read that input. In my case, that means pressing the button down in order to read <code>HIGH</code> or <code>1</code> (since I&rsquo;m connected to 3.3v&hellip; if my pin were connected to ground, it&rsquo;d read <code>LOW</code> or <code>0</code> when closed).</p>
<p>But what about when I&rsquo;m not pressing the button in the above circuit? It should be <code>LOW</code> or <code>0</code>, right? That&rsquo;s where the problem lies. Since the circuit is open, the GPIO pin could be reading all kinds of things from the environment, and it&rsquo;s fairly sensitive. We need a way to force the pin to <code>LOW</code> (also known as &ldquo;pull down&rdquo;) when the circuit is open (or to <code>HIGH</code> if the original circuit was connected to ground, also known as a &ldquo;pull up&rdquo;).</p>
<p><strong>Floating</strong></p>
<p>When you should be using a pull down or pull up resistor, but aren&rsquo;t, the status is said to be &ldquo;floating&rdquo;. That is, the pin and any wires connected to it pick up on random radiation and electromagnetic signals from the environment. When the circuit is open, it&rsquo;s not HIGH or LOW, but somewhere in between.</p>
<p><strong>Pull Down</strong></p>
<p>When you have a circuit that connects 3.3v to a GPIO pin, it&rsquo;ll read HIGH when the circuit is closed. When it&rsquo;s open, it could read anything. You need a &ldquo;pull down&rdquo; resistor connecting your circuit to ground, so that it reads LOW when the circuit is open. <em>(I&rsquo;ll show this in effect later.)</em></p>
<p><strong>Pull Up</strong></p>
<p>Similarly, if you have a circuit connecting your GPIO pin to ground when it&rsquo;s closed, it&rsquo;ll read <code>LOW</code>. You need a &ldquo;pull up&rdquo; resistor so that, when it&rsquo;s open, it defaults to the <code>HIGH</code> state.</p>
<p>In both cases, the button has no resistance (or at least, less resistance), and so when the circuit is closed it short-circuits around the pull up or pull down resistor and reads the other value. Hopefully this will make more sense with a couple demonstrations.</p>
<p><strong>Strong</strong></p>
<p>Strong resistors versus weak resistors only has meaning relative to one another. A lower value resistor is going to be stronger, in that it allows more current to flow through.</p>
<p><strong>Weak</strong></p>
<p>The weaker resistor will be the higher value one, which allows less current to flow through it.</p>
<p><strong>Internal Resistors</strong></p>
<p>In my circuit, I added a 10kΩ resistor to the breadboard, so I could see the circuit. The Pi has its own 1.8kΩ internal resistors that you can use, though, and I’ll show you how to use both.</p>

<h2 class="relative group">Revisiting the Simple Circuit
    <div id="revisiting-the-simple-circuit" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#revisiting-the-simple-circuit" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Let&rsquo;s check out our simple circuit again, and think about how to fix the floating problem.</p>

<h3 class="relative group">Option 1: Adding a Pull-Down Resistor to the Breadboard
    <div id="option-1-adding-a-pull-down-resistor-to-the-breadboard" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#option-1-adding-a-pull-down-resistor-to-the-breadboard" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Here&rsquo;s the circuit again. I shifted everything to the right a little bit, to make room for two things – a 10kΩ resistor and a wire, which effectively short-circuits pin #6 to ground. This forces (pulls down) the circuit into an &ldquo;off&rdquo; or 0 state when the button isn&rsquo;t being pushed, preventing the ups and downs we saw earlier.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Simple Button Circuit with pulldown res"
    src="/raspberry-pi-using-pullup-and-pulldown-resistors/Simple-Button-Circuit-with-pulldown-res.png"
    width="1257"
      height="990"></figure>
<p>All of the examples I&rsquo;ve seen elsewhere use 10kΩ, and according to at least <a href="http://www.mosaic-industries.com/embedded-systems/microcontroller-projects/raspberry-pi/gpio-pin-electrical-specifications#rpi-gpio-input-voltage-and-output-current-limitations"  target="_blank" rel="noreferrer">one authoritative source</a> you should stick with that (per my comment in the &ldquo;A Simple Circuit&rdquo; section above). A higher value resistor allows less current to flow through.</p>

<h3 class="relative group">Option 2: Enabling an Internal Pull-Down Resistor in the Code
    <div id="option-2-enabling-an-internal-pull-down-resistor-in-the-code" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#option-2-enabling-an-internal-pull-down-resistor-in-the-code" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Another option is to leave our circuit the way it was originally, but enable one of the internal resistors that reside on the Pi itself.</p>
<p>That&rsquo;s done by passing a value for <code>pull_up_down</code> to the <code>GPIO.setup()</code> function, as seen below. By specifying a value of <code>GPIO.PUD_DOWN</code>, we effectively add a pulldown resistor to our circuit.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># coding=utf-8</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">RPi.GPIO</span> <span class="k">as</span> <span class="nn">GPIO</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">datetime</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">my_callback</span><span class="p">(</span><span class="n">channel</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">input</span><span class="p">(</span><span class="n">channel</span><span class="p">)</span> <span class="o">==</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">HIGH</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\n</span><span class="s1">▼  at &#39;</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()))</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\n</span><span class="s1"> ▲ at &#39;</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()))</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">setmode</span><span class="p">(</span><span class="n">GPIO</span><span class="o">.</span><span class="n">BCM</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">setup</span><span class="p">(</span><span class="mi">6</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">IN</span><span class="p">,</span> <span class="n">pull_up_down</span><span class="o">=</span><span class="n">GPIO</span><span class="o">.</span><span class="n">PUD_DOWN</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">add_event_detect</span><span class="p">(</span><span class="mi">6</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">BOTH</span><span class="p">,</span> <span class="n">callback</span><span class="o">=</span><span class="n">my_callback</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="n">message</span> <span class="o">=</span> <span class="n">raw_input</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\n</span><span class="s1">Press any key to exit.</span><span class="se">\n</span><span class="s1">&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">finally</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">cleanup</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Goodbye!&#34;</span><span class="p">)</span></span></span></code></pre></div></div>

<h2 class="relative group">Demos
    <div id="demos" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#demos" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p><a href="https://res.cloudinary.com/dxm4riq52/video/upload/q_auto:eco/v1583296431/Raspberry%20Pi/Demonstration_of_a_Pulldown_Resistor_gejypz.mp4"  target="_blank" rel="noreferrer">Here&rsquo;s a short video showing what I see</a> (with and without) the pulldown resistor, and <a href="https://www.youtube.com/watch?v=wxjerCHCEMg"  target="_blank" rel="noreferrer">here&rsquo;s a better video</a>, in which James Lewis explains the same concept, except he&rsquo;s using a TI LaunchPad instead of a Raspberry Pi.</p>

<h2 class="relative group">Resources
    <div id="resources" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#resources" aria-label="Anchor">#</a>
    </span>
    
</h2>
<ul>
<li>
<p><a href="http://raspberrypi.stackexchange.com/q/14105/44926"  target="_blank" rel="noreferrer">How does python GPIO bouncetime parameter work</a>?</p>
</li>
<li>
<p><a href="https://www.raspberrypi.org/documentation/usage/gpio/"  target="_blank" rel="noreferrer">GPIO: Raspberry Pi Models A and B</a></p>
</li>
<li>
<p><a href="https://www.raspberrypi.org/documentation/usage/gpio-plus-and-raspi2/README.md"  target="_blank" rel="noreferrer">GPIO: Raspberry Pi Models A+, B+, 2B and 3B</a></p>
</li>
<li>
<p><a href="http://makezine.com/projects/tutorial-raspberry-pi-gpio-pins-and-python/"  target="_blank" rel="noreferrer">Tutorial: Raspberry Pi GPIO Pins and Python</a></p>
</li>
<li>
<p><a href="http://raspi.tv/2013/how-to-use-interrupts-with-python-on-the-raspberry-pi-and-rpi-gpio-part-3"  target="_blank" rel="noreferrer">Multiple threaded callback interrupts in Python</a></p>
</li>
<li>
<p><a href="http://raspi.tv/2014/rpi-gpio-update-and-detecting-both-rising-and-falling-edges"  target="_blank" rel="noreferrer">RPi.GPIO update and detecting BOTH rising and falling edges</a></p>
</li>
<li>
<p><a href="http://raspi.tv/2013/rpi-gpio-basics-7-rpi-gpio-cheat-sheet-and-pointers-to-rpi-gpio-advanced-tutorials"  target="_blank" rel="noreferrer">RasPi.TV RPi.GPIO Quick Reference “cheat sheet”</a></p>
</li>
<li>
<p><a href="http://raspi.tv/2013/rpi-gpio-basics-6-using-inputs-and-outputs-together-with-rpi-gpio-pull-ups-and-pull-downs#pullup"  target="_blank" rel="noreferrer">RPi.GPIO basics 6 – Using inputs and outputs together with RPi.GPIO – pull-ups and pull-downs</a></p>
</li>
<li>
<p><a href="http://electronics.stackexchange.com/a/58545/109152"  target="_blank" rel="noreferrer">Pull-up and Pull-down Resistor Usage on Input or Output MCU Pins</a></p>
</li>
<li>
<p><a href="http://www.mosaic-industries.com/embedded-systems/microcontroller-projects/raspberry-pi/gpio-pin-electrical-specifications#rpi-gpio-input-voltage-and-output-current-limitations"  target="_blank" rel="noreferrer">GPIO Electrical Specifications, Raspberry Pi Input and Output Pin Voltage and Current Capability</a>
A three-parter by Alex at RasPi.TV, titled <em>&ldquo;How to use interrupts with Python on the Raspberry Pi and RPi.GPIO&rdquo;.</em></p>
</li>
<li>
<p><a href="http://raspi.tv/2013/how-to-use-interrupts-with-python-on-the-raspberry-pi-and-rpi-gpio"  target="_blank" rel="noreferrer">How to use interrupts… Part 1</a></p>
</li>
<li>
<p><a href="http://raspi.tv/2013/how-to-use-interrupts-with-python-on-the-raspberry-pi-and-rpi-gpio-part-2"  target="_blank" rel="noreferrer">How to use interrupts… Part 2</a></p>
</li>
<li>
<p><a href="http://raspi.tv/2013/how-to-use-interrupts-with-python-on-the-raspberry-pi-and-rpi-gpio-part-3"  target="_blank" rel="noreferrer">How to use interrupts… Part 3</a></p>
</li>
</ul>
<p>If you&rsquo;re brand new to Python…</p>
<ul>
<li><a href="https://automatetheboringstuff.com/"  target="_blank" rel="noreferrer">Automate the Boring Stuff with Python</a></li>
</ul>
<p>And if you just can&rsquo;t get your fill of Pi_,_ check out my list of resources on GitHub:</p>
<ul>
<li><a href="https://github.com/grantwinney/314-or-so-awesome-raspberry-pi-resources"  target="_blank" rel="noreferrer">314 (or so) Awesome Raspberry Pi Resources</a></li>
</ul>
]]></content:encoded><media:content url="https://grantwinney.com/raspberry-pi-using-pullup-and-pulldown-resistors/feature.webp" medium="image" type="image/webp"/></item><item><title>Building a Morse Code Transmitter on a Raspberry Pi</title><link>https://grantwinney.com/raspberry-pi-morse-code-transmitter/</link><pubDate>Sun, 03 Apr 2016 09:03:25 +0000</pubDate><guid>https://grantwinney.com/raspberry-pi-morse-code-transmitter/</guid><description>Making the Pi blink an LED a few times is thrilling, but what about building something.. more? Let&amp;rsquo;s build a morse code transmitter!</description><content:encoded><![CDATA[<p>Last week, <a href="https://grantwinney.com/raspberry-pi-making-an-led-blink/"  target="_blank" rel="noreferrer">I made the Raspberry Pi blink an LED a few times</a>. As thrilling as that was, I almost immediately wanted something more. Building a morse code transmitter seemed like a nice little challenge.</p>

<h2 class="relative group">Goals
    <div id="goals" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#goals" aria-label="Anchor">#</a>
    </span>
    
</h2>
<ul>
<li>Setup a simple circuit (LED and resistor) using a breadboard</li>
<li>Learn about Morse Code in order to correctly translate a sentence</li>
<li>Manipulate the GPIO pins on the Raspberry Pi to send signals at intervals</li>
<li>Get familiar with basic Python constructs, like dictionaries, functions and loops</li>
</ul>

<h2 class="relative group">Setup
    <div id="setup" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#setup" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>To do this, a few things are necessary:</p>
<ul>
<li>Install Raspbian on the Pi <em>(comes with Python 3 preinstalled)</em></li>
<li>Get a kit with a breadboard, LED and resistor <em>(cobbler is optional, but helpful)</em></li>
<li><a href="https://grantwinney.com/raspberry-pi-making-an-led-blink/"  target="_blank" rel="noreferrer">Setup a breadboard with an LED and resistor</a></li>
</ul>

<h2 class="relative group">What is Morse Code?
    <div id="what-is-morse-code" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-is-morse-code" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The first thing I did was head over to wikipedia to read all about <a href="https://en.wikipedia.org/wiki/Morse_code"  target="_blank" rel="noreferrer">morse code</a>, and to decide how much I wanted to implement for this project.</p>
<ul>
<li>Morse code is a method of transmitting text as a series of on-off tones, lights, or clicks.</li>
<li>One variant is called International Morse Code (IMC).</li>
<li>IMC consists of the Latin alphabet (A-Z), Arabic numerals (0-9), some punctuation, and procedural signals (prosigns).</li>
<li>Each IMC symbol is represented by a unique sequence of short and long signals called “dots” and “dashes”.</li>
<li>Prosigns are sequences of letters which have special meaning, such as AS for “Wait”, SN for “Understood” or SOS for “Distress”.</li>
</ul>
<p>Here are the rules regarding timing, for relaying a message in morse code.</p>
<ul>
<li>A dot duration is the basic unit of time measurement in code transmission (.) : 1</li>
<li>A dash duration is 3x the dot duration (–) : 111</li>
<li>Each dot or dash is followed by a short silence, equal to the dot duration : 0</li>
<li>Letters in a word are separated by a gap equal to 3 dots : 000</li>
<li>Words are separated by a gap equal to 7 dots : 0000000</li>
</ul>
<p>The wikipedia page also includes a <a href="https://en.wikipedia.org/wiki/File:International_Morse_Code.svg"  target="_blank" rel="noreferrer">morse code reference chart</a> we can use for translating.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/raspberry-pi-morse-code-transmitter/International_Morse_Code.png"
    width="883"
      height="829"></figure>
<p>That should be enough information to get started.</p>

<h2 class="relative group">Coding the Translator
    <div id="coding-the-translator" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#coding-the-translator" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Here are a few notable notes about the approach I took.</p>

<h3 class="relative group">The Circuit
    <div id="the-circuit" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-circuit" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>A closeup of the circuit I created. <em>Pin 21 » LED » resistor » ground</em></p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="breadboard single led circuit"
    src="/raspberry-pi-morse-code-transmitter/breadboard-single-led-circuit.jpg"
    width="720"
      height="389"></figure>

<h3 class="relative group">Mapping Characters to Morse Code
    <div id="mapping-characters-to-morse-code" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#mapping-characters-to-morse-code" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The first thing I decided to do was get the above chart into a dictionary. Now I can look up any character and get its morse code equivalent.</p>
<p>In Python, the dictionary looks something like this (trimmed down):</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">Symbols</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;A&#39;</span><span class="p">:</span> <span class="s1">&#39;.-&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;B&#39;</span><span class="p">:</span> <span class="s1">&#39;-...&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;C&#39;</span><span class="p">:</span> <span class="s1">&#39;-.-.&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># more letters</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;1&#39;</span><span class="p">:</span> <span class="s1">&#39;.----&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;2&#39;</span><span class="p">:</span> <span class="s1">&#39;..---&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;3&#39;</span><span class="p">:</span> <span class="s1">&#39;...--&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># more numbers</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;.&#39;</span><span class="p">:</span> <span class="s1">&#39;.-.-.-&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;,&#39;</span><span class="p">:</span> <span class="s1">&#39;--..--&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;?&#39;</span><span class="p">:</span> <span class="s1">&#39;..--..&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># more punctuation</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

<h3 class="relative group">Mocking Out the GPIO Module
    <div id="mocking-out-the-gpio-module" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#mocking-out-the-gpio-module" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The GPIO module is a library of methods that make manipulating the GPIO pins easy. As easy as specifying which pin to use, if it’ll receive a signal (in) or send one (out), and whether it’s on (high) or off (low).</p>
<p>I found it easier to develop on a laptop with PyCharm installed, instead of on the Pi itself, so I didn’t have access to the RPi.GPIO module.</p>
<p>As a workaround, I created a mock file with the same functions I’d be accessing from the RPi.GPIO library, each of which just printed a message to the screen. It prints “Sending High signal” when it should’ve turned the LED on, “Sending Low signal” when it should’ve turned the LED off, and so on.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">HIGH</span> <span class="o">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="n">LOW</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="n">BCM</span> <span class="o">=</span> <span class="s1">&#39;BroadCom Numbering&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">BOARD</span> <span class="o">=</span> <span class="s1">&#39;Board Numbering&#39;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="n">IN</span> <span class="o">=</span> <span class="s1">&#39;&#34;In&#34; Direction for Pin&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">OUT</span> <span class="o">=</span> <span class="s1">&#39;&#34;Out&#34; Direction for Pin&#39;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">output</span><span class="p">(</span><span class="n">pin</span><span class="p">,</span> <span class="n">signal</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Sending </span><span class="si">{}</span><span class="s2"> signal to GPIO pin </span><span class="si">{}</span><span class="s2">&#34;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">_convert_signal_to_text</span><span class="p">(</span><span class="n">signal</span><span class="p">),</span> <span class="n">pin</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="c1"># omitted other functions my program mocks out,</span>
</span></span><span class="line"><span class="cl"><span class="c1">#  but you can find them in the github repo</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">_convert_signal_to_text</span><span class="p">(</span><span class="n">signal</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">signal</span> <span class="o">==</span> <span class="n">HIGH</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;High&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Low&#34;</span></span></span></code></pre></div></div>
<p>Whether importing the real library or my mock, I made sure to give them an alias of “GPIO”. Running the program on the Pi was then as simple as replacing <code>import GPIOmock as GPIO</code> with <code>import RPi.GPIO as GPIO</code>, and all other lines of code referencing <code>GPIO.whatever()</code> could be left as-is.</p>

<h3 class="relative group">Distinguishing Dots from Dashes from Letters from…
    <div id="distinguishing-dots-from-dashes-from-letters-from" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#distinguishing-dots-from-dashes-from-letters-from" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The user can input a full sentence, but that has to be broken down into smaller components for transmission.</p>
<p>Python’s <code>split()</code> command breaks up the initial sentence into words nicely, and from there it’s loops all the way down. Do <em>something</em> for each symbol, in each letter, in each word.</p>
<p>And since all intervals between the dots and dashes are simply a multiple of the base unit time, defined as the time for a single “dot”, I created a variable called <code>UNIT_TIME</code> that represents seconds. Everything else – dots, dashes, space between letters and words, is based off of that, per the rules above. We can change the value in <code>UNIT_TIME</code> to transmit messages more quickly or more slowly.</p>

<h2 class="relative group">Trying it Out
    <div id="trying-it-out" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#trying-it-out" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The kids have been having fun with it. Said they were sending messages to Australia. Hey, anything’s possible, but you’d have to have a pretty decent pair of binoculars and maybe a few mirrors to see our LED from the other side of the world. :)</p>
<p><a href="https://res.cloudinary.com/dxm4riq52/video/upload/q_auto/v1583296394/Raspberry%20Pi/Morse_Code_via_LED_on_the_Raspberry_Pi_2_lmqsvf.mp4"  target="_blank" rel="noreferrer">Here it is working</a>.. if you try this out yourself, let me know how it goes! If you improve on it, or find a flaw in my approach, I’d like to hear about that too – share in the comments below.</p>

<h2 class="relative group">Final Thoughts
    <div id="final-thoughts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#final-thoughts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Other random things I learned…</p>

<h3 class="relative group">Circuit Design Tools
    <div id="circuit-design-tools" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#circuit-design-tools" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The circuit I created for this was as simple as it gets. But I’ve seen some that use most of the breadboard and have wires crossing everywhere, and in those cases a tool that helps map out the design of the circuit may be useful. I haven’t tried these yet, so I can’t say much else.</p>
<ul>
<li><a href="http://fritzing.org/home/"  target="_blank" rel="noreferrer">Fritzing</a>, <em>electronics made easy</em></li>
<li><a href="https://123d.circuits.io/"  target="_blank" rel="noreferrer">123D Circuits</a>, <em>electronics from beginner to pro</em></li>
<li><a href="https://easyeda.com/"  target="_blank" rel="noreferrer">EasyEDA</a>,* circuit simulation, PCB design, electronic circuit design online*</li>
</ul>

<h3 class="relative group">Tutorials
    <div id="tutorials" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#tutorials" aria-label="Anchor">#</a>
    </span>
    
</h3>
<ul>
<li>Introduction to <a href="https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/robot/breadboard/"  target="_blank" rel="noreferrer">Using a Breadboard</a></li>
<li><a href="https://web.archive.org/web/20160716090534/http://computers.tutsplus.com/tutorials/how-to-use-a-breadboard-and-build-a-led-circuit--mac-54746"  target="_blank" rel="noreferrer">How to Use a Breadboard and Build a LED Circuit</a></li>
</ul>

<h3 class="relative group">gpiocrust (mocking RPi.GPIO)
    <div id="gpiocrust-mocking-rpigpio" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#gpiocrust-mocking-rpigpio" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>I mocked a few functions for this short program, but when I find myself doing something more complicated that uses more of the GPIO library, I’ll check into <a href="https://github.com/zourtney/gpiocrust"  target="_blank" rel="noreferrer">gpiocrust</a>.</p>
<blockquote><p>A concise, pythonic wrapper around the Raspberry Pi’s RPi.GPIO library. An encrusting, if you will. With (almost silent) fallback to mock objects, you can prototype pin I/O locally on your favorite computer, even when your Pi is on the other side of town (see Mock API for more details). gpiocrust is fully compatible with Python 2 and Python 3.</p>
</blockquote>
<h3 class="relative group">RPi.GPIO Module
    <div id="rpigpio-module" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#rpigpio-module" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The <a href="https://pypi.python.org/pypi/RPi.GPIO"  target="_blank" rel="noreferrer">RPi.GPIO</a> module makes it easier to access the GPIO pins. I used it a little bit here. If you’re interested, here’s some <a href="https://sourceforge.net/p/raspberry-gpio-python/wiki/Home/"  target="_blank" rel="noreferrer">documentation</a>.</p>
]]></content:encoded><media:content url="https://grantwinney.com/raspberry-pi-morse-code-transmitter/feature.webp" medium="image" type="image/webp"/></item><item><title>Hello World for the Raspberry Pi - Making an LED Blink</title><link>https://grantwinney.com/raspberry-pi-making-an-led-blink/</link><pubDate>Sat, 26 Mar 2016 16:37:30 +0000</pubDate><guid>https://grantwinney.com/raspberry-pi-making-an-led-blink/</guid><description>I unboxed my Raspberry Pi a few weeks ago and started learning Python. Let&amp;rsquo;s code the &amp;ldquo;Hello World&amp;rdquo; of the Pi, and make an LED blink.</description><content:encoded><![CDATA[<p>I finally unboxed my Pi a few weeks ago, and since then I’ve been learning some Python - the primary language of the Pi.</p>
<p>You can do fun things with it out-of-the-box, like running and modifying the Python games that install with Raspbian (as well as writing your own), or playing around with <a href="https://scratch.mit.edu/"  target="_blank" rel="noreferrer">MIT’s Scratch program</a> (which also comes preinstalled). Or you could try another OS, like the <a href="http://www.htpcbeginner.com/install-openelec-on-raspberry-pi-linux/"  target="_blank" rel="noreferrer">OpenElec media platform</a> that turns your Pi into a photo gallery / movie streamer (something a few of us were playing around with at the a recent user group).</p>
<p>But a whole new world opens up when you start experimenting with the GPIO (General Purpose Input/Output) pins. By turning them on or off, and sending (or receiving) signals through them, you can interact with other devices and react to the surrounding environment.</p>

<h2 class="relative group">A Brave New World (GPIO and Sensors)
    <div id="a-brave-new-world-gpio-and-sensors" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#a-brave-new-world-gpio-and-sensors" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>One way to use the GPIO pins is with a piece of hardware called a <a href="https://www.raspberrypi.org/blog/introducing-raspberry-pi-hats/"  target="_blank" rel="noreferrer">HAT</a>, which sits on top of the Pi and plugs directly into the pins, adding a new set of functionality. For example, the <a href="https://www.raspberrypi.org/products/sense-hat/"  target="_blank" rel="noreferrer">Sense HAT</a> provides multiple sensors and a grid of RGB LEDs (they can be set to any color), and is even being used on the ISS by ESA astronaut <a href="https://twitter.com/astro_timpeake"  target="_blank" rel="noreferrer">Tim Peake</a> as part the <a href="https://astro-pi.org/"  target="_blank" rel="noreferrer">Astro Pi</a> competition.</p>
<p>A second option is to buy a kit with random peripherals that you can connect to, and signal through, the GPIO pins. Kits vary, but generally include a breadboard, LEDs and resistors at a minimum. Some offer switches, small motors and fans, sensors, and more.</p>
<p>If you&rsquo;re just starting out, look for one of the CanaKit or Vilros &ldquo;complete starter&rdquo; kits on Amazon or similar. They&rsquo;ll run about $75 - $100, but I&rsquo;ve had good luck with them, and they&rsquo;re highly rated, and the kit should come with some extras that help you get started playing around quickly. Go for the Pi 4 unless cost is an issue and you want to save a little money.</p>
<p>The main difference is that the HAT provides an out-of-the-box set of features, whereas the kit takes longer to setup but allows you to customize the configuration. And you don’t have to choose one or the other, as the HAT leaves the GPIO pins accessible.</p>
<p>The kit above cost me about $20. There are less expensive options like the <a href="https://thepihut.com/collections/camjam-edukit/products/camjam-edukit"  target="_blank" rel="noreferrer">CamJam EduKit</a> that runs about $7, which is good for a tight budget, but the kit I got comes with more of everything, as well as a cobbler and ribbon cable.</p>

<h2 class="relative group">The Cobbler
    <div id="the-cobbler" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-cobbler" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Sounds like the villain in a Batman movie…</p>
<p>The cobbler is the black T-shaped device above, sitting on the red padding material. They can come in different shapes and sizes, like <a href="https://learn.adafruit.com/adafruits-raspberry-pi-lesson-4-gpio-setup/the-gpio-connector"  target="_blank" rel="noreferrer">this smaller one for the original Pi</a>. The images below, from the CanaKit product page, show it in more detail. It plugs into the breadboard, bridging all 40 pins from your Pi to the breadboard.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/raspberry-pi-making-an-led-blink/canakit-cable-1.webp"
    width="1742"
      height="876"></figure>
<p>Cobbler and ribbon, connected to the Pi</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/raspberry-pi-making-an-led-blink/canakit-cable-2.jpg"
    width="731"
      height="510"></figure>
<p>Without a cobbler, as with the EduKit set, you’ll have to connect individual wires from the GPIO pins on the Pi to your breadboard. That’s not difficult, and would allow for more flexibility depending on what you’re trying to do, but it could be tedious and an unnecessary step. The cobbler eliminates wires that might get tangled up, and the one I got is clearly labeled, so you can tell at a glance which GPIO pin is mapped where on the breadboard.</p>
<p>Anyway, I got my kit quickly (thank you Amazon Prime!) and pulled it out this weekend to try the “Hello World” of Pi hardware…</p>

<h2 class="relative group">Blinking an LED (Hello World)
    <div id="blinking-an-led-hello-world" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#blinking-an-led-hello-world" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Making an LED blink is simple once you know how, and it’s about the smallest project you can complete that makes your Pi <em>do</em> something in the outside world.</p>
<p>If you don’t have one of those cobbler boards, then you can just follow along with the The PiHut tutorial titled “<a href="https://thepihut.com/blogs/raspberry-pi-tutorials/27968772-turning-on-an-led-with-your-raspberry-pis-gpio-pins"  target="_blank" rel="noreferrer">Turning on an LED with your Raspberry Pi’s GPIO Pins</a>“. It’s short, has good images demonstrating where everything goes, and even includes a nice explanation of what each line of the short Python program is doing at the end. Read through it once or twice and go for it!</p>
<p>In my case, there was no need to make the connections from the Pi to the breadboard, seen as black and orange wires in the tutorial. The cobbler takes care of that. But I did stare at the diagram for a few minutes, trying to envision how to modify it without accidentally frying something.</p>

<h3 class="relative group">Breadboard Setup
    <div id="breadboard-setup" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#breadboard-setup" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Unlike the tutorial, I used pin 21 for the longer (anode) side of the LED <em>(again,</em> <a href="https://thepihut.com/blogs/raspberry-pi-tutorials/27968772-turning-on-an-led-with-your-raspberry-pis-gpio-pins"  target="_blank" rel="noreferrer"><em>read the tutorial first</em></a><em>),</em> then connected the shorter (cathode) side to an empty row. This allowed me to add a resistor to the same row as the cathode side, and then complete the circuit by connecting to one of the “ground” terminals. Hopefully that makes more sense once you take a look at the image below, showing my setup.</p>
<p>Also, if you’re wondering why to even bother with the resistor, I was too. From what little I understand, once the LED lights up it offers no resistance at all, so you’ve basically got a short-circuit. Things may work for awhile, but you could drastically shorten the lives of both the LED and the Pi. From the aforementioned tutorial:</p>
<blockquote><p>You must ALWAYS use resistors to connect LEDs up to the GPIO pins of the Raspberry Pi. The Raspberry Pi can only supply a small current (about 60mA). The LEDs will want to draw more, and if allowed to they will burn out the Raspberry Pi. Therefore putting the resistors in the circuit will ensure that only this small current will flow and the Pi will not be damaged. Resistors are a way of limiting the amount of electricity going through a circuit; specifically, they limit the amount of ‘current’ that is allowed to flow.</p>
</blockquote><p>Ultimately, here’s how I laid it out. The resistor I used was only 220 Ω (the tutorial recommends 330 Ω), but it worked okay.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="breadboard single led circuit"
    src="breadboard-single-led-circuit.jpg"
    ></figure>

<h3 class="relative group">Python Script
    <div id="python-script" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#python-script" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>After you create the circuit, you still need to signal the GPIO pin, in order to turn on the LED.</p>
<p>I wrote a short program (modified from the tutorial), which loops 10 times. Each time, it signals pin 21 once to turn the LED on for a quarter-second, then again to turn it off for a quarter-second. The effect is a light that blinks twice a second for 5 seconds.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">RPi.GPIO</span> <span class="k">as</span> <span class="nn">GPIO</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">time</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="n">pin</span> <span class="o">=</span> <span class="mi">21</span>         <span class="c1"># The pin connected to the LED</span>
</span></span><span class="line"><span class="cl"><span class="n">iterations</span> <span class="o">=</span> <span class="mi">10</span>  <span class="c1"># The number of times to blink</span>
</span></span><span class="line"><span class="cl"><span class="n">interval</span> <span class="o">=</span> <span class="mf">.25</span>   <span class="c1"># The length of time to blink on or off</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="n">GPIO</span><span class="o">.</span><span class="n">setmode</span><span class="p">(</span><span class="n">GPIO</span><span class="o">.</span><span class="n">BCM</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">GPIO</span><span class="o">.</span><span class="n">setwarnings</span><span class="p">(</span><span class="kc">False</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">GPIO</span><span class="o">.</span><span class="n">setup</span><span class="p">(</span><span class="n">pin</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">OUT</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="c1"># The parameters to &#34;range&#34; are inclusive and exclusive, respectively,</span>
</span></span><span class="line"><span class="cl"><span class="c1">#  so to go from 1 to 10 we have to use 1 and 11 (add 1 to the max)</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">iterations</span><span class="o">+</span><span class="mi">1</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="nb">print</span> <span class="s2">&#34;Loop </span><span class="si">%d</span><span class="s2">: LED on&#34;</span> <span class="o">%</span> <span class="p">(</span><span class="n">x</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">output</span><span class="p">(</span><span class="n">pin</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">HIGH</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="n">interval</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="nb">print</span> <span class="s2">&#34;Loop </span><span class="si">%d</span><span class="s2">: LED off&#34;</span> <span class="o">%</span> <span class="p">(</span><span class="n">x</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">output</span><span class="p">(</span><span class="n">pin</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">LOW</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="n">interval</span><span class="p">)</span></span></span></code></pre></div></div>

<h3 class="relative group">Console Output
    <div id="console-output" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#console-output" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Here’s the output that prints to the console while the LED blinks. That last word, “scrot”, is the command for taking a full-screen capture with the Scrot app in Raspbian.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="blinking led output"
    src="/raspberry-pi-making-an-led-blink/blinking-led-output.png"
    width="657"
      height="517"></figure>

<h3 class="relative group">It’s Aliiiiive!
    <div id="its-aliiiiive" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#its-aliiiiive" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Feeling invulnerable, I got crazy and tried <em>two</em> LEDs. Slow down, right? 😏</p>
<p>I just wanted to see it operate both (<a href="https://res.cloudinary.com/dxm4riq52/video/upload/q_auto/v1583296287/Raspberry%20Pi/Blinking_LED_via_the_Raspberry_Pi_2_eyvjjs.mp4"  target="_blank" rel="noreferrer">and you can too</a>), and it did, albeit the green LED seemed a bit dimmer. When I tried to use blue with red or green, the blue didn’t light up. I assume that’s because it uses more power than the resistor was allowing through.</p>
<p>If I’m wrong about that assumption, I’d be interested in hearing the real reason. I assumed all the LEDs were identical other than color, but the blue is brighter than the red or green. And there’s a white LED with four wires that I haven’t tried it yet, but I’d bet it’s brighter than blue.</p>

<h2 class="relative group">Where can I purchase the hardware?
    <div id="where-can-i-purchase-the-hardware" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#where-can-i-purchase-the-hardware" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you want any of the accessory hardware you saw here, including the T-shaped cobbler and cable <em>(it’s very handy to not have to wire up all the individual GPIO pins!),</em> you can pick up a kit on Amazon - I&rsquo;ve found Canakit and Vilros to be good brands, but I&rsquo;m sure there&rsquo;s others too.</p>

<h2 class="relative group">Where can I go from here?
    <div id="where-can-i-go-from-here" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#where-can-i-go-from-here" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Try out these easy projects, to get more familiar with the Raspberry Pi:</p>
<ul>
<li><a href="https://grantwinney.com/raspberry-pi-morse-code-transmitter/"  target="_blank" rel="noreferrer">Building a Morse Code Transmitter on a Raspberry Pi</a></li>
<li><a href="https://grantwinney.com/raspberry-pi-morse-code-transmitter-v2/"  target="_blank" rel="noreferrer">Generating Morse Code on the Raspberry Pi Using a Button on a Breadboard</a></li>
<li><a href="https://grantwinney.com/raspberry-pi-flash-led-for-new-email/"  target="_blank" rel="noreferrer">How to Flash an LED on Your Raspberry Pi When You Get New Email</a></li>
</ul>

<h2 class="relative group">What else did I learn this week?
    <div id="what-else-did-i-learn-this-week" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-else-did-i-learn-this-week" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>More (completely random) things I learned this week.</p>

<h3 class="relative group">Taking Screen Captures in Raspbian
    <div id="taking-screen-captures-in-raspbian" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#taking-screen-captures-in-raspbian" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>If you want to be able to take screen shots on the Pi, you need to install an app called “scrot”:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-none" data-lang="none">sudo apt-get install scrot</code></pre></div>
<p>Then just run “scrot” from the command line and it’ll take a full-screen capture and place it in the current working directory, wherever you happen to be in the command window when you call the command.</p>

<h3 class="relative group">The GPIO Library (aka, don’t reinvent the wheel)
    <div id="the-gpio-library-aka-dont-reinvent-the-wheel" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-gpio-library-aka-dont-reinvent-the-wheel" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>There’s a Python library called Rpi.GPIO that makes it easier to access and manipulate the GPIO pins. Here’s a few useful commands:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">Rpi.GPIO</span>           <span class="c1"># You have to import the library to use the commands in it</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="n">GPIO</span><span class="o">.</span><span class="n">setmode</span><span class="p">(</span><span class="n">GPIO</span><span class="o">.</span><span class="n">BOARD</span><span class="p">)</span>  <span class="c1"># Recommended! &#34;Board&#34; numbering mode, consistent between models</span>
</span></span><span class="line"><span class="cl"><span class="n">GPIO</span><span class="o">.</span><span class="n">setmode</span><span class="p">(</span><span class="n">GPIO</span><span class="o">.</span><span class="n">BCM</span><span class="p">)</span>    <span class="c1"># &#34;Broadcom&#34; numbering mode, could change between models of Pi</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="n">GPIO</span><span class="o">.</span><span class="n">setup</span><span class="p">(</span><span class="mi">13</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">OUT</span><span class="p">)</span>  <span class="c1"># Set the pin direction. In this case, sending signals out.</span>
</span></span><span class="line"><span class="cl"><span class="n">GPIO</span><span class="o">.</span><span class="n">output</span><span class="p">(</span><span class="mi">13</span><span class="p">,</span> <span class="kc">True</span><span class="p">)</span>     <span class="c1"># Turn the pin &#34;on&#34;. Could use: True/False, 1/0, GPIO.HIGH/GPIO.LOW</span></span></span></code></pre></div></div>

<h3 class="relative group">Courses, Demos and Random Notes
    <div id="courses-demos-and-random-notes" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#courses-demos-and-random-notes" aria-label="Anchor">#</a>
    </span>
    
</h3>
<ul>
<li><a href="https://www.coursera.org/learn/raspberry-pi-platform/lecture/vJ5Az/lecture-2-3-demo-of-a-blink"  target="_blank" rel="noreferrer">Demo of an LED blinking</a>, as seen in <a href="https://www.coursera.org/learn/raspberry-pi-platform"  target="_blank" rel="noreferrer">The Raspberry Pi Platform and Python Programming for the Raspberry Pi</a> course on Coursera.</li>
<li><a href="https://www.coursera.org/learn/raspberry-pi-interface"  target="_blank" rel="noreferrer">Interfacing with the Raspberry Pi</a>, another course on Coursera. Now that I’ve got the breadboard, I’ll be trying this out.</li>
<li>I’m also taking <a href="https://www.coursera.org/learn/python/"  target="_blank" rel="noreferrer">Programming for Everybody (Getting Started with Python)</a> on Coursera.</li>
<li><a href="https://www.coursera.org/learn/raspberry-pi-platform/lecture/xyVag/lecture-3-2-tkinter-library"  target="_blank" rel="noreferrer">Tkinter Library</a>, a Python library for GUI development <em>(no clue about this yet, just noting it)</em></li>
</ul>
<p>When programming in Python, there are online Python editors, but personally I just installed the free <a href="https://www.jetbrains.com/pycharm/"  target="_blank" rel="noreferrer">PyCharm IDE</a> from JetBrains and that went well enough.</p>
<p>By using <a href="https://www.raspberrypi.org/documentation/remote-access/vnc/"  target="_blank" rel="noreferrer">VNC (Virtual Network Computing)</a> and following a tutorial on <a href="https://www.modmypi.com/blog/tutorial-how-to-give-your-raspberry-pi-a-static-ip-address"  target="_blank" rel="noreferrer">How to give your Raspberry Pi a Static IP Address</a>, I’m now able to connect to the Pi from other machines. Maybe I’ll write something up on how I did that. It’s convenient to not have* *to plug it into the TV or use a spare mouse/keyboard with it unless I want to.</p>
]]></content:encoded><media:content url="https://grantwinney.com/raspberry-pi-making-an-led-blink/feature.webp" medium="image" type="image/webp"/></item><item><title>Building the model 4-stroke combustion engine from Smithsonian</title><link>https://grantwinney.com/building-a-4-stroke-internal-combustion-engine-with-the-smithsonian-motor-works-model/</link><pubDate>Sat, 06 Feb 2016 11:55:48 +0000</pubDate><guid>https://grantwinney.com/building-a-4-stroke-internal-combustion-engine-with-the-smithsonian-motor-works-model/</guid><description>Last summer, I found this “Smithsonian Motor-Works” set at a garage sale. Once built, it models a 4-stroke internal combustion engine. I’d shelved it for a rainy day and rediscovered it last weekend while cleaning the basement. Time for a little father/son bonding!</description><content:encoded><![CDATA[<p>Garage sales are great for finding random, interesting things to do with the kids, usually for dirt cheap. Last summer, I found this “Smithsonian Motor-Works” set for $5, which I figured was a steal. Once built, it’s supposed to model a 4-stroke internal combustion engine.</p>
<p>I’d shelved it for a rainy day and completely forgotten about it, then rediscovered it last weekend during an overdue cleaning session in the basement. The girls were at a play and the toddler was napping, so it was the perfect time for a little father/son bonding over a cool project.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Smithsonian-Motor-Works-00001"
    src="/building-a-4-stroke-internal-combustion-engine-with-the-smithsonian-motor-works-model/Smithsonian-Motor-Works-00001.jpg"
    width="837"
      height="602"></figure>
<p>The concept is awesome, but the materials are not. I wouldn’t recommend ever buying this at full price. <a href="http://www.amazon.com/NSI-90805-Smithsonian-Motor-Works/dp/B000246MNS"  target="_blank" rel="noreferrer">Amazon ratings are abysmal</a>, mostly because of thin/cheap/missing parts. The Smithsonian is a respected name, but obviously partnered with the wrong manufacturer. I’d be hesitant to buy another similar toy with their label on it.</p>
<p>Enough of that.. if you&rsquo;ve got one and you&rsquo;re feeling intrepid, read on! Hopefully I can help you avoid a few issues. Read through the Amazon reviews too. Plenty of helpful suggestions in there before you get started.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Smithsonian-Motor-Works-00003"
    src="/building-a-4-stroke-internal-combustion-engine-with-the-smithsonian-motor-works-model/Smithsonian-Motor-Works-00003.jpg"
    width="712"
      height="426"></figure>
<p>It wasn’t an overly difficult model, and we lucked out. Our set had all the right pieces in the right quantities, not something you should even have to worry about. :p</p>
<p>It was mostly screwing everything together in the right order… lots and <em>lots and</em> <em><strong>lots</strong></em> of tiny screws. The cheap kind with the heads that strip while you’re screwing them in.</p>
<p>My son would start the screws, then I’d finish them up. That worked out well, since it was tough to apply enough force to <em>not</em> strip the head, while applying the minimal force necessary so the plastic didn’t crack (which almost happened a few times). There were about 60 screws in the set, and only 2 left over, so no room for error.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Smithsonian-Motor-Works-00016"
    src="/building-a-4-stroke-internal-combustion-engine-with-the-smithsonian-motor-works-model/Smithsonian-Motor-Works-00016.jpg"
    width="598"
      height="481"></figure>
<p>One of the most common complaints was misaligned holes. It happened to us when we inserted the valve into the cylinder head, seen below on the lower-right. The upper hole didn’t quite line up with the lower, so the valve (purple pin) would stick instead of springing back. The valves need to slide back and forth (open and close) freely.</p>
<p>The plastic is thin and malleable (not really a good thing… cheap “feel”), so applying some pressure to it for a few minutes was enough to bend it back to the right position and make it stay.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Smithsonian-Motor-Works-00008"
    src="/building-a-4-stroke-internal-combustion-engine-with-the-smithsonian-motor-works-model/Smithsonian-Motor-Works-00008.jpg"
    width="845"
      height="749"></figure>
<p>The instructions were decent for the most part, but the diagrams could’ve used some work. Here’s one that shows the camshaft with the cams lined up a certain way. There are indicator lines on the cams to indicate which way they should be installed. That’s helpful.</p>
<p>Unfortunately, half of them aren’t visible in the diagram due to the orientation of the sketch. Not so helpful, as you end up guessing which way half of them should go, and hoping they line up correctly with the valves, so they’ll open and close them.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/building-a-4-stroke-internal-combustion-engine-with-the-smithsonian-motor-works-model/Smithsonian-Motor-Works-00013.jpg"
    width="610"
      height="411"></figure>
<p>Here’s the camshaft, oriented just like the diagram.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/building-a-4-stroke-internal-combustion-engine-with-the-smithsonian-motor-works-model/Smithsonian-Motor-Works-00015.jpg"
    width="1550"
      height="750"></figure>
<p>Here it is again, rotated 180º so you can see the indicators on the other side. Even an inset inside the larger image would’ve helped. Not that it’s a huge deal if you have to fix it, but every time you have to remove the screws you risk stripping them out.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/building-a-4-stroke-internal-combustion-engine-with-the-smithsonian-motor-works-model/Smithsonian-Motor-Works-00014.jpg"
    width="1537"
      height="771"></figure>
<p>For the most part though, it went well. My daughter jumped in when the girls got home. Here we are, attaching the cylinder block to the lower crank case, after assembling the crankshaft and piston assemblies, and then later installing a cover over the timing belt and pulley, attaching the cylinder block to the crank case, and installing a cover over the timing belt.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="Smithsonian-Motor-Works-00007.jpg"
    ></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/building-a-4-stroke-internal-combustion-engine-with-the-smithsonian-motor-works-model/Smithsonian-Motor-Works-00019.jpg"
    width="708"
      height="527"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/building-a-4-stroke-internal-combustion-engine-with-the-smithsonian-motor-works-model/Smithsonian-Motor-Works-00020.jpg"
    width="810"
      height="540"></figure>
<p>Here’s the motor, battery compartment and spark plug lights. I thought the headphone jacks were a nice touch, until we ran into another quality issue.</p>
<p>The plastic shielding on one of the jacks literally fell apart in my hand. It just crumbled. The black part by my hand ended up splitting into several pieces too. I managed to glue it back together and then wrap it in electrical tape. The toy’s not <em>that</em> old, and even if it were left somewhere really cold, <em>nothing else on the toy was remotely brittle</em>, not even the other jack. Sigh.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/building-a-4-stroke-internal-combustion-engine-with-the-smithsonian-motor-works-model/Smithsonian-Motor-Works-00018.jpg"
    width="513"
      height="550"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/building-a-4-stroke-internal-combustion-engine-with-the-smithsonian-motor-works-model/Smithsonian-Motor-Works-00021.jpg"
    width="1000"
      height="608"></figure>
<p>A few hours in, and we were getting pretty close to finishing it up. The manual has you do some final calibration to make sure parts are lined up correctly, so the “spark plugs” will light up when the pistons are in the correct positions.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Smithsonian-Motor-Works-00022"
    src="/building-a-4-stroke-internal-combustion-engine-with-the-smithsonian-motor-works-model/Smithsonian-Motor-Works-00022.jpg"
    width="922"
      height="524"></figure>
<p>I mounted the finished product – engine and battery compartment – to a spare piece of wood for easier transport.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Smithsonian-Motor-Works-00023"
    src="/building-a-4-stroke-internal-combustion-engine-with-the-smithsonian-motor-works-model/Smithsonian-Motor-Works-00023.jpg"
    width="712"
      height="558"></figure>
<p>When my parents came over the next day (I was at work), grandpa took the time to explain to the kids how it all worked, which was awesome. He’s been a car enthusiast for a long time, so I think he enjoyed seeing it and getting to teach his grandson about it too.</p>
<p>There’s a half-dozen pages dedicated to how it all works in the manual, which goes great with the model. You absolutely cannot help reading it without thinking, how in the world did we ever get this to work?! Your car is literally <em>exploding</em> gasoline (in very small amounts) 50x a second!</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Smithsonian-Motor-Works-00026"
    src="/building-a-4-stroke-internal-combustion-engine-with-the-smithsonian-motor-works-model/Smithsonian-Motor-Works-00026.jpg"
    width="920"
      height="679"></figure>
<p>Nearly every day since we made it, one of my kids fires it up and watches it go. Unfortunately, they didn’t include an on/off switch, and they keep forgetting to remove the batteries. You’re supposed to unplug the jacks when you’re done, but since the one crumbled I had to glue it to the side of the battery compartment. We’ve gone through more batteries than usual this week. :)</p>
<p><a href="https://res.cloudinary.com/dxm4riq52/video/upload/q_auto/v1583296635/The_Smithsonian_Motor-Works_Model_Finished_Product_rnnqlm.mp4"  target="_blank" rel="noreferrer">Here’s a short video of the finished product</a>. Sounds like a washing machine, but everything works! You can see the pistons moving up and down, and the spark plugs firing when the pistons are near the top (end of the compression cycle).</p>
<p>Have you ever put anything together like this? Do you know of any better models than what the Smithsonian produced?</p>
<p>You can find all kinds of animations on YouTube about <a href="https://www.youtube.com/results?search_query=4&#43;stroke&#43;engine&amp;page=&amp;utm_source=opensearch"  target="_blank" rel="noreferrer">how a 4-stroke engine works</a>.</p>
]]></content:encoded><media:content url="https://grantwinney.com/building-a-4-stroke-internal-combustion-engine-with-the-smithsonian-motor-works-model/feature.webp" medium="image" type="image/webp"/></item><item><title>HacktoberFest and my first OSS contributions</title><link>https://grantwinney.com/hacktoberfest-and-making-my-first-oss-contributions/</link><pubDate>Thu, 12 Nov 2015 07:27:00 +0000</pubDate><guid>https://grantwinney.com/hacktoberfest-and-making-my-first-oss-contributions/</guid><description>I made my first OSS contributions during HacktoberFest, gaining experience (and swag!) with the help of DigitalOcean and GitHub. :)</description><content:encoded><![CDATA[<p>All last month, DigitalOcean carried out an event called HacktoberFest. In their own words…</p>
<blockquote><p>HacktoberFest is a month-long event encouraging people to contribute to GitHub-hosted open source projects, whether by fixing bugs, creating new features, or updating and writing documentation.</p>
</blockquote><p>More significantly, t-shirts were involved. And stickers.</p>
<p>Developers love them some swag. And we’re pretty much suckers for any kind of prize if it means proudly lording it over our fellow developers for a little while. In hopes of inspiring each other, of course.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="hacktoberfest-tshirt"
    src="/hacktoberfest-and-making-my-first-oss-contributions/hacktoberfest-tshirt.png"
    width="800"
      height="760"></figure>
<p>DigitalOcean didn’t leave people to flounder about looking for projects either. They worked with project owners to compile <a href="https://hacktoberfest.digitalocean.com#projects"  target="_blank" rel="noreferrer">The List</a>, filterable by language, and then those owners could tag issues as “up for grabs” or “jump in”. It made it easier to find things to work on.</p>
<p>Some people in our local Akron/Cleveland community even organized meetups throughout the month, which was great. It gave the rest of us an opportunity to hack (and learn) together.</p>
<p>In the end, I only managed 2 pull requests, but I’m okay with that. At least I got my feet wet, and provided a couple contributions, which after all was the spirit of the thing…</p>
<p>What did I learn?</p>
<ul>
<li><strong>Look for documentation.</strong> My first PR was for someone who knew English, but wasn’t fluent. He opened an issue asking for proof-reading, and seemed genuinely happy with my contribution.</li>
<li><strong>It’s not your baby.</strong> My second PR wasn’t accepted, and the terse comment left on it gave me nothing to improve. But I didn’t grow the project from scratch, laboring hour after hour to cultivate it. In the end, I tried to provide something of value and apparently didn’t.</li>
<li><strong>Look for other lists too</strong>, like <a href="http://up-for-grabs.net/#/"  target="_blank" rel="noreferrer">up-for-grabs.net</a>.</li>
<li><strong>I found cool projects I never knew existed</strong>, like this curated <a href="https://github.com/vhf/free-programming-books"  target="_blank" rel="noreferrer">list of free programming books</a>.</li>
<li><strong>GitHub makes it easy to contribute.</strong> Fork a project, make changes, and complete a PR right from the web. And when your PR is accepted, GitHub will let you know it’s safe to delete your fork, and even give you a button to do it.</li>
</ul>
<p>It got me thinking about all the awesome free software I use (open source or otherwise). While writing this, I visited the page for every WordPress plugin I use and left a thank you comment and positive rating. It’s easy to forget how much time and effort goes into some of the software we use every day!</p>
]]></content:encoded><media:content url="https://grantwinney.com/hacktoberfest-and-making-my-first-oss-contributions/feature.webp" medium="image" type="image/webp"/></item><item><title>Implicit vs Explicit Conversion in C#</title><link>https://grantwinney.com/csharp-implicit-vs-explicit-conversion/</link><pubDate>Wed, 11 Feb 2015 19:42:36 +0000</pubDate><guid>https://grantwinney.com/csharp-implicit-vs-explicit-conversion/</guid><description>We use implicit and explicit conversion in C# all the time, without even realizing it. Let&amp;rsquo;s learn more about them and look at examples of each.</description><content:encoded><![CDATA[<p>If you’ve programmed in C# for awhile, you’ve likely used both implicit and explicit conversion without even realizing it. Let&rsquo;s take a look at examples of each.</p>
<blockquote><p>If you&rsquo;d like to follow along while you read, the code in this article is available on <a href="https://github.com/grantwinney/CSharpDotNetExamples/tree/master/GeneralConcepts/ImplicitExplicitOperators"  target="_blank" rel="noreferrer">GitHub</a>.</p>
</blockquote>
<h2 class="relative group">Implicit Conversion of .NET Data Types
    <div id="implicit-conversion-of-net-data-types" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#implicit-conversion-of-net-data-types" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Consider an example. A <code>Decimal</code> is capable of storing any <code>Int32</code> value, without losing any information about the number the integer represents. So we&rsquo;re allowed to define an integer and then store it in a decimal without the compiler yelling at us. The conversion from integer to decimal is implicit:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">quantity</span> <span class="p">=</span> <span class="m">5</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">decimal</span> <span class="n">amount</span> <span class="p">=</span> <span class="n">quantity</span><span class="p">;</span>  <span class="c1">// no problemo</span></span></span></code></pre></div></div>
<p>What if we go the opposite direction though?</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">decimal</span> <span class="n">amount</span> <span class="p">=</span> <span class="m">5</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">quantity</span> <span class="p">=</span> <span class="n">amount</span><span class="p">;</span>  <span class="c1">// compiler: &#34;Cannot implicitly convert type &#39;decimal&#39; to &#39;int&#39;&#34;</span></span></span></code></pre></div></div>
<p>This is disallowed, because we run the risk of losing information about the <code>Decimal</code>, which can store a fraction as well as a much larger value than <code>Int32</code>. The compiler is saving us from ourselves, since we may not realize we’re potentially losing data.</p>
<p>Even though we can’t implicitly convert a <code>Decimal</code> to an <code>Int32</code>, we can still <em>explicitly</em> convert the values:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">decimal</span> <span class="n">quantity</span> <span class="p">=</span> <span class="m">5</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">quantity2</span> <span class="p">=</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">quantity</span><span class="p">;</span>  <span class="c1">// whatever, cast away!</span></span></span></code></pre></div></div>
<p>The <code>(int)</code> cast here is forcing the conversion, in essence telling the compiler, “Yes, I know the risks, convert it anyway.”</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">decimal</span> <span class="n">quantity</span> <span class="p">=</span> <span class="m">5</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">quantity2</span> <span class="p">=</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">quantity</span><span class="p">;</span> <span class="c1">// whatever, cast away!</span></span></span></code></pre></div></div>
<p>There&rsquo;s a variety of reasons you might need to do this, but the important thing is that you can.</p>

<h2 class="relative group">Implicit Conversion in Our Own Types
    <div id="implicit-conversion-in-our-own-types" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#implicit-conversion-in-our-own-types" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/user-defined-conversion-operators?redirectedfrom=MSDN"  target="_blank" rel="noreferrer">implicit and explicit conversion operators</a> allow us to implement conversiosn in our own types. Here&rsquo;s what Microsoft has to say on the <code>implicit</code> operator (emphasis mine) <em>(note: this verbiage seems to have been removed since the time this article was written, but it&rsquo;s still relevant and important):</em></p>
<blockquote><p>By eliminating unnecessary casts, implicit conversions can improve source code readability. However, because implicit conversions do not require programmers to explicitly cast from one type to the other, care must be taken to prevent unexpected results. <strong>In general, implicit conversion operators should never throw exceptions and never lose information</strong> so that they can be used safely without the programmer’s awareness. <strong>If a conversion operator cannot meet those criteria, it should be marked explicit.</strong></p>
</blockquote><p>In other words, we can use the implicit keyword to hide the exact details of the conversion from others, but don&rsquo;t do something like allowing a decimal to be converted to an integer implicitly and then just silently dropping the fractional portion.</p>
<p>Let&rsquo;s look at some examples.</p>

<h3 class="relative group">Example 1 – Implicitly Convert String to Person
    <div id="example-1--implicitly-convert-string-to-person" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#example-1--implicitly-convert-string-to-person" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Imagine we have a <code>Person</code> class with an <code>implicit</code> operator defined on it:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Person</span><span class="p">(</span><span class="kt">string</span> <span class="n">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">readonly</span> <span class="kt">string</span> <span class="n">_name</span> <span class="p">=</span> <span class="n">name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;summary&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// Implicitly convert a string to a new Person.</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;/summary&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;param name=&#34;name&#34;&gt;&lt;/param&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kd">implicit</span> <span class="kd">operator</span> <span class="n">Person</span><span class="p">(</span><span class="kt">string</span> <span class="n">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">new</span> <span class="n">Person</span><span class="p">(</span><span class="n">name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>We&rsquo;d normally instantiate the class like this:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">Person</span> <span class="n">person</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Person</span><span class="p">(</span><span class="s">&#34;Bob&#34;</span><span class="p">);</span></span></span></code></pre></div></div>
<p>But thanks to the <code>implicit</code> conversion defined, we could also do this:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">Person</span> <span class="n">person</span> <span class="p">=</span> <span class="s">&#34;Mary&#34;</span><span class="p">;</span></span></span></code></pre></div></div>
<p>That gives us a new <code>Person</code> with the name set to &ldquo;Mary&rdquo;.</p>

<h3 class="relative group">Example 2 – Implicitly Convert DateTime to Birthday
    <div id="example-2--implicitly-convert-datetime-to-birthday" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#example-2--implicitly-convert-datetime-to-birthday" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Now imagine we have a <code>Birthday</code> class with its own <code>implicit</code> operator:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Birthday</span><span class="p">(</span><span class="n">DateTime</span> <span class="n">birthday</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">readonly</span> <span class="n">DateTime</span> <span class="n">_birthday</span> <span class="p">=</span> <span class="n">birthday</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;summary&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// Implicitly convert a DateTime to a new Birthday.</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;/summary&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;param name=&#34;birthday&#34;&gt;&lt;/param&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kd">implicit</span> <span class="kd">operator</span> <span class="n">Birthday</span><span class="p">(</span><span class="n">DateTime</span> <span class="n">birthday</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">new</span> <span class="n">Birthday</span><span class="p">(</span><span class="n">birthday</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Similar to the <code>Person</code> class, we can just pass it a <code>DateTime</code> and get a new <code>Birthday</code>:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">Birthday</span> <span class="n">birthday</span> <span class="p">=</span> <span class="k">new</span> <span class="n">DateTime</span><span class="p">(</span><span class="m">1970</span><span class="p">,</span> <span class="m">6</span><span class="p">,</span> <span class="m">2</span><span class="p">);</span></span></span></code></pre></div></div>
<p>What if we wanted to convert a <code>Birthday</code> back to a <code>DateTime</code> for some reason? We <em>could</em> define another <code>implicit</code> operator, but doing that might go against the general guidance that implicit conversions shouldn&rsquo;t lose data.</p>
<p>Instead, we could create an <code>explicit</code> operator:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="kd">explicit</span> <span class="kd">operator</span> <span class="n">DateTime</span><span class="p">(</span><span class="n">Birthday</span> <span class="n">birthday</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">birthday</span><span class="p">.</span><span class="n">_birthday</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Using it means that anyone using our code needs to explicitly convert a <code>Birthday</code> to the desired type.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">DateTime</span> <span class="n">birthdate</span> <span class="p">=</span> <span class="p">(</span><span class="n">DateTime</span><span class="p">)</span><span class="n">birthday</span><span class="p">;</span></span></span></code></pre></div></div>
<p>If they don&rsquo;t cast it, the compiler throws an error:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/csharp-implicit-vs-explicit-conversion/implicit-conversion-compiler-error.png"
    width="672"
      height="119"></figure>

<h2 class="relative group">Final Thoughts
    <div id="final-thoughts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#final-thoughts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Being able to define our own implicit and explicit conversions is a powerful tool for .NET developers, but it should be used carefully. In particular, if there&rsquo;s any chance of data loss, use an <code>explicit</code> conversion instead of an <code>implicit</code> one.</p>
<p>One other caveat – there’s no intellisense when using a conversion operator, so any “summary” comment you add above the keyword will go unnoticed and unread by whoever&rsquo;s using your class. Don&rsquo;t depend on someone dipping into your code to see if there&rsquo;s any gotchas or side-effects in your conversions!</p>
<p>If you want to learn more, there&rsquo;s a programming guide from Microsoft titled <a href="https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/types/casting-and-type-conversions"  target="_blank" rel="noreferrer">Casting and Type Conversions</a> that might be interesting.</p>
]]></content:encoded><media:content url="https://grantwinney.com/csharp-implicit-vs-explicit-conversion/feature.webp" medium="image" type="image/webp"/></item><item><title>Obsolete Attribute on a Class is Ignored When an Interface is Involved</title><link>https://grantwinney.com/csharp-obsolete-attribute-on-class-ignored-when-interface-is-involved/</link><pubDate>Wed, 04 Feb 2015 17:49:31 +0000</pubDate><guid>https://grantwinney.com/csharp-obsolete-attribute-on-class-ignored-when-interface-is-involved/</guid><description>The Obsolete attribute on a class is ignored when an interface is involved. It caught me by surprise, but makes sense. Let&amp;rsquo;s see why.</description><content:encoded><![CDATA[<p>While marking some code as <a href="https://msdn.microsoft.com/en-us/library/system.obsoleteattribute%5C%28v=vs.110%5C%29.aspx"  target="_blank" rel="noreferrer">obsolete</a> the other day, it seemed that the attribute was being ignored. As it turns out, there&rsquo;s a reasonable explanation, but it took me by surprise at first.</p>
<blockquote><p>If you&rsquo;d like to follow along with the code yourself, it&rsquo;s available on <a href="https://github.com/grantwinney/BlogCodeSamples/tree/master/Languages/CSharp/ObsoleteAttributeOnInterfaces"  target="_blank" rel="noreferrer">GitHub</a>.</p>
</blockquote>
<h2 class="relative group">Simple Classes and the Obsolete Attribute
    <div id="simple-classes-and-the-obsolete-attribute" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#simple-classes-and-the-obsolete-attribute" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Let&rsquo;s start with a simple class that has a <code>Move()</code> method and the <code>Obsolete</code> attribute on that method:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Dinosaur</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="na">    [Obsolete(&#34;Dinos don&#39;t move anymore&#34;, true)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">Move</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;The dino, uh... remained still.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The <code>error</code> flag is set to <code>true</code>, so it&rsquo;ll throw a compiler error:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/csharp-obsolete-attribute-on-class-ignored-when-interface-is-involved/obsolete-code-error.png"
    width="503"
      height="123"></figure>
<p>Now let&rsquo;s say we have a couple other classes with their own <code>Move()</code> methods, without the <code>Obsolete</code> attribute:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Penguin</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">Move</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;The penguin waddled.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Fish</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">Move</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;The fish swam.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>We can instantiate either of these classes and call the <code>Move()</code> method to our heart&rsquo;s content.</p>

<h2 class="relative group">Interfaces Hide the Obsolete Attribute
    <div id="interfaces-hide-the-obsolete-attribute" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#interfaces-hide-the-obsolete-attribute" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Now that we have several classes with the same method, maybe we want to define an interface that they all implement, perhaps to simplify our code or to support <a href="https://grantwinney.com/what-is-mocking-a-dependency/"  target="_blank" rel="noreferrer">testing</a>. So we create an <code>IAnimal</code> interface that defines a <code>Move()</code> method, and have each of the other classes implement it:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">interface</span> <span class="nc">IAnimal</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">void</span> <span class="n">Move</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Penguin</span> <span class="p">:</span> <span class="n">IAnimal</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">Move</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;The penguin waddled.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Fish</span> <span class="p">:</span> <span class="n">IAnimal</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">Move</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;The fish swam.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Dinosaur</span> <span class="p">:</span> <span class="n">IAnimal</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="na">    [Obsolete(&#34;Dinos don&#39;t move anymore&#34;, true)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">void</span> <span class="n">Move</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;The dino, uh... remained still.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>If we instantiate each of the animals against the new interface, we don&rsquo;t get a compiler error anymore. The <code>Obsolete</code> attribute seems to be getting ignored for dinos.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/csharp-obsolete-attribute-on-class-ignored-when-interface-is-involved/no-obsolete-for-interface.png"
    width="304"
      height="160"></figure>
<p>In case you&rsquo;re wondering, it doesn&rsquo;t result in a runtime error either:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">The penguin waddled.
</span></span><span class="line"><span class="cl">The fish swam.
</span></span><span class="line"><span class="cl">The dino, uh... remained still.</span></span></code></pre></div></div>
<p>After thinking about all this for a bit, what else <em>could</em> it do? Given that a single interface can be implemented by any number of classes, what should happen when one class marks the code obsolete, but the others do not? Should <em>any</em> call to <code>Move()</code> cause an error? Absolutely not&hellip; that would break the other classes that don&rsquo;t have the attribute. And so, <strong>any presence of the attribute on the classes themselves are ignored in favor of the interface</strong>.</p>
<p>In other words, it’s not enough to mark the class itself. If we have interfaces that the class is implementing, its methods may need to be decorated with the attribute too:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">interface</span> <span class="nc">IAnimal</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="na">    [Obsolete(&#34;Animals don&#39;t move anymore, I decided.&#34;, true)]</span>
</span></span><span class="line"><span class="cl">    <span class="k">void</span> <span class="n">Move</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The caveat is that, <strong>if we add the</strong> <em><code>*Obsolete*</code></em> <strong>attribute to the interface, then every class implementing the interface will inherit the attribute too</strong>, regardless of whether each of those classes actually has the attribute set on it.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/csharp-obsolete-attribute-on-class-ignored-when-interface-is-involved/interface-marked-obsolete.png"
    width="498"
      height="212"></figure>

<h2 class="relative group">Final Thoughts
    <div id="final-thoughts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#final-thoughts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>When interfaces are involved, the <code>Obsolete</code> attribute on a class is ignored by the compiler. The correct fix depends on the situation. It may mean adding the attribute to the interface itself, or it may mean changing the method to throw a <code>NotSupportedException</code> or some other appropriate exception.</p>
<p>To learn more, check out the MS docs on <a href="https://learn.microsoft.com/en-us/dotnet/csharp/advanced-topics/reflection-and-attributes/"  target="_blank" rel="noreferrer">attributes</a> and <a href="https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/types/interfaces"  target="_blank" rel="noreferrer">interfaces</a>.</p>
]]></content:encoded><media:content url="https://grantwinney.com/csharp-obsolete-attribute-on-class-ignored-when-interface-is-involved/feature.webp" medium="image" type="image/webp"/></item><item><title>An Extension Method to Pass a Column Name to SqlDataReader.GetFieldValue</title><link>https://grantwinney.com/csharp-extension-method-to-pass-column-name-to-getfieldvalue/</link><pubDate>Thu, 15 Jan 2015 00:45:22 +0000</pubDate><guid>https://grantwinney.com/csharp-extension-method-to-pass-column-name-to-getfieldvalue/</guid><description>Let&amp;rsquo;s combine the SqlDataReader&amp;rsquo;s GetFieldValue and GetOrdinal methods into an extension method that lets us pass a column name and get back a specific type.</description><content:encoded><![CDATA[<p>The <a href="http://msdn.microsoft.com/en-us/library/hh485652%5C%28v=vs.110%5C%29.aspx"  target="_blank" rel="noreferrer">SqlDataReader.GetFieldValue</a> method uses generics to return the value of a column as the requested data type, which is nice, but it also requires us to know and pass the column index instead of just using its name, which is less nice.</p>
<p>Let&rsquo;s see if we can do better with a simple extension method.</p>
<blockquote><p>If you&rsquo;d like to follow along while you read, the code in this article is available on <a href="https://github.com/grantwinney/BlogCodeSamples/tree/master/Languages/CSharp/SqlDataReaderGetFieldValueByName"  target="_blank" rel="noreferrer">GitHub</a>.</p>
</blockquote>
<h2 class="relative group">GetFieldValue Only Accepts a Column Index
    <div id="getfieldvalue-only-accepts-a-column-index" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#getfieldvalue-only-accepts-a-column-index" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The <code>GetFieldValue&lt;T&gt;()</code> method requires us to know and pass the index of the column we&rsquo;re interested in:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">var</span> <span class="n">conn</span> <span class="p">=</span> <span class="k">new</span> <span class="n">SqlConnection</span><span class="p">(</span><span class="s">&#34;yourConnectionString&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">var</span> <span class="n">cmd</span> <span class="p">=</span> <span class="k">new</span> <span class="n">SqlCommand</span><span class="p">(</span><span class="s">&#34;SELECT name, age FROM students&#34;</span><span class="p">,</span> <span class="n">conn</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">conn</span><span class="p">.</span><span class="n">Open</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">var</span> <span class="n">dr</span> <span class="p">=</span> <span class="n">cmd</span><span class="p">.</span><span class="n">ExecuteReader</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">name</span> <span class="p">=</span> <span class="n">dr</span><span class="p">.</span><span class="n">GetFieldValue</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;(</span><span class="m">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">age</span> <span class="p">=</span> <span class="n">dr</span><span class="p">.</span><span class="n">GetFieldValue</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;(</span><span class="m">1</span><span class="p">);</span></span></span></code></pre></div></div>
<p>What happens if the query changes? What if an additional column is added to (or removed from) the beginning, or the existing columns are swapped? It&rsquo;s not hard to imagine causing an <code>InvalidCastException</code> or <code>IndexOutOfRangeException</code>.</p>
<p>On the up side, at least it casts the object to the type we specify before returning it.</p>

<h2 class="relative group">The Indexer Accepts a Column Name
    <div id="the-indexer-accepts-a-column-name" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-indexer-accepts-a-column-name" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>We can also access the column value using an indexer, passing either the index of the column or its name:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">conn</span> <span class="p">=</span> <span class="k">new</span> <span class="n">SqlConnection</span><span class="p">(</span><span class="s">&#34;yourConnectionString&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">cmd</span> <span class="p">=</span> <span class="k">new</span> <span class="n">SqlCommand</span><span class="p">(</span><span class="s">&#34;SELECT name, age FROM students&#34;</span><span class="p">,</span> <span class="n">conn</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="n">conn</span><span class="p">.</span><span class="n">Open</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">dr</span> <span class="p">=</span> <span class="n">cmd</span><span class="p">.</span><span class="n">ExecuteReader</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">name</span> <span class="p">=</span> <span class="n">dr</span><span class="p">[</span><span class="s">&#34;name&#34;</span><span class="p">].</span><span class="n">ToString</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">age</span> <span class="p">=</span> <span class="n">Convert</span><span class="p">.</span><span class="n">ToInt32</span><span class="p">(</span><span class="n">dr</span><span class="p">[</span><span class="s">&#34;age&#34;</span><span class="p">]);</span></span></span></code></pre></div></div>
<p>Internally, this runs pretty much the same code as the above method, except it doesn’t cast to a particular data type, so all you get back is an object that you have to convert yourself.</p>

<h2 class="relative group">An Extension Method to Combine Both
    <div id="an-extension-method-to-combine-both" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#an-extension-method-to-combine-both" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Let&rsquo;s combine both of these methods into a single extension method, so we can both specify the return data type <em>and</em> reference the column name instead of the index:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="k">class</span> <span class="nc">SqlReaderExtensions</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;summary&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// Gets the value of the specified column as a type, given the column name.</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;/summary&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;typeparam name=&#34;T&#34;&gt;The expected type of the column being retrieved.&lt;/typeparam&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;param name=&#34;reader&#34;&gt;The reader from which to retrieve the column.&lt;/param&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;param name=&#34;columnName&#34;&gt;The name of the column to be retrieved.&lt;/param&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;returns&gt;The returned type object.&lt;/returns&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="n">T</span> <span class="n">GetFieldValue</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;(</span><span class="k">this</span> <span class="n">SqlDataReader</span> <span class="n">reader</span><span class="p">,</span> <span class="kt">string</span> <span class="n">columnName</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">reader</span><span class="p">.</span><span class="n">GetFieldValue</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;(</span><span class="n">reader</span><span class="p">.</span><span class="n">GetOrdinal</span><span class="p">(</span><span class="n">columnName</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>And here&rsquo;s how to use it:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">var</span> <span class="n">conn</span> <span class="p">=</span> <span class="k">new</span> <span class="n">SqlConnection</span><span class="p">(</span><span class="s">&#34;yourConnectionString&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">var</span> <span class="n">cmd</span> <span class="p">=</span> <span class="k">new</span> <span class="n">SqlCommand</span><span class="p">(</span><span class="s">&#34;SELECT name, age FROM students&#34;</span><span class="p">,</span> <span class="n">conn</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">conn</span><span class="p">.</span><span class="n">Open</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">var</span> <span class="n">dr</span> <span class="p">=</span> <span class="n">cmd</span><span class="p">.</span><span class="n">ExecuteReader</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">name</span> <span class="p">=</span> <span class="n">dr</span><span class="p">.</span><span class="n">GetFieldValue</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;(</span><span class="s">&#34;name&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">age</span> <span class="p">=</span> <span class="n">dr</span><span class="p">.</span><span class="n">GetFieldValue</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;(</span><span class="s">&#34;age&#34;</span><span class="p">);</span></span></span></code></pre></div></div>
]]></content:encoded><media:content url="https://grantwinney.com/csharp-extension-method-to-pass-column-name-to-getfieldvalue/feature.webp" medium="image" type="image/webp"/></item><item><title>Passing Data Between Forms in WinForms</title><link>https://grantwinney.com/winforms-passing-data-between-two-forms/</link><pubDate>Fri, 12 Dec 2014 07:57:57 +0000</pubDate><guid>https://grantwinney.com/winforms-passing-data-between-two-forms/</guid><description>Passing data between two Forms is very common in WinForms. There&amp;rsquo;s a couple ways to do it, and one&amp;rsquo;s better than the other. Let&amp;rsquo;s take a look.</description><content:encoded><![CDATA[<p>In any but the smallest of WinForms apps, we&rsquo;ll have multiple Forms interacting with one another. And while every app we write will be different, there&rsquo;s really only a couple of ways for two Forms to pass data back and forth.</p>
<p>Let&rsquo;s take a closer look.</p>

<h2 class="relative group">Child Form Pushes Data Back to Parent
    <div id="child-form-pushes-data-back-to-parent" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#child-form-pushes-data-back-to-parent" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>When one Form displays another Form to collect user input, or to display a database record for editing, the changes a user makes need to make it back to the first Form or risk being lost.</p>
<p>One way to pass the data is to push it back to the parent Form from the child Form. <strong>A</strong> <em><code>*Form*</code></em> <strong>is just another class, and in order to call methods or change properties of</strong> <em><strong>any</strong></em> <strong>class, you need to have a reference to an instance of it.</strong> There&rsquo;s some caveats to doing it this way, which I&rsquo;ll point out in a minute.</p>
<p>Let&rsquo;s assume we have a simple app with just two Forms - a parent and a child. Here&rsquo;s the code behind <code>ParentForm</code>:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnGetUserInput_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">childForm</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ChildForm</span><span class="p">(</span><span class="k">this</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">childForm</span><span class="p">.</span><span class="n">ShowDialog</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">SetName</span><span class="p">(</span><span class="kt">string</span> <span class="n">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">lblName</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="n">name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kt">int</span> <span class="n">Age</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">set</span> <span class="p">{</span> <span class="n">lblAge</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="k">value</span><span class="p">.</span><span class="n">ToString</span><span class="p">();</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>It displays <code>ChildForm</code> on a button push, sending it a reference to itself. It also defines a method and a property, made public so the child Form can access them.</p>
<p>Now let&rsquo;s look at the code behind <code>ChildForm</code>:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">readonly</span> <span class="n">ParentForm</span> <span class="n">parentForm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="n">ChildForm</span><span class="p">(</span><span class="n">ParentForm</span> <span class="n">form</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">InitializeComponent</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">parentForm</span> <span class="p">=</span> <span class="n">form</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnSaveInput_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">parentForm</span><span class="p">.</span><span class="n">SetName</span><span class="p">(</span><span class="n">txtName</span><span class="p">.</span><span class="n">Text</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">parentForm</span><span class="p">.</span><span class="n">Age</span> <span class="p">=</span> <span class="kt">int</span><span class="p">.</span><span class="n">TryParse</span><span class="p">(</span><span class="n">txtAge</span><span class="p">.</span><span class="n">Text</span><span class="p">,</span> <span class="k">out</span> <span class="kt">var</span> <span class="n">age</span><span class="p">)</span> <span class="p">?</span> <span class="n">age</span> <span class="p">:</span> <span class="m">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>In the constructor, it accepts the reference from <code>ParentForm</code> and stores it. When the &ldquo;Save Input&rdquo; button is pushed, it uses that reference to call the method and property on <code>ParentForm</code>, passing the data back to it.</p>
<p>So what&rsquo;s so bad about this code?</p>

<h3 class="relative group">Problems With This Approach
    <div id="problems-with-this-approach" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#problems-with-this-approach" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>I see two issues with this code, and the first is reusability. Imagine that next week, we want to use <code>ChildForm</code> from another, new Form. <code>ChildForm</code> will need to be reworked because it currently has a constructor that expects to be passed <code>ParentForm</code>. We&rsquo;ll have to add more code and more complexity to <code>ChildForm</code>, so it&rsquo;s not easily reusable.</p>
<p>The second issue is that <code>ChildForm</code> has knowledge it doesn&rsquo;t need. There is no reason for <code>ChildForm</code> to know about other forms, user controls, class libraries, etc that could potentially use it. In general, a thing being <em>called</em> should know very little (or nothing) about the thing calling it.</p>
<p>Imagine as our app grows, we have two Forms calling <code>ChildForm</code> to get a name and age. One calls a new instance of <code>ChildForm</code>, passing an instance of itself, and has a public <code>EmployeeName</code> property that <code>ChildForm</code> can call:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnGetUserInput_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">childForm</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ChildForm</span><span class="p">(</span><span class="k">this</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">childForm</span><span class="p">.</span><span class="n">ShowDialog</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kt">string</span> <span class="n">EmployeeName</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">set</span> <span class="p">{</span> <span class="n">lblName</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="k">value</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The other does the same, except it has a public <code>SetStudentName()</code> method for <code>ChildForm</code> to call instead:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnGetUserInput_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">childForm</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ChildForm</span><span class="p">(</span><span class="k">this</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">childForm</span><span class="p">.</span><span class="n">ShowDialog</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">SetStudentName</span><span class="p">(</span><span class="kt">string</span> <span class="n">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">lblStudentName</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="n">name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The logic in <code>ChildForm</code> increases in complexity with each new object calling it:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">readonly</span> <span class="n">ParentForm1</span> <span class="n">parentForm1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">readonly</span> <span class="n">ParentForm2</span> <span class="n">parentForm2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="n">ChildForm</span><span class="p">(</span><span class="n">ParentForm1</span> <span class="n">form</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">InitializeComponent</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">parentForm1</span> <span class="p">=</span> <span class="n">form</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="n">ChildForm</span><span class="p">(</span><span class="n">ParentForm2</span> <span class="n">form</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">InitializeComponent</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">parentForm2</span> <span class="p">=</span> <span class="n">form</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnSaveInput_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">parentForm1</span> <span class="p">!=</span> <span class="kc">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">parentForm1</span><span class="p">.</span><span class="n">EmployeeName</span> <span class="p">=</span> <span class="n">txtName</span><span class="p">.</span><span class="n">Text</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">parentForm2</span> <span class="p">!=</span> <span class="kc">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">parentForm2</span><span class="p">.</span><span class="n">SetStudentName</span><span class="p">(</span><span class="n">txtName</span><span class="p">.</span><span class="n">Text</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Now <code>ChildForm</code> needs to know about two potential callers, as well as which property or method to call, depending on how it was called. We could try to simplify things, perhaps by introducing an <code>IParentForm</code> interface, but no matter what, things are getting ugly.</p>
<p>Let&rsquo;s look at a better way&hellip;</p>

<h2 class="relative group">Parent Form Pulls Data from Child
    <div id="parent-form-pulls-data-from-child" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#parent-form-pulls-data-from-child" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If we make the data available from the second Form, then let the individual callers retrieve as much or as little of the data as they need, then <code>ChildForm</code> doesn&rsquo;t need to change at all, no matter how many other objects are referencing it.</p>
<p>The easiest way to do this is to create public &ldquo;getter&rdquo; methods on <code>ChildForm</code>:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">partial</span> <span class="k">class</span> <span class="nc">DetailForm</span> <span class="p">:</span> <span class="n">Form</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">DetailForm</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">InitializeComponent</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">new</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">=&gt;</span> <span class="n">txtName</span><span class="p">.</span><span class="n">Text</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">int</span> <span class="n">Age</span> <span class="p">=&gt;</span> <span class="kt">int</span><span class="p">.</span><span class="n">TryParse</span><span class="p">(</span><span class="n">txtAge</span><span class="p">.</span><span class="n">Text</span><span class="p">,</span> <span class="k">out</span> <span class="kt">int</span> <span class="n">result</span><span class="p">)</span> <span class="p">?</span> <span class="n">result</span> <span class="p">:</span> <span class="m">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The two fields on <code>ChildForm</code>, to collect a name and age, are both made accessible to other forms, classes, etc that might need them. It doesn&rsquo;t know for sure that a caller <em>will</em> need them, or anything else about potential callers – and that&rsquo;s a good thing.</p>
<p>Here&rsquo;s how one Form might call <code>ChildForm</code>, and it only cares about the name:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnGetUserInput_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">childForm</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ChildForm</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">childForm</span><span class="p">.</span><span class="n">ShowDialog</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">lblEmployeeName</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="n">childForm</span><span class="p">.</span><span class="n">Name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Then another Form calls <code>ChildForm</code>, this time keeping both name <em>and</em> age:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">btnGetUserInput_Click</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">childForm</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ChildForm</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">childForm</span><span class="p">.</span><span class="n">ShowDialog</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">lblStudentName</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="n">childForm</span><span class="p">.</span><span class="n">Name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">lblAge</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="n">childForm</span><span class="p">.</span><span class="n">Age</span><span class="p">.</span><span class="n">ToString</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The <code>ChildForm</code> is left cleaner. It requires no special knowledge of its callers, and has greater reusability and maintainability. The callers can grab as much or as little data as they need, or do nothing at all.</p>

<h2 class="relative group">Final Thoughts
    <div id="final-thoughts" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#final-thoughts" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There&rsquo;s two practical choices for passing data between two Forms:</p>
<ul>
<li>While we&rsquo;re still in Form2, push data back to Form1.</li>
<li>After we return to Form1, pull data from Form2.</li>
</ul>
<p>The second option leads to easier code maintenance and greater usability.</p>
<p>I hope this helped clarify a few things. If it still seems unclear, or you see a possible error somewhere, leave a comment below and we’ll figure it out!</p>
]]></content:encoded><media:content url="https://grantwinney.com/winforms-passing-data-between-two-forms/feature.webp" medium="image" type="image/webp"/></item><item><title>Filtering a ListView in WPF Using a TextBox and CollectionViewSource</title><link>https://grantwinney.com/wpf-filtering-listview-using-textbox-and-collectionviewsource/</link><pubDate>Sun, 07 Dec 2014 09:13:17 +0000</pubDate><guid>https://grantwinney.com/wpf-filtering-listview-using-textbox-and-collectionviewsource/</guid><description>In WPF, a ListView allows for quite a bit of flexibility. Let&amp;rsquo;s take a look at filtering a ListView, using input being typed into a TextBox.</description><content:encoded><![CDATA[<p>I was recently asked to provide a field users could type in, that would filter a ListView with up to a couple hundred names in real-time.</p>
<blockquote><p>If you&rsquo;d like to follow along while you read, the code in this article is available on <a href="https://github.com/grantwinney/BlogCodeSamples/tree/master/Frameworks/WPF/CollectionViewSourceSample"  target="_blank" rel="noreferrer">GitHub</a>.</p>
</blockquote><p>In WinForms, filtering is easy – a few settings on a ComboBox control, set the data source, and off you go. Although WPF is a bit more complex, it provides for more flexibility out of the box like grouping and sorting, although we&rsquo;ll only look at filtering for now.</p>

<h2 class="relative group">Data Binding in a ListView
    <div id="data-binding-in-a-listview" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#data-binding-in-a-listview" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>One of the biggest strengths in WPF is in binding. As developers, we regularly bind to data – collections of numbers and strings, dates and classes. We present these to our users in grids, combo boxes, list views, etc.</p>
<p><em>When we bind a collection to a <code>ListView</code> in WPF, there&rsquo;s another layer between the control and the collection it&rsquo;s binding to, and that&rsquo;s the <code>CollectionView</code>.</em></p>

<h3 class="relative group">The CollectionView Class
    <div id="the-collectionview-class" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-collectionview-class" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Much like a database table vs a view, or a <code>DataTable</code> vs a <code>DataView</code>, <em>the <code>CollectionView</code> allows us to manipulate the <em>presentation</em> of a collection of data without affecting the underlying data.</em> Per <a href="http://msdn.microsoft.com/en-us/library/system.windows.data.collectionview%5C%28v=vs.110%5C%29.aspx"  target="_blank" rel="noreferrer">MSDN</a>:</p>
<blockquote><p>You can think of a collection view as a layer on top of a binding source collection that allows you to navigate and display the collection based on sort, filter, and group queries, all without having to manipulate the underlying source collection itself.</p>
</blockquote><p>But the documentation advises against creating a <code>CollectionView</code> ourselves, so how do we take advantage of its capabilities? From the same source:</p>
<blockquote><p>In WPF applications, all collections have an associated default collection view. Rather than working with the collection directly, the binding engine always accesses the collection through the associated view.</p>
</blockquote><p>We don’t have to create a <code>CollectionView</code> because WPF does it for us. That&rsquo;s convenient! Let&rsquo;s find out how to access and manipulate that default view.</p>

<h3 class="relative group">The CollectionViewSource Class
    <div id="the-collectionviewsource-class" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-collectionviewsource-class" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Similar to how you can access the default view of a <code>DataTable</code> using the (appropriately named) <code>DataTable.DefaultView</code>, we can also access a collection&rsquo;s default view using <code>CollectionViewSource.GetDefaultView()</code>.</p>
<p>From <a href="http://msdn.microsoft.com/en-us/library/system.windows.data.collectionviewsource.getdefaultview%5C%28v=vs.110%5C%29.aspx"  target="_blank" rel="noreferrer">MSDN</a> again, the following restates some of what we just learned, but with an additional important note about how the default binding works for multiple controls <em>(emphasis mine)</em>.</p>
<blockquote><p>All collections have a default CollectionView. WPF always binds to a view rather than a collection. <strong>If you bind directly to a collection, WPF actually binds to the default view for that collection.</strong> This default view is shared by all bindings to the collection, which causes all direct bindings to the collection to share the sort, filter, group, and current item characteristics of the one default view.</p>
</blockquote><p>Once we have a reference to the default view, what can we do with it?</p>
<blockquote><p>Views allow the same data collection to be viewed in different ways, depending on sorting, filtering, or grouping criteria. Every collection has one shared default view, which is used as the actual binding source when a binding specifies a collection as its source.</p>
</blockquote><p>Let&rsquo;s take a look at filtering in action.</p>

<h2 class="relative group">Filtering in a ListView
    <div id="filtering-in-a-listview" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#filtering-in-a-listview" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>We&rsquo;ll start by creating a <code>Pirate</code> class, to eventually bind to a <code>ListView</code>:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Pirate</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Pirate</span><span class="p">(</span><span class="kt">string</span> <span class="n">firstName</span><span class="p">,</span> <span class="kt">string</span> <span class="n">lastName</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">FirstName</span> <span class="p">=</span> <span class="n">firstName</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">LastName</span> <span class="p">=</span> <span class="n">lastName</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">FirstName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="kd">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">LastName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="kd">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">string</span> <span class="n">FullName</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="kt">string</span><span class="p">.</span><span class="n">Format</span><span class="p">(</span><span class="s">&#34;{0} {1}&#34;</span><span class="p">,</span> <span class="n">FirstName</span><span class="p">,</span> <span class="n">LastName</span><span class="p">);</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Then we&rsquo;ll create a <code>ViewModel</code> that create a list of pirates:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">MainWindowViewModel</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">MainWindowViewModel</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Pirates</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Pirate</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                  <span class="p">{</span>
</span></span><span class="line"><span class="cl">                      <span class="k">new</span> <span class="n">Pirate</span><span class="p">(</span><span class="s">&#34;Anne&#34;</span><span class="p">,</span> <span class="s">&#34;Bonny&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                      <span class="k">new</span> <span class="n">Pirate</span><span class="p">(</span><span class="s">&#34;Black&#34;</span><span class="p">,</span> <span class="s">&#34;Bart&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                      <span class="k">new</span> <span class="n">Pirate</span><span class="p">(</span><span class="s">&#34;Hayreddin&#34;</span><span class="p">,</span> <span class="s">&#34;Barbarossa&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                      <span class="k">new</span> <span class="n">Pirate</span><span class="p">(</span><span class="s">&#34;Hector&#34;</span><span class="p">,</span> <span class="s">&#34;Barbossa&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                      <span class="k">new</span> <span class="n">Pirate</span><span class="p">(</span><span class="s">&#34;Henry&#34;</span><span class="p">,</span> <span class="s">&#34;Avery&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                      <span class="k">new</span> <span class="n">Pirate</span><span class="p">(</span><span class="s">&#34;Henry&#34;</span><span class="p">,</span> <span class="s">&#34;Morgan&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                      <span class="k">new</span> <span class="n">Pirate</span><span class="p">(</span><span class="s">&#34;Howell&#34;</span><span class="p">,</span> <span class="s">&#34;Davis&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                      <span class="k">new</span> <span class="n">Pirate</span><span class="p">(</span><span class="s">&#34;William&#34;</span><span class="p">,</span> <span class="s">&#34;Kidd&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                      <span class="k">new</span> <span class="n">Pirate</span><span class="p">(</span><span class="s">&#34;William&#34;</span><span class="p">,</span> <span class="s">&#34;Turner&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                  <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Pirate</span><span class="p">&gt;</span> <span class="n">Pirates</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Pirate</span> <span class="n">SelectedPirate</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>Now we need the XAML, with a <code>ListView</code> and <code>TextBox</code>:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-xaml" data-lang="xaml">&lt;Window x:Class=&#34;CollectionViewSourceSample.MainWindow&#34;
        xmlns=&#34;http://schemas.microsoft.com/winfx/2006/xaml/presentation&#34;
        xmlns:x=&#34;http://schemas.microsoft.com/winfx/2006/xaml&#34;
        xmlns:mc=&#34;http://schemas.openxmlformats.org/markup-compatibility/2006&#34;
        mc:Ignorable=&#34;d&#34;
        xmlns:d=&#34;http://schemas.microsoft.com/expression/blend/2008&#34;
        xmlns:collectionViewSourceSample=&#34;clr-namespace:CollectionViewSourceSample&#34;
        d:DataContext=&#34;{d:DesignInstance collectionViewSourceSample:MainWindowViewModel}&#34;
        Title=&#34;Arrrr Matey&#34; Width=&#34;250&#34; Height=&#34;300&#34; Background=&#34;WhiteSmoke&#34;
        Loaded=&#34;MainWindow_OnLoaded&#34;&gt;
    &lt;StackPanel&gt;
        &lt;TextBox Name=&#34;PiratesFilter&#34;
                 TextChanged=&#34;PiratesFilter_OnTextChanged&#34;
                 Margin=&#34;5&#34; FontSize=&#34;20&#34; /&gt;
 
        &lt;TextBox IsEnabled=&#34;False&#34; Text=&#34;Pirates:&#34;
                 FontSize=&#34;16&#34; BorderThickness=&#34;0&#34; /&gt;
 
        &lt;ListView Name=&#34;PiratesListView&#34;
                  ItemsSource=&#34;{Binding Path=Pirates}&#34;
                  SelectedValue=&#34;{Binding Path=SelectedPirate}&#34;
                  DisplayMemberPath=&#34;FullName&#34;
                  BorderBrush=&#34;LightGray&#34; Margin=&#34;5&#34; /&gt;
    &lt;/StackPanel&gt;
&lt;/Window&gt;</code></pre></div>
<p>Here we&rsquo;re binding the <code>Pirates</code> collection to <code>PiratesListView</code>, and using the <code>TextBox</code> named <code>PiratesFilter</code> to help filter the contents of the list.</p>
<p>Finally, a few lines of code in the code-behind file help us wire up the filtering mechanism. <em>(If there&rsquo;s a way to define this in the XAML too, I&rsquo;d like to hear about it, but this works just fine too.)</em></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">partial</span> <span class="k">class</span> <span class="nc">MainWindow</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">MainWindow</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">InitializeComponent</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">        <span class="n">DataContext</span> <span class="p">=</span> <span class="k">new</span> <span class="n">MainWindowViewModel</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">void</span> <span class="n">MainWindow_OnLoaded</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">RoutedEventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">CollectionViewSource</span><span class="p">.</span><span class="n">GetDefaultView</span><span class="p">(</span><span class="n">PiratesListView</span><span class="p">.</span><span class="n">ItemsSource</span><span class="p">).</span><span class="n">Filter</span> <span class="p">=</span> <span class="n">UserFilter</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="k">void</span> <span class="n">PiratesFilter_OnTextChanged</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">TextChangedEventArgs</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">CollectionViewSource</span><span class="p">.</span><span class="n">GetDefaultView</span><span class="p">(</span><span class="n">PiratesListView</span><span class="p">.</span><span class="n">ItemsSource</span><span class="p">).</span><span class="n">Refresh</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kt">bool</span> <span class="n">UserFilter</span><span class="p">(</span><span class="kt">object</span> <span class="n">item</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">String</span><span class="p">.</span><span class="n">IsNullOrEmpty</span><span class="p">(</span><span class="n">PiratesFilter</span><span class="p">.</span><span class="n">Text</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">pirate</span> <span class="p">=</span> <span class="p">(</span><span class="n">Pirate</span><span class="p">)</span><span class="n">item</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">(</span><span class="n">pirate</span><span class="p">.</span><span class="n">FirstName</span><span class="p">.</span><span class="n">StartsWith</span><span class="p">(</span><span class="n">PiratesFilter</span><span class="p">.</span><span class="n">Text</span><span class="p">,</span> <span class="n">StringComparison</span><span class="p">.</span><span class="n">OrdinalIgnoreCase</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="p">||</span> <span class="n">pirate</span><span class="p">.</span><span class="n">LastName</span><span class="p">.</span><span class="n">StartsWith</span><span class="p">(</span><span class="n">PiratesFilter</span><span class="p">.</span><span class="n">Text</span><span class="p">,</span> <span class="n">StringComparison</span><span class="p">.</span><span class="n">OrdinalIgnoreCase</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>In the <code>OnLoaded</code> event, we&rsquo;ve attached a delegate to the <a href="http://msdn.microsoft.com/en-us/library/system.windows.data.collectionview.filter%5C%28v=vs.110%5C%29.aspx"  target="_blank" rel="noreferrer">Filter</a> property, which runs the <code>UserFilter</code> method against each item in the collection to determine if it should be displayed in the <code>ListView</code>. That&rsquo;s it!</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="arrmatey"
    src="/wpf-filtering-listview-using-textbox-and-collectionviewsource/arrmatey1.gif"
    width="287"
      height="337"></figure>

<h3 class="relative group">Manually Refreshing the View
    <div id="manually-refreshing-the-view" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#manually-refreshing-the-view" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Most of the time, WPF applies the filter automatically for us, without doing anything extra. However, if our code is performing some heavy calculations, we may want to have more control over the timing of when the filter is applied.</p>
<p><strong>It&rsquo;s possible to manually</strong> <a href="http://msdn.microsoft.com/en-us/library/system.windows.data.collectionview.refresh%5C%28v=vs.110%5C%29.aspx"  target="_blank" rel="noreferrer"><strong>refresh</strong></a> <strong>the view, even though most of the time it&rsquo;s unnecessary</strong>:</p>
<blockquote><p>When you set the <a href="https://learn.microsoft.com/en-us/dotnet/api/system.windows.data.collectionview.filter?view=windowsdesktop-8.0"  target="_blank" rel="noreferrer">Filter</a>, <a href="https://learn.microsoft.com/en-us/dotnet/api/system.windows.data.collectionview.sortdescriptions?view=windowsdesktop-8.0"  target="_blank" rel="noreferrer">SortDescriptions</a>, or <a href="https://learn.microsoft.com/en-us/dotnet/api/system.windows.data.collectionview.groupdescriptions?view=windowsdesktop-8.0"  target="_blank" rel="noreferrer">GroupDescriptions</a> property; a refresh occurs. You do not have to call the <a href="https://learn.microsoft.com/en-us/dotnet/api/system.windows.data.collectionview.refresh?view=windowsdesktop-8.0"  target="_blank" rel="noreferrer">Refresh</a> method immediately after you set one of those properties. For information about how to delay automatic refresh, see <a href="https://learn.microsoft.com/en-us/dotnet/api/system.windows.data.collectionview.deferrefresh?view=windowsdesktop-8.0"  target="_blank" rel="noreferrer">DeferRefresh</a>.</p>
</blockquote><p>Ultimately, we&rsquo;re in control of how often the view is refreshed. And now that&rsquo;s <em>really</em> it. 😄</p>
]]></content:encoded><media:content url="https://grantwinney.com/wpf-filtering-listview-using-textbox-and-collectionviewsource/feature.webp" medium="image" type="image/webp"/></item><item><title>Installing Windows 3.1 in VMware Player</title><link>https://grantwinney.com/installing-windows-3-1-in-vmware-player/</link><pubDate>Thu, 24 Oct 2013 22:14:08 +0000</pubDate><guid>https://grantwinney.com/installing-windows-3-1-in-vmware-player/</guid><description>While looking for a copy of Windows 98 on MSDN to install some old software (compatibility mode under Windows 7 didn’t work), I came across Windows 3.11. Installing it was a little tricky though&amp;hellip;</description><content:encoded><![CDATA[<p>Every once in awhile it’s interesting in a nerdy way to check out some legacy technology and… I dunno… reminisce about the old days and how far we’ve come or some crap like that. Like the fact that the pen drive in my pocket is 10x bigger than the largest hard drives 15 years ago.</p>
<p>I decided this week to install Windows 3.1 on a virtual machine. First, <a href="https://grantwinney.com/installing-dos-6-22-in-vmware-player/"  target="_blank" rel="noreferrer">you’ll need to install DOS</a>. When that’s done, power off the virtual machine.</p>

<h2 class="relative group">Requirements
    <div id="requirements" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#requirements" aria-label="Anchor">#</a>
    </span>
    
</h2>
<ul>
<li><del>VMware Player</del> I <em>think</em> this was replaced by <a href="https://www.vmware.com/products/desktop-hypervisor/workstation-and-fusion"  target="_blank" rel="noreferrer">VMware Fusion and Workstation</a></li>
<li><a href="https://grantwinney.com/installing-dos-6-22-in-vmware-player/"  target="_blank" rel="noreferrer">An instance of DOS 6.22 running in a virtual machine</a></li>
<li>Software capable of packaging files into a floppy disk image (may end in FLV, IMA, etc), such as <a href="http://www.winimage.com/download.htm"  target="_blank" rel="noreferrer">WinImage</a><em>(30 day free trial)</em></li>
<li>Software capable of packaging files into an cd (ISO) image, such as <a href="http://www.trustfm.net/divx/SoftwareFolder2Iso.php"  target="_blank" rel="noreferrer">Folder2Iso</a><em>(freeware)</em></li>
<li>A driver to enable CD-ROM capabilities, such as <a href="http://www.computerhope.com/download/hardware.htm"  target="_blank" rel="noreferrer">oakcdrom.sys</a>. <em>(a generic CD-ROM driver that will work with the majority of all IDE CD-ROM drives)</em></li>
<li>A Windows 3.1 image. If you have an MSDN account, you can download a zip file with the installation files inside it, package them into an ISO image using <a href="http://www.trustfm.net/software/utilities/Folder2Iso.php"  target="_blank" rel="noreferrer">Folder2Iso</a> and mount the image.</li>
</ul>

<h2 class="relative group">Installing Windows
    <div id="installing-windows" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#installing-windows" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>DOS doesn’t support CDs right out of the box, so we’ll need a driver. I used oakcdrom.sys, a generic CD-ROM driver. At this point, all we’ve got access to is a floppy drive.</p>
<p>Install <a href="http://www.winimage.com/winimage.htm"  target="_blank" rel="noreferrer">WinImage</a> and package the driver into a floppy disk image.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-001.png"
    width="660"
      height="470"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-002.png"
    width="666"
      height="562"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-003.png"
    width="396"
      height="171"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-004.png"
    width="666"
      height="309"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-005.png"
    width="571"
      height="556"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-006.png"
    width="641"
      height="434"></figure>
<p>Mount the image to your virtual machine and copy the driver into <code>c:\dos</code>.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-007.png"
    width="676"
      height="587"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-009.png"
    width="736"
      height="472"></figure>
<p>You’ll need to adjust two configuration files to use the new driver:</p>
<p>Type <code>edit c:\config.sys</code> and add to the end of that file:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">DeviceHigh=C:\DOS\oakcdrom.sys /D:CD1</span></span></code></pre></div></div>
<p>And then add to the end of <code>C:\AUTOEXEC.BAT</code>:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">LH MSCDEx /D:CD1</span></span></code></pre></div></div>
<p>These drivers extend BIOS and DOS, respectively, to support the CD-ROM drive, and Windows 3.1 inherits that when it runs.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-011.png"
    width="736"
      height="472"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-012.png"
    width="736"
      height="472"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-013.png"
    width="736"
      height="472"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-014.png"
    width="736"
      height="472"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-015.png"
    width="736"
      height="472"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-016.png"
    width="736"
      height="472"></figure>
<p>Reboot the virtual machine to apply your changes and then mount the Windows 3.1 disk.</p>
<ul>
<li>If you’ve already got floppy disk or cd-rom images of Windows, then mount those now.</li>
<li>If you’ve got a zip file with the installation files inside it, like the one I got from MSDN, then you’ll need to run an app (such as <a href="http://www.trustfm.net/software/utilities/Folder2Iso.php"  target="_blank" rel="noreferrer">Folder2Iso</a>) that can package them into a CD ISO, which you can then mount. It’s a straight-forward process.</li>
</ul>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Win 311 Image 010"
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-010.png"
    width="676"
      height="587"></figure>
<p>With the CD-ROM now recognized and the Windows 3.1 disk mounted, you can boot up into DOS and type <code>D:</code> at the prompt to access the disk. Type <code>setup</code> to begin installation. I just accepted all the defaults.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-017.png"
    width="736"
      height="472"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-018.png"
    width="736"
      height="472"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-019.png"
    width="736"
      height="472"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-020.png"
    width="640"
      height="480"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-021.png"
    width="640"
      height="480"></figure>
<p>I opted for all the features, but at what cost? <em>Over</em> <em><strong>2 MB</strong></em> <em>of hard drive space!</em> 😱</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-022.png"
    width="640"
      height="480"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-023.png"
    width="640"
      height="480"></figure>
<p>Some changes to the same files we edited earlier. Windows saves a backup copy before modifying it.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-024.png"
    width="640"
      height="480"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-027.png"
    width="640"
      height="480"></figure>
<p>Wasn’t sure what to do with this. Considered skipping it, but ended up selecting the ‘generic’ printer. Maybe I’ll search for a driver that would allow me to print.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-028.png"
    width="640"
      height="480"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-029.png"
    width="640"
      height="480"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-030.png"
    width="640"
      height="480"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-031.png"
    width="640"
      height="480"></figure>
<p>I <em>had</em> to go through the tutorial. Never too late to learn to use a mouse.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-032.png"
    width="640"
      height="480"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-033.png"
    width="640"
      height="480"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-034.png"
    width="640"
      height="480"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-035.png"
    width="640"
      height="480"></figure>
<p>Mmm… icecream. I’m a little dubious of how they calculate the number of calories in “chocolate sauce” and “nuts”. Weight-watchers app, this is not.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-036.png"
    width="640"
      height="480"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-037.png"
    width="640"
      height="480"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-038.png"
    width="720"
      height="400"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-040.png"
    width="640"
      height="480"></figure>

<h2 class="relative group">What&rsquo;s here?
    <div id="whats-here" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whats-here" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Wow, notice the evolution of Notepad over 20 years. :p</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Win 311 Image 042"
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-042.png"
    width="1338"
      height="480"></figure>
<p>Something I threw together.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Win 311 Image 043"
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-043.png"
    width="640"
      height="480"></figure>
<p>Hmm.. what else can I install on here?</p>

<h3 class="relative group">Visual Basic 2.0
    <div id="visual-basic-20" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#visual-basic-20" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Here’s Visual Basic 2.0. Woah. Okay, that one’s improved in 20 years. Notice that a complete installation will require 18 MB! <em>(gasp)</em></p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-044.png"
    width="640"
      height="480"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-045.png"
    width="640"
      height="480"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-046.png"
    width="640"
      height="480"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-047.png"
    width="640"
      height="480"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-048.png"
    width="640"
      height="480"></figure>

<h3 class="relative group">QBasic 4.5
    <div id="qbasic-45" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#qbasic-45" aria-label="Anchor">#</a>
    </span>
    
</h3>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-049.png"
    width="720"
      height="400"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-050.png"
    width="720"
      height="400"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-051.png"
    width="720"
      height="400"></figure>
<p>I suppose other than finding other apps to install, I’ll try to find a graphics driver, sound driver, etc. Or I’ll get bored after using it for 10 minutes because, come on, the real fun is in getting it to work. Who the heck wants to <em>use</em> it for anything??</p>
<p>You can download this <a href="https://sites.google.com/site/chitchatvmback/misc"  target="_blank" rel="noreferrer">graphics driver</a> and follow the instructions in the zip file; it gets you 256 colors and 1024×768 res in a VMWARE environment. You may also need <a href="http://www.sierrahelp.com/Patches-Updates/Patches-Updates-Misc/Win31SVGAUpdate.html"  target="_blank" rel="noreferrer">these drivers</a>.. not sure.</p>
<p>Installed IE too. Ouch.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-061.png"
    width="1024"
      height="768"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-windows-3-1-in-vmware-player/Win-311-Image-062.png"
    width="1024"
      height="768"></figure>
]]></content:encoded><media:content url="https://grantwinney.com/installing-windows-3-1-in-vmware-player/feature.webp" medium="image" type="image/webp"/></item><item><title>Installing DOS 6.22 in VMware Player</title><link>https://grantwinney.com/installing-dos-6-22-in-vmware-player/</link><pubDate>Wed, 23 Oct 2013 01:26:46 +0000</pubDate><guid>https://grantwinney.com/installing-dos-6-22-in-vmware-player/</guid><description>While looking for a copy of Windows 98 on MSDN to install some old software (compatibility mode under Windows 7 didn’t work), I came across Windows 3.11. I just had to try installing it! But first, DOS 6.22&amp;hellip;</description><content:encoded><![CDATA[<p>I started off looking at my MSDN account for a copy of Windows 98 to install some old software (compatibility mode under Windows 7 didn’t work), and noticed the only versions of Windows available are 7, 8 and….. 3.11. Ooo, I already have Windows 8 running in a virtual machine. Why not fire up Win 3.11?</p>
<p>This was back in the days when Windows ran on top of DOS though, so I needed to install DOS 6.22 first. Should be easy. Oh, but MSDN and TechNet only provide floppy disk images (IMG files) for the <em>upgrade</em> versions of DOS 6.22. They provide other copies of DOS 6.0 and 6.22, but those seem to be the contents of a CD or a file from a hard drive. Not exactly useful, and if you try to mount the floppy disks, you’ll get an error and the setup stops.</p>
<p>First, you’ll need:</p>
<ul>
<li><del>VMware Player</del> I <em>think</em> this was replaced by <a href="https://www.vmware.com/products/desktop-hypervisor/workstation-and-fusion"  target="_blank" rel="noreferrer">VMware Fusion and Workstation</a></li>
<li>A valid copy of the DOS 6.22 floppy disk image files - Available with an MSDN or TechNet account</li>
<li>Also available for free from <a href="http://www.allbootdisks.com/download/iso.html"  target="_blank" rel="noreferrer">AllBootDisks</a>; I can’t vouch for them, but they were linked to on several forums, including Microsoft’s</li>
</ul>
<p>Install “VMware Player” and then unzip the contents of the DOS file to wherever’s convenient.</p>
<p>Here’s how I setup the virtual machine to prepare for DOS. First, create a new machine and decide where to save it:</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-001.png"
    width="442"
      height="401"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-002.png"
    width="442"
      height="401"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-003.png"
    width="442"
      height="401"></figure>
<p>I lowered the disk size from the default 8 GB. I mean, really, <em>1</em> GB was a dream back then.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-004.png"
    width="442"
      height="401"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-005.png"
    width="442"
      height="401"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/enhanced-buzz-1383-1316471212-32.jpg"
    width="600"
      height="678"></figure>
<p>No floppy drive by default. We’ll need one of those to install our flashy new disk operating system. Open the DISKS folder wherever you unzipped DOS, and find the three 144UPGx.IMG files. Load the first one (see the following screen shots), and then “OK” your way out of all the virtual machine setup screens.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-006.png"
    width="669"
      height="569"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-007.png"
    width="669"
      height="569"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-008.png"
    width="669"
      height="569"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-009.png"
    width="669"
      height="569"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-010.png"
    width="442"
      height="401"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-011.png"
    width="687"
      height="619"></figure>
<p>Fire up the virtual machine and it’ll load the floppy disk you mounted and start the DOS setup process. <strong>DON’T PRESS ENTER</strong> after the first screen below!</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-012.png"
    width="720"
      height="400"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-013.png"
    width="720"
      height="400"></figure>
<p>D’oh! Like I mentioned earlier, MSDN and TechNet only provide the upgrade versions of DOS 6.22. Luckily, we can trick it. If you did press enter in the previous screen, just reboot the machine so you get back to the DOS setup screen.</p>
<p>Press F3 multiple times until you exit out to a prompt.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Dos 622 Image 014"
    src="/installing-dos-6-22-in-vmware-player/Image-014.png"
    width="720"
      height="400"></figure>
<p>Now type FDISK to enter the disk format utility. Follow the prompts, accept the defaults and voila .. you have an unformatted c: drive. Thank you to someone in the <a href="http://social.technet.microsoft.com/Forums/windowsserver/en-US/e18f4409-6b2d-437a-b505-7e18db77f608/msdos-622-under-hyperv?forum=winserverhyperv"  target="_blank" rel="noreferrer">TechNet</a> forums.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-015.png"
    width="720"
      height="400"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-016.png"
    width="720"
      height="400"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-017.png"
    width="720"
      height="400"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-018.png"
    width="720"
      height="400"></figure>
<p>Reboot, which will load the DOS setup screen again. F3 your way out, go into FDISK again, and follow the prompts again… and voila, you have a formatted c: drive.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt="Dos 622 Image 019"
    src="/installing-dos-6-22-in-vmware-player/Image-019.png"
    width="720"
      height="400"></figure>
<p>Here’s where we trick DOS into thinking it’s doing an upgrade an not an initial install. Switch to your freshly minted c: drive, create a directory named DOS and copy the contents of your floppy disk image to it.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-020.png"
    width="720"
      height="400"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-021.png"
    width="720"
      height="400"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-022.png"
    width="720"
      height="400"></figure>
<p>Reboot the computer again. Enter setup again. Now the setup program sees the files on the hard drive and assumes you’re upgrading an existing system. Run through the whole installation, mowing right over the files you copied out there in the previous step.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-023.png"
    width="720"
      height="400"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-024.png"
    width="720"
      height="400"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-025.png"
    width="720"
      height="400"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-026.png"
    width="720"
      height="400"></figure>
<p>When prompted, load the other two floppy disk images to complete the installation.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-027.png"
    width="736"
      height="472"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-028.png"
    width="676"
      height="587"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-029.png"
    width="720"
      height="400"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-030.png"
    width="720"
      height="400"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-031.png"
    width="736"
      height="472"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-032.png"
    width="720"
      height="400"></figure>
<p>One final reboot and you’re the envy of all your friends with their smith corona typewriters. Except you can’t actually type a document yet.</p>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-033.png"
    width="720"
      height="400"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-034.png"
    width="720"
      height="400"></figure>
<figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/installing-dos-6-22-in-vmware-player/Image-035.png"
    width="720"
      height="400"></figure>
<p>Ok, now what was I originally trying to install? Oh yeah&hellip; <a href="https://grantwinney.com/installing-windows-3-1-in-vmware-player/"  target="_blank" rel="noreferrer">click here to read about how I installed Windows 3.1 on top of this.</a></p>
]]></content:encoded><media:content url="https://grantwinney.com/installing-dos-6-22-in-vmware-player/feature.webp" medium="image" type="image/webp"/></item><item><title>Hi! I'm Grant.</title><link>https://grantwinney.com/about/</link><pubDate>Sun, 22 Sep 2013 14:15:46 +0000</pubDate><guid>https://grantwinney.com/about/</guid><description/><content:encoded><![CDATA[<p>For over a decade, I&rsquo;ve been developing software in a variety of settings and industries, focusing primarily on C# and the .NET stack, but branching out whenever the job calls for it. I&rsquo;m a constant learner with an appreciation for good docs, DevOps, and the Agile process.</p>
<p>I enjoy a good challenge, producing clear and maintainable code, and sharing what I learn with others. It&rsquo;s a great feeling, learning some new piece of knowledge and then getting to share it and maybe even witness someone&rsquo;s <a href="https://xkcd.com/1053/"  target="_blank" rel="noreferrer">&ldquo;ah ha&rdquo; moment</a>. It&rsquo;s why I do what I do here, and why I appreciate the comments visitors leave!</p>

<h2 class="relative group">Projects
    <div id="projects" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#projects" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I don&rsquo;t have time for a lot of side projects, since each one requires some level of TLC forever, but here&rsquo;s a couple of useful browser addons I created:</p>
<table>
  <thead>
      <tr>
          <th></th>
          <th></th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/about/proj-hce.webp"
    width="60"
      height="60"></figure>
</td>
          <td><a href="/hide-comments-everywhere/" >Hide Comments Everywhere</a><br>Hide comments across the web, including (but not limited to) Disqus, YouTube, news sites and forums, etc.</td>
      </tr>
      <tr>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/about/proj-glfh.webp"
    width="60"
      height="60"></figure>
</td>
          <td><a href="/generate-links-for-headers/" >Generate Links for Headers</a><br>Automatically generates links for all headers on the page, to make it easier to share specific sections of the page.</td>
      </tr>
  </tbody>
</table>

<h2 class="relative group">Career
    <div id="career" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#career" aria-label="Anchor">#</a>
    </span>
    
</h2>
<table>
  <thead>
      <tr>
          <th></th>
          <th></th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/about/logo-progressive.png"
    width="100"
      height="100"></figure>
</td>
          <td><strong>IT Apps Programmer Sr</strong><br><a href="https://www.progressive.com/"  target="_blank" rel="noreferrer">Progressive Insurance</a><br><em>Oct 2025 - Present</em></td>
      </tr>
      <tr>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/about/logo-hdmc.png"
    width="100"
      height="100"></figure>
</td>
          <td><strong>Sr Software Developer</strong><br><a href="https://www.harley-davidson.com"  target="_blank" rel="noreferrer">Harley-Davidson</a><br><em>May 2021 - Oct 2025</em><br><ul><li>Led a team of devs as needed. Previous projects included a parts catalog integration and increasing dealer productivity with a scheduled reporting system and access to more complete, accurate VIN data.</li><li>Participated in all facets of software design, including planning, estimation, architecture/design and development, documentation, and feature demos.</li><li>Strove for collaboration with QAs, BAs, DBAs, etc., using the Agile methodology.</li><li>Pursued opportunities to help other members of the team, sharing knowledge, automating processes through DevOps, and working together to solve issues and come to the best solution.</li><li>Primary technologies and practices included Agile/Scrum, Azure DevOps, React, C#/.NET Core, RESTful APIs, and xUnit / NUnit.</li></ul></td>
      </tr>
      <tr>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/about/logo-codemaze.jpg"
    width="100"
      height="100"></figure>
</td>
          <td><strong>Technical Editor</strong><br><a href="https://code-maze.com"  target="_blank" rel="noreferrer">Code Maze</a><br><em>Jan 2024 - Feb 2025</em><br><ul><li>Worked with authors and senior editors, providing technical editing and proofreading for .NET articles that covered a variety of technologies and topics.</li></ul></td>
      </tr>
      <tr>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/about/logo-beaconhill.jpg"
    width="100"
      height="100"></figure>
</td>
          <td><strong>Sr Software Developer</strong><br><a href="https://www.beaconhillstaffing.com/"  target="_blank" rel="noreferrer">Beacon Hill Staffing Group</a><br><em>Mar 2020 - May 2021</em><br><ul><li>Led a team of developers on a variety of projects, including a new custom coverage initiative, upgrading a parts catalog integration, and a web portal in support of their <a href="https://investor.harley-davidson.com/news/news-details/2021/Harley-Davidson-Launches-H-D1-Marketplace/default.aspx"  target="_blank" rel="noreferrer">Certified Pre-Owned (CPO) program</a>.</li><li>Participated in all facets of software design, including planning, estimation, architecture/design and development, documentation, and feature demos.</li><li>Worked closely with QAs, BAs, DBAs, etc., using the Agile methodology.</li><li>Pursued opportunities to help other members of the team, sharing knowledge, automating processes, and working together to solve issues and come to the best solution.</li><li>Primary technologies and practices included Agile/Scrum, Azure DevOps, React, C#/.NET Core, RESTful APIs, and xUnit / NUnit.</li></ul></td>
      </tr>
      <tr>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/about/logo-vht.jpg"
    width="100"
      height="100"></figure>
</td>
          <td><strong>Software Developer</strong><br><a href="https://www.vhtcx.com/"  target="_blank" rel="noreferrer">Virtual Hold Technology</a><br><em>Oct 2015 - Feb 2020</em><br><ul><li>Developed the next generation of their primary telephony application.</li><li>Collaborated with a team of developers, using pair programming and Agile.</li><li>Communicated with the project manager and other stakeholders.</li><li>Contributed to internal and external documentation and demonstrating new features.</li><li>Primary technologies and practices included Agile/Scrum, TravisCI/Jenkins, GitHub, and any language or tool required for the task at hand (Erlang/OTP, Dialyzer, Ruby and RSpec, C# and NUnit, etc.).</li></ul></td>
      </tr>
      <tr>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/about/logo-partssource.jpg"
    width="100"
      height="100"></figure>
</td>
          <td><strong>Software Developer</strong><br><a href="https://www.partssource.com/"  target="_blank" rel="noreferrer">PartsSource Inc</a><br><em>Dec 2007 - Oct 2015</em><br><ul><li>Developed, maintained, and supported their flagship application, used by 150 employees.</li><li>Significant projects included integrating credit card processing, ensuring PCI compliance, working with the DBA to improve query performance across the app, and conversion of a manual fax process to an online service to save time and improve record-keeping.</li><li>Primary technologies and practices included C#, NUnit (testing), TeamCity (CI), Crystal Reports, and Subversion.</li></ul></td>
      </tr>
      <tr>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/about/logo-progressive.png"
    width="100"
      height="100"></figure>
</td>
          <td><strong>IT Helpdesk Support II / III</strong><br><a href="https://www.progressive.com/"  target="_blank" rel="noreferrer">Progressive Insurance</a><br><em>Feb 2004 - Dec 2007</em><br><ul><li>Worked as part of a team to resolve computer-related issues for Progressive&rsquo;s 30,000 employees.</li><li> Acted as a coach and escalation point for other helpdesk support reps as needed.</li><li>Maintained troubleshooting documentation and determined escalation steps, developing communication and critical listening skills.</li></ul></td>
      </tr>
  </tbody>
</table>

<h2 class="relative group">Education
    <div id="education" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#education" aria-label="Anchor">#</a>
    </span>
    
</h2>
<table>
  <thead>
      <tr>
          <th></th>
          <th></th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/about/logo-franklin2.webp"
    width="200"
      height="200"></figure>
</td>
          <td><strong>Bachelor&rsquo;s Degree, Computer Science</strong><br><a href="https://www.franklin.edu/"  target="_blank" rel="noreferrer">Franklin University</a><br><br>While holding a full-time job and starting a family, I gradually worked towards my degree, earning it Summa Cum Laude with a 4.0 GPA.</td>
      </tr>
  </tbody>
</table>

<h2 class="relative group">Volunteering
    <div id="volunteering" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#volunteering" aria-label="Anchor">#</a>
    </span>
    
</h2>
<table>
  <thead>
      <tr>
          <th></th>
          <th></th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/about/logo-oas.jpg"
    width="100"
      height="100"></figure>
</td>
          <td><strong>Judge</strong> at <a href="http://www.believeinohio.org/"  target="_blank" rel="noreferrer">Believe in Ohio</a> (The Ohio Academy of Science)<br><em>2015, 2016, 2020</em><br><br>Participated as a judge in the annual student STEM entrepreneurship program, assessing commercialization and business plans in the regional and state final competitions.</td>
      </tr>
      <tr>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/about/logo-exercism.svg"
    ></figure>
</td>
          <td><strong>Mentor</strong> on <a href="https://exercism.io/about"  target="_blank" rel="noreferrer">Exercism</a><br><em>July - Nov 2018</em><br><br>As a mentor, I was able to help others learn more about C#. I get to meet them where they&rsquo;re at, and encourage them to take the next step&hellip; and in the process, I learned more too.</td>
      </tr>
      <tr>
          <td><figure><img
    class="my-0 rounded-md"
    loading="lazy"
    decoding="async"
    fetchpriority="low"
    alt=""
    src="/about/logo-stackoverflow.webp"
    width="100"
      height="100"></figure>
</td>
          <td><strong>Contributor</strong> for Stack Overflow<br><em>2011 - 2019</em><br><br>I enjoyed helping others by sharing what I&rsquo;d learned. Platforms like Stack Overflow allow developers to help one another through the tough spots, and it was nice to give back once in awhile.</td>
      </tr>
  </tbody>
</table>
]]></content:encoded><media:content url="https://grantwinney.com/about/feature.webp" medium="image" type="image/webp"/></item></channel></rss>