<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Ian Walter</title>
    <description>The latest articles on Forem by Ian Walter (@ianwalter).</description>
    <link>https://forem.com/ianwalter</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F184997%2Faf38f3be-94ad-43d2-a9b0-03a33662ee6e.jpeg</url>
      <title>Forem: Ian Walter</title>
      <link>https://forem.com/ianwalter</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ianwalter"/>
    <language>en</language>
    <item>
      <title>Have you dismissed TailwindCSS?</title>
      <dc:creator>Ian Walter</dc:creator>
      <pubDate>Sun, 19 Apr 2020 01:29:33 +0000</pubDate>
      <link>https://forem.com/ianwalter/have-you-dismissed-tailwindcss-1ihi</link>
      <guid>https://forem.com/ianwalter/have-you-dismissed-tailwindcss-1ihi</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zoUF13B0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ianwalter.dev/content/images/2020/04/Screenshot_2020-04-18-Tailwind-CSS---A-utility-first-CSS-framework-for-rapidly-building-custom-designs-1-.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zoUF13B0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ianwalter.dev/content/images/2020/04/Screenshot_2020-04-18-Tailwind-CSS---A-utility-first-CSS-framework-for-rapidly-building-custom-designs-1-.png" alt="Have you dismissed TailwindCSS?"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Maybe you've heard of &lt;a href="https://tailwindcss.com/"&gt;TailwindCSS&lt;/a&gt; but have dismissed it because you think writing out class names for each CSS rule is too tedious and you don't want to "litter" your HTML with them? Well that's understandable since it "encourages a &lt;a href="https://tailwindcss.com/docs/utility-first"&gt;utility-first&lt;/a&gt; workflow, where designs are initially implemented using only utility classes to avoid premature abstraction", but that's not the end of the story.&lt;/p&gt;

&lt;p&gt;Components can be composed of utility classes using &lt;code&gt;@apply&lt;/code&gt;. So the following button:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-blue-500 text-white font-bold py-2 px-4 rounded"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Click Me
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Can be optimized to use this class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;bg-blue-500&lt;/span&gt; &lt;span class="err"&gt;text-white&lt;/span&gt; &lt;span class="err"&gt;font-bold&lt;/span&gt; &lt;span class="err"&gt;py-2&lt;/span&gt; &lt;span class="err"&gt;px-4&lt;/span&gt; &lt;span class="err"&gt;rounded;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Simplifying it to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Click Me
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Pseudo-classes can't be used with &lt;code&gt;@apply&lt;/code&gt; so if you wanted to use &lt;code&gt;hover:bg-blue-700&lt;/code&gt; with the button class you would simply add a separate pseudo-class for the underlying utility class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.button&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;bg-blue-700;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  So why do this instead of just using CSS rules directly?
&lt;/h3&gt;

&lt;p&gt;Because now you benefit from the convenience of the button component class &lt;strong&gt;and&lt;/strong&gt; you can still use the utility classes to compose other components, style other areas of your site, and easily modify the styles of other elements. The flexibility that utility classes provide is extremely powerful and, because of how it works behind the scenes, the generated CSS is smaller than if you had specified CSS rules directly for all of those things (as long as you're using a tool like &lt;a href="https://github.com/FullHuman/purgecss"&gt;PurgeCSS&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Read more about this on the TailwindCSS site: &lt;strong&gt;&lt;a href="https://tailwindcss.com/docs/extracting-components"&gt;Extracting Components&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Fixing ChunkLoadError</title>
      <dc:creator>Ian Walter</dc:creator>
      <pubDate>Sat, 18 Apr 2020 19:46:02 +0000</pubDate>
      <link>https://forem.com/ianwalter/fixing-chunkloaderror-3791</link>
      <guid>https://forem.com/ianwalter/fixing-chunkloaderror-3791</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1584824486516-0555a07fc511%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1584824486516-0555a07fc511%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" alt="Fixing ChunkLoadError"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I encountered a bug last year that was a bit tricky because it wasn't something that we would ever run into during development but was a legitimate problem in production. I found the bug because I set up Sentry for our client application and it was reporting that there were unhandled exceptions being thrown with the name ChunkLoadError. It would happen consistently in production but rarely, if ever, in the master and stage environments.&lt;/p&gt;

&lt;p&gt;Because of the name, I immediately understood that this was probably being thrown by the client-side Webpack logic which was failing to load a "page" in our application. Pages are split into separate bundles (also known as chunks) so that users only need to download them when they navigate to a route for that individual page.&lt;/p&gt;

&lt;p&gt;At first we thought that the error was caused by mobile users with spotty cell service. The thinking was that a user would try to navigate to a new page, the request would fail because of a weak or interrupted connection, Webpack would throw the error when the request failed, and Sentry would store and report the error when the connection had been re-established. This assumption turned out to be incorrect and lulled us into a false sense of security that the error wasn't one we could fix.&lt;/p&gt;

&lt;p&gt;A little later on we enabled the Release Tracking feature in Sentry which allowed us to correlate exceptions to individual releases and I noticed that the the error reports seemed to cluster around new releases.&lt;/p&gt;

&lt;p&gt;By trying to replicate a simplified production release locally, I was able to figure out that the problem was one we could fix. The error occurred because each chunk's filename contains a hash based on it's content so that if the content changes, the application would be able to retrieve the updated chunk instead of using the old one stored in the browser's cache.&lt;/p&gt;

&lt;p&gt;The manifest of chunk filenames that the application uses is only loaded on a full page load so if a user navigates to the application, we release a new version with updated pages, and, without doing a full page load, the user tries to navigate to one of those pages, the client application will try to request the old chunk filename contained in the manifest. At the time, we weren't using a CDN and instead serving static files directly from the server so requests for the old chunks would fail since they didn't exist within the application server's filesystem.&lt;/p&gt;

&lt;p&gt;Using a CDN prevented the error from being thrown since the old chunks would still be cached for a certain period of time, but it didn't solve the core issue since users that were using our application during a new release would still be using the old client application as long as they kept the browser tab open.&lt;/p&gt;

&lt;p&gt;I solved this issue by adding a try-catch around the navigation and page import logic so that if a page chunk request fails, the app will do a full page request via &lt;code&gt;window.location&lt;/code&gt; instead of just a request for the individual chunk.&lt;/p&gt;

</description>
      <category>clientside</category>
      <category>webpack</category>
    </item>
    <item>
      <title>Check a DNS record value on macOS using the command-line</title>
      <dc:creator>Ian Walter</dc:creator>
      <pubDate>Tue, 10 Mar 2020 13:28:30 +0000</pubDate>
      <link>https://forem.com/ianwalter/check-a-dns-record-value-on-macos-using-the-command-line-2d10</link>
      <guid>https://forem.com/ianwalter/check-a-dns-record-value-on-macos-using-the-command-line-2d10</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--udZcJr_f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1531547629769-f2e504fe4521%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--udZcJr_f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1531547629769-f2e504fe4521%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" alt="Check a DNS record value on macOS using the command-line"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I was manually renewing a SSL certificate with Let's Encrypt using DNS verification and needed to verify that the DNS record had been updated before continuing the process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;host -t TXT &amp;lt;domain&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Make sure to change &lt;code&gt;TXT&lt;/code&gt; to whatever record type you're looking for, e.g. &lt;code&gt;CNAME&lt;/code&gt;, &lt;code&gt;A&lt;/code&gt;, etc.&lt;/p&gt;

</description>
      <category>dns</category>
      <category>letsencrypt</category>
      <category>cli</category>
    </item>
    <item>
      <title>Selenium WebDriver testing on GitHub Actions</title>
      <dc:creator>Ian Walter</dc:creator>
      <pubDate>Sun, 12 Jan 2020 05:33:25 +0000</pubDate>
      <link>https://forem.com/ianwalter/selenium-webdriver-testing-on-github-actions-5ech</link>
      <guid>https://forem.com/ianwalter/selenium-webdriver-testing-on-github-actions-5ech</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Dm1fO4rp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1477949331575-2763034b5fb5%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Dm1fO4rp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1477949331575-2763034b5fb5%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" alt="Selenium WebDriver testing on GitHub Actions"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've found that running WebDriver-based tests using GitHub Actions is relatively inexpensive and pretty easy to set up and maintain. I'm sure others are doing this already but I couldn't find much on this topic and there are a few gotchas, so here's a summary of my experience getting this up and running.&lt;/p&gt;

&lt;p&gt;I am working on a &lt;a href="https://github.com/ianwalter/bff"&gt;testing library called bff&lt;/a&gt; and a &lt;a href="https://ianwalter.dev/running-selenium-webdriver-tests-using-github-actions/github.com/ianwalter/bff-webdriver"&gt;plugin for it called bff-webdriver&lt;/a&gt; that allows you to run &lt;a href="https://developer.mozilla.org/en-US/docs/Web/WebDriver"&gt;WebDriver&lt;/a&gt;-based tests. To continuously test the plugin, I've been using &lt;a href="https://github.com/features/actions"&gt;GitHub Actions&lt;/a&gt; but only running WebDriver test through the plugin's &lt;a href="https://www.browserstack.com"&gt;BrowserStack&lt;/a&gt; integration (shout out to BrowserStack for providing the service for free). When my colleague Jim submitted the first PR, however, the CI job failed. This is because the BrowserStack credentials that are stored as secrets in the repo are not shared (otherwise people would be able to submit a PR to your repo with malicious code that can access/leak those secrets). I decided that it was time to not only test the BrowserStack integration but also run the tests with a local &lt;a href="https://selenium.dev/"&gt;Selenium&lt;/a&gt; server.&lt;/p&gt;

&lt;p&gt;The first thing I needed to do was set up "&lt;a href="https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idservices"&gt;services&lt;/a&gt;" in my workflow file. Services are just Docker images that are run as containers to support your workflow. I first had to set these up for testing a &lt;a href="https://github.com/ianwalter/nrg"&gt;web framework I'm working on called nrg&lt;/a&gt;. It was more straightforward in that case because the requests are unidirectional. The web framework makes requests to the Redis and PostgreSQL services and that's it. With WebDriver, it's bidirectional. The test framework makes requests to the Selenium Hub, the Selenium Hub makes requests to the browser services, and the browsers make requests to the server being tested.&lt;/p&gt;

&lt;p&gt;Set up the Selenium Hub and a Firefox service (&lt;a href="https://github.com/ianwalter/bff-webdriver/blob/master/.github/workflows/ci.yml#L16"&gt;Click here for the whole file&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    services:
      hub:
        image: selenium/hub:3.141.59-gold
      firefox:
        image: selenium/node-firefox:3.141.59-gold
        env:
          HUB_HOST: hub
          HUB_PORT: 4444
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
ci.yml





&lt;p&gt;(You can set up other browsers like Chrome or maybe even Edge and Safari since GitHub Actions can run Windows and macOS VMs but this is beyond the scope of what I needed).&lt;/p&gt;

&lt;p&gt;Next I had to configure a &lt;code&gt;--network-alias&lt;/code&gt; option which is used as the hostname for your test server by the browser(s). I also had to specify an environment variable for the Selenium Hub hostname (it's the same name as the service, &lt;code&gt;hub&lt;/code&gt;) so that the test framework knows where to make the WebDriver requests as well as an environment variable for the network alias so that my tests know where to tell the browser(s) to navigate.&lt;/p&gt;

&lt;p&gt;Set up your server and tests within a container (&lt;a href="https://github.com/ianwalter/bff-webdriver/blob/master/.github/workflows/ci.yml#L10"&gt;Click here for the whole file&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    container:
      image: node:13.6
      options: --network-alias testHost
    env:
      SELENIUM_HUB_HOST: hub
      TEST_HOST: testHost
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
ci.yml





&lt;p&gt;Now in your tests, make requests to &lt;code&gt;http://testHost&lt;/code&gt; and that's it, you should now have a WebDriver testing setup using GitHub Actions. If you're interested in a lighter-weight browser testing setup, checkout my &lt;a href="https://github.com/ianwalter/puppeteer"&gt;Puppeteer GitHub action&lt;/a&gt; which makes testing in a headless Chrome environment easier.&lt;/p&gt;

</description>
      <category>selenium</category>
      <category>webdriver</category>
      <category>testing</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>Spotlight on decision-tree</title>
      <dc:creator>Ian Walter</dc:creator>
      <pubDate>Fri, 25 Oct 2019 19:22:49 +0000</pubDate>
      <link>https://forem.com/ianwalter/spotlight-on-decision-tree-39kf</link>
      <guid>https://forem.com/ianwalter/spotlight-on-decision-tree-39kf</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WFDAD6BD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ianwalter.dev/content/images/2019/10/decision-tree-7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WFDAD6BD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ianwalter.dev/content/images/2019/10/decision-tree-7.jpg" alt="Spotlight on decision-tree"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I created &lt;a href="https://github.com/ianwalter/decision-tree"&gt;decision-tree&lt;/a&gt; because I was creating a sort of online quiz and I wanted an abstraction to keep track of state as the user completed questions. With decision-tree, you can create a tree-like structure in which answers to questions determine which question or result is shown next as the user continues through the quiz. As the path (or branch) is constructed, it is stored so that the application can know how the user arrived at a result or allow them to backtrack if necessary.&lt;/p&gt;

&lt;p&gt;Let’s take a look at an example. First, we need to construct the tree which consists of objects representing questions, options, and results. I apologize in advance for how geeky this example is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const decisionTree = new DecisionTree({
  key: 'attribute',
  title: 'What is your greatest attribute?',
  options: [
    {
      key: 'S',
      label: 'Strength',
      leadsTo: 'proficiency'
    },
    {
      key: 'I',
      label: 'Intelligence',
      leadsTo: 'spells'
    },
    {
      key: 'D',
      label: 'Dexterity',
      leadsTo: 'proficiency'
    },
    {
      key: 'C',
      label: 'Charisma',
      leadsTo: 'bard'
    }
  ],
  children: [
    {
      key: 'spells',
      title: 'What are your preffered type of spells?',
      options: [
        {
          key: 'damage',
          label: 'Damage',
          leadsTo: 'mage'
        },
        {
          key: 'healing',
          label: 'Healing',
          leadsTo: 'cleric'
        }
      ],
      children: [
        {
          key: 'mage',
          title: 'Mage',
          description: `
            You are a powerful mage, hurling fireballs at your foes!
          `
        },
        {
          key: 'cleric',
          title: 'Cleric',
          description: `
            You are a knowledgeable cleric, saving your friends by casting
            spells to heal them.
          `
        }
      ]
    },
    {
      key: 'proficiency',
      title: 'What type of weapon are you most proficient with?',
      options: [
        {
          key: 'swords',
          label: 'Swords',
          leadsTo: dt =&amp;gt; {
            if (dt.state.attribute === 'D') {
              return 'thief'
            } else {
              return 'fighter'
            }
          }
        },
        {
          key: 'bows',
          label: 'Bows',
          leadsTo: 'ranger'
        }
      ],
      children: [
        {
          key: 'fighter',
          title: 'Fighter',
          description: `
            You are a strong fighter, using your sword to cut down those who
            stand in your way!
          `
        },
        {
          key: 'thief',
          title: 'Thief',
          description: `
            You are a dexterous thief, piercing enemies before they even
            know what hit them.
          `
        },
        {
          key: 'ranger',
          title: 'Ranger',
          description: `
            You are a skilled ranger, felling combatants with your arrows.
          `
        }
      ]
    },
    {
      key: 'bard',
      title: 'Bard',
      description: `
        You are a talented bard, inspiring your party with heroic ballads.
      `
    }
  ]
})
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
Our decision-tree instance





&lt;p&gt;Once a decision-tree instance is created, we can navigate it by setting answers to questions and calling &lt;code&gt;next&lt;/code&gt; like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;decisionTree.set('attribute', 'D').next()
const result = decisionTree.set('proficiency', 'swords').next()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
Initial tree navigation





&lt;p&gt;In this example, if the node returned by &lt;code&gt;next&lt;/code&gt; has no &lt;code&gt;leadsTo&lt;/code&gt; property we can assume that we've reached a result. In the navigation above, we would have arrived at the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  key: 'thief',
  title: 'Thief',
  description: `
    You are a dexterous thief, piercing enemies before they even
    know what hit them.
  `
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
Initial result





&lt;p&gt;If we wanted to arrive at a different result, we can call &lt;code&gt;prev&lt;/code&gt; to go back to the previous question and change our answer like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;decisionTree.prev()
const result = decisionTree.set('proficiency', 'bows').next()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
Changing our response to the previous question





&lt;p&gt;Now our result would be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  key: 'ranger',
  title: 'Ranger',
  description: `
    You are a skilled ranger, felling combatants with your arrows.
  `
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
New result





&lt;p&gt;You can see decision-tree in action at the binx health website where it’s being used to power Find My Test, an assessment that helps people determine what kind of sexually-transmitted infection (STI) testing is suited for them: &lt;a href="https://app.mybinxhealth.com/find-my-test"&gt;https://app.mybinxhealth.com/find-my-test&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope this article was helpful in explaining how you would use decision-tree. If you find this utility useful, please &lt;a href="https://github.com/ianwalter/decision-tree"&gt;star it on GitHub&lt;/a&gt; and consider &lt;a href="https://github.com/sponsors/ianwalter"&gt;sponsoring me&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;[&lt;/p&gt;

&lt;p&gt;ianwalter/decision-tree&lt;/p&gt;

&lt;p&gt;A utility for traversing decision trees by selecting options - ianwalter/decision-tree&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dCMT_LfE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.githubassets.com/favicon.ico" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dCMT_LfE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.githubassets.com/favicon.ico" alt="Spotlight on decision-tree"&gt;&lt;/a&gt;ianwalterGitHub&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w1AQ183C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://repository-images.githubusercontent.com/156609988/5fdc1980-f738-11e9-89b0-e27bc5974b7c" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w1AQ183C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://repository-images.githubusercontent.com/156609988/5fdc1980-f738-11e9-89b0-e27bc5974b7c" alt="Spotlight on decision-tree"&gt;&lt;/a&gt;&lt;br&gt;
](&lt;a href="https://github.com/ianwalter/decision-tree"&gt;https://github.com/ianwalter/decision-tree&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>quiz</category>
      <category>tree</category>
    </item>
    <item>
      <title>Spotlight on vuex-reset</title>
      <dc:creator>Ian Walter</dc:creator>
      <pubDate>Wed, 23 Oct 2019 03:10:11 +0000</pubDate>
      <link>https://forem.com/ianwalter/spotlight-on-vuex-reset-4n6k</link>
      <guid>https://forem.com/ianwalter/spotlight-on-vuex-reset-4n6k</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BbMs-waT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ianwalter.dev/content/images/2019/10/vuex-reset-6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BbMs-waT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ianwalter.dev/content/images/2019/10/vuex-reset-6.jpg" alt="Spotlight on vuex-reset"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://vuex.vuejs.org/"&gt;Vuex&lt;/a&gt; makes it easy to manage the app-wide state for a &lt;a href="https://vuejs.org"&gt;Vue.js&lt;/a&gt; application, but there is no built-in way to easily reset that state to it’s initial value. That’s where &lt;a href="https://github.com/ianwalter/vuex-reset"&gt;vuex-reset&lt;/a&gt; comes in. It’s a small Vuex plugin that will allow you to reset a store and/or store module(s) to it’s initial state by calling a special reset mutation.&lt;/p&gt;

&lt;p&gt;At &lt;a href="https://mybinxhealth.com"&gt;binx health&lt;/a&gt; we're always trying to think of ways we can prevent leaking user information. We use vuex-reset to clear user information after it’s not needed anymore, like when a customer has finished placing an order or has logged out. Although that data is not accessible outside of our application, clearing that data provides a secondary safeguard around leaking that information in ways we did not intend.&lt;/p&gt;

&lt;p&gt;Security isn’t the only use-case, though. The utility is generally useful whenever you need to reset state without having to write more code to do so. Let's look at an example in which a form has it's fields bound to a Vuex module's state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Vuex from 'vuex'
import VuexReset from '@ianwalter/vuex-reset'

// Create a Vuex store.
export default new Vuex.Store({
  // Add the vuex-reset plugin to the store.
  plugins: [VuexReset()],
  modules: {
    // The colorScheme module has initial values that we may want to 
    // reset to at some point.
    colorScheme: {
      namespaced: true,
      state: {
        name: 'Default',
        colors: [
          '#ff8080',
          '#95ffa4',
          '#ffe9aa',
          '#91ddff',
          '#c991e1'
        ]
      },
      mutations: {
        // We need to add a dummy reset mutation so we can trigger 
        // the reset when desired.
        reset: () =&amp;gt; {},
        // Here is an example mutation that can be used to update the   
        // colorScheme values.
        save: (state, colorScheme) =&amp;gt; Object.assign(state, colorScheme)
      }
    }
  }
})
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
store.js







&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default {
  data () {
    // Copy the color scheme stored in the Vuex module to the 
    // component's data so it can be used as the default value
    // in the imaginary New Color Scheme form.
    return { colorScheme: this.$store.state.colorScheme }
  },
  methods: {
    save () {
      // Save the new color scheme values from the imaginary New
      // Color Scheme form to the store.
      this.$store.commit('colorScheme/save', this.colorScheme)
    },
    reset () {
      // Reset the color scheme to the initial 'Default' color scheme.
      this.$store.commit('colorScheme/reset')
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
NewColorScheme.vue





&lt;p&gt;When a user first navigates to the imaginary New Color Scheme form in the &lt;code&gt;NewColorScheme.vue&lt;/code&gt; component the default values will be the initial values in the Vuex &lt;code&gt;colorScheme&lt;/code&gt; module. They can then modify the name and colors of the color scheme and click a save button which would save those updated values to the store. If they ever wanted to reset those values to the &lt;code&gt;Default&lt;/code&gt; color scheme, they could hit a reset button which would call the colorScheme &lt;code&gt;reset&lt;/code&gt; mutation and vuex-reset would restore the initial color scheme. Check out a demo (of a different example) here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://vuex-reset.ianwalter.dev"&gt;https://vuex-reset.ianwalter.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope this article was helpful in explaining what vuex-reset is and why you would want to use it. If you find this utility useful, please &lt;a href="https://github.com/ianwalter/vuex-reset"&gt;star it on GitHub&lt;/a&gt; and consider &lt;a href="https://github.com/sponsors/ianwalter"&gt;sponsoring me&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;[&lt;/p&gt;

&lt;p&gt;ianwalter/vuex-reset&lt;/p&gt;

&lt;p&gt;A Vuex plugin that makes restoring initial state to the store simple - ianwalter/vuex-reset&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dCMT_LfE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.githubassets.com/favicon.ico" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dCMT_LfE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.githubassets.com/favicon.ico" alt="Spotlight on vuex-reset"&gt;&lt;/a&gt;ianwalterGitHub&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--b3fd3ppK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://repository-images.githubusercontent.com/153390676/8d618e00-f678-11e9-87c6-796282bef7d9" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--b3fd3ppK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://repository-images.githubusercontent.com/153390676/8d618e00-f678-11e9-87c6-796282bef7d9" alt="Spotlight on vuex-reset"&gt;&lt;/a&gt;&lt;br&gt;
](&lt;a href="https://github.com/ianwalter/vuex-reset"&gt;https://github.com/ianwalter/vuex-reset&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>vue</category>
      <category>vuex</category>
      <category>javascript</category>
      <category>clientside</category>
    </item>
  </channel>
</rss>
