<?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: Daniel Waller (he/him)</title>
    <description>The latest articles on Forem by Daniel Waller (he/him) (@danielw).</description>
    <link>https://forem.com/danielw</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%2F5045%2Fd9d0766e-f6bf-4c0b-b213-2d0236090c12.png</url>
      <title>Forem: Daniel Waller (he/him)</title>
      <link>https://forem.com/danielw</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/danielw"/>
    <language>en</language>
    <item>
      <title>JetBrains Code With Me - Collaborative Coding in IntelliJ</title>
      <dc:creator>Daniel Waller (he/him)</dc:creator>
      <pubDate>Thu, 01 Oct 2020 10:45:00 +0000</pubDate>
      <link>https://forem.com/danielw/jetbrains-code-with-me-collaborative-coding-in-intellij-4n08</link>
      <guid>https://forem.com/danielw/jetbrains-code-with-me-collaborative-coding-in-intellij-4n08</guid>
      <description>&lt;p&gt;Has anyone else completely missed &lt;a href="https://blog.jetbrains.com/blog/2020/09/28/code-with-me-eap/"&gt;JetBrain's early access announcement&lt;/a&gt; for the &lt;a href="https://plugins.jetbrains.com/plugin/14896-code-with-me"&gt;Code With Me&lt;/a&gt; plugin that happened 3 days ago!?&lt;/p&gt;

&lt;p&gt;It is finally happening, collaborative coding in IntelliJ! And it's not even restricted to IntelliJ Ultimate (for now).&lt;/p&gt;

&lt;p&gt;All you need is an IntelliJ 2020.2.x build and an internet connection. Just install the plugin and share a collaboration link.&lt;br&gt;
The other side then has to quickly install a dedicated IntelliJ Client and you're good to go.&lt;/p&gt;

&lt;p&gt;From what I've seen during a quick test run it looks pretty stable and promising. And I'm not sure if I'm interpreting the connection UI correctly...but it seemed that the connection was peer-to-peer, with JetBrain's servers only used for lobby management. That would be a huge plus if true!&lt;/p&gt;

</description>
      <category>todayilearned</category>
      <category>productivity</category>
      <category>technology</category>
    </item>
    <item>
      <title>Show and tell: Show off your VS Code setup :)</title>
      <dc:creator>Daniel Waller (he/him)</dc:creator>
      <pubDate>Thu, 17 Sep 2020 12:56:15 +0000</pubDate>
      <link>https://forem.com/danielw/show-and-tell-show-off-your-vs-code-setup-47be</link>
      <guid>https://forem.com/danielw/show-and-tell-show-off-your-vs-code-setup-47be</guid>
      <description>&lt;p&gt;Hey DEV community 👋&lt;/p&gt;

&lt;p&gt;I primarily use VS Code for working with React/TypeScript and Python.&lt;br&gt;
I could use some help and inspiration for really getting the most out of my VS Code experience!&lt;/p&gt;

&lt;p&gt;I'd love to see your setups :)&lt;br&gt;
From pretty themes and icon sets to your favorite plugins for productivity and ease of use.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>beginners</category>
      <category>vscode</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Cookie Expiry Pitfalls</title>
      <dc:creator>Daniel Waller (he/him)</dc:creator>
      <pubDate>Wed, 24 Jul 2019 14:05:39 +0000</pubDate>
      <link>https://forem.com/danielw/cookie-expiry-pitfalls-2g2b</link>
      <guid>https://forem.com/danielw/cookie-expiry-pitfalls-2g2b</guid>
      <description>&lt;p&gt;I just stumbled upon some browser behavior regarding cookies that seemed very strange to me but turned out to be actually pretty obvious and well documented.&lt;br&gt;
I thought I'd share what I've learned just in case some of you might not have come across this either.&lt;/p&gt;

&lt;h2&gt;
  
  
  Session Cookie Expiry
&lt;/h2&gt;

&lt;p&gt;This one should be pretty straight forward, right? Omitting the &lt;code&gt;Expires&lt;/code&gt; property will result in a cookie that is deleted when a user ends the session, i.e. closes the browser. &lt;br&gt;
Except, as &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie" rel="noopener noreferrer"&gt;MDN will inform you&lt;/a&gt;, if the user has set their browser to restore the previous session when closing.&lt;/p&gt;

&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F4xxeayfb9s1gcce6dqu7.png" 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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F4xxeayfb9s1gcce6dqu7.png" alt="A screenshot of the Firefox settings startpage showing the checked option 'Restore previous session'"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are like me and carry around 100+ open tabs at all times, chances are you'll have this setting activated. &lt;/p&gt;

&lt;p&gt;Chrome also has this feature of course. &lt;/p&gt;

&lt;p&gt;While this is nothing new, it had never manifested itself as a problem for me until now...and it's easy to forget about when conceptualizing cookie use. So be warned.&lt;/p&gt;

&lt;h2&gt;
  
  
  👻 Timezones 👻
&lt;/h2&gt;

&lt;p&gt;So you set an expiry date on your cookie, hm? Would be a shame if you didn't think about the possible discrepancies between your server's timezone and the browser using your site 😈&lt;br&gt;
Again, as &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie" rel="noopener noreferrer"&gt;MDN explains&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When an expiry date is set, the time and date set is relative to the client the cookie is being set on, not the server.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is fairly obvious when you think about it but something that is easily missed when thinking about setting cookies.&lt;/p&gt;




&lt;p&gt;So there you have it. Two small details about setting cookies on server responses that could easily be missed and could lead to data being stored on a client for much longer than you intended.  &lt;/p&gt;

&lt;p&gt;Additionally this can become a fun source of obscure bugs that will manifest themselves with some classic "works on my machine 🤷" behavior in the future.&lt;/p&gt;

&lt;p&gt;Happy coding! :)&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>todayisearched</category>
      <category>beginners</category>
      <category>security</category>
    </item>
    <item>
      <title>EFAIL mitigations</title>
      <dc:creator>Daniel Waller (he/him)</dc:creator>
      <pubDate>Tue, 15 May 2018 21:54:51 +0000</pubDate>
      <link>https://forem.com/danielw/efail-mitigations-5179</link>
      <guid>https://forem.com/danielw/efail-mitigations-5179</guid>
      <description>&lt;p&gt;As you might have heard there is a flaw in many popular email clients that allows an attacker to decrypt PGP or S/MIME encrypted email content.&lt;/p&gt;

&lt;p&gt;Here's the website with all of the info about the attacks: &lt;a href="https://efail.de" rel="noopener noreferrer"&gt;EFAIL&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Let's collect mitigation techniques especially for the clients affected by the direct exfiltration attack (Apple Mail, iOS Mail, and Mozilla Thunderbird)&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Some immediate mitigations I've read about so far:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Turn off OpenPGP and S/MIME encryption in your mail client and use an external tool (eg. &lt;a href="http://keybase.io" rel="noopener noreferrer"&gt;Keybase&lt;/a&gt;) for encryption/decryption by copy-pasting content. &lt;/li&gt;
&lt;li&gt;Disable HTML support. Only show plaintext&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;(For Thunderbird)&lt;/strong&gt; Leave OpenPGP encryption enabled but turn off automatic decryption/verification of messages in Enigmail settings&lt;/li&gt;
&lt;/ul&gt;

&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Ftawh034iq7dhe7wvlmff.png" 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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Ftawh034iq7dhe7wvlmff.png" alt="A screenshot of thunderbird with the 'Enigmail' dropdown menu open and the selection on the menu item 'Automatically Decrypt/Verify Messages'"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;(For Thunderbird)&lt;/strong&gt; Disallow remote content in messages.&lt;/li&gt;
&lt;/ul&gt;

&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fr1ndg1zkk8owsun3sajk.png" 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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fr1ndg1zkk8owsun3sajk.png" alt="A screenshot of thunderbirds preferences with the 'Privacy' tab selected and the mouse cursor hovering over the checkbox 'Allow remote content in messages'"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Note
&lt;/h4&gt;

&lt;p&gt;The last 2 just protect you from stumbling into the attack. You can still be exploited if you accept remote content for that particular mail and/or if you manually decrypt the message.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>security</category>
    </item>
    <item>
      <title>Test organization in Python</title>
      <dc:creator>Daniel Waller (he/him)</dc:creator>
      <pubDate>Sat, 05 May 2018 09:32:43 +0000</pubDate>
      <link>https://forem.com/danielw/test-organization-in-python-14kk</link>
      <guid>https://forem.com/danielw/test-organization-in-python-14kk</guid>
      <description>&lt;p&gt;For quite some time now Python has been my hacking and proof-of-concept language of choice. &lt;/p&gt;

&lt;p&gt;As such, testing in Python never really was an important issue for me because I wouldn't use the code in production or even revisit it most of the time.&lt;/p&gt;

&lt;p&gt;Recently, I have been writing a small flask app in python to calibrate and continuously &lt;a href="https://tutorials-raspberrypi.com/digital-raspberry-pi-scale-weight-sensor-hx711/" rel="noopener noreferrer"&gt;read from an HX711 load cell with a Raspberry Pi Zero W&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since I'm using this code as part of my bachelor thesis I consider it to be 'production' code and most importantly I want it to be easy to refactor and extend.&lt;/p&gt;

&lt;p&gt;So for the first time I had to think about testing in Python.&lt;/p&gt;




&lt;h3&gt;
  
  
  The setup
&lt;/h3&gt;

&lt;p&gt;First things first, my project without tests looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flt1wkbhvmv4lu1oivstj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flt1wkbhvmv4lu1oivstj.png" alt="Output of tree command on the root of the project directory containing my code without tests" width="300" height="143"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, it is a very simple setup with 2 modules &lt;em&gt;backend&lt;/em&gt; and &lt;em&gt;service&lt;/em&gt; and an entry point &lt;code&gt;server.py&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Goal
&lt;/h3&gt;

&lt;p&gt;For easy testing I want to be able to define tests for each module and be able to run them separately, as well as an easy way of running all tests of all modules.&lt;/p&gt;

&lt;h3&gt;
  
  
  The solution
&lt;/h3&gt;

&lt;p&gt;For testing I will use pythons &lt;code&gt;unittest&lt;/code&gt; module. The smallest unit for testing with unittest is the &lt;code&gt;TestCase&lt;/code&gt; which can have 1 or more methods of the form &lt;code&gt;def test_*():&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;To begin, I tried to separate the different responsibilities of my modules into &lt;code&gt;TestCase&lt;/code&gt; classes, eg.:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;unittest&lt;/span&gt;

    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestCalibrationChecks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        methods that test whether all checks on the calibration state of the scale work
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestCalibrationProcess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        methods that test calibration of the scale
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After writing test cases for the modules my project looked like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff3w6i1bwe41q9m0ac4ty.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff3w6i1bwe41q9m0ac4ty.png" alt="Output of tree command on the root of the project directory containing my code with module tests" width="309" height="249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Running a TestCase from the project root looks like this: &lt;code&gt;python -m unittest backend.test.calibration_test.TestCalibrationChecks&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now I wanted to be able to run all tests in each module so I looked around &lt;a href="https://docs.python.org/3/library/unittest.html#grouping-tests" rel="noopener noreferrer"&gt;the docs&lt;/a&gt; and came to unittest's &lt;code&gt;TestSuite&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;TestSuite&lt;/code&gt; is a collection of &lt;em&gt;TestCases&lt;/em&gt; and/or &lt;em&gt;TestSuites&lt;/em&gt; and can be used to group tests together. &lt;/p&gt;

&lt;p&gt;So in the root of my modules I wrote a file &lt;code&gt;tests.py&lt;/code&gt; to combine all TestCases from that module's &lt;code&gt;test&lt;/code&gt; folder into a single suite.&lt;br&gt;
For the backend module it looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;unittest&lt;/span&gt;

    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;backend.test&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TestCalibrationChecks&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;backend.test&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TestCalibrationProcess&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;backend.test&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TestSystemHealthChecks&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_scale_suite&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;scale_test_suite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TestSuite&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TestLoader&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;loadTestsFromTestCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestCalibrationChecks&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TestLoader&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;loadTestsFromTestCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestCalibrationProcess&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TestLoader&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;loadTestsFromTestCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestSystemHealthChecks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TestResult&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;runner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TextTestRunner&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scale_test_suite&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;test_scale_suite&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After writing a file like that for each module my project looked like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F45mhmq0312psy24stqlo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F45mhmq0312psy24stqlo.png" alt="Output of tree command on the root of the project directory containing my code with module tests and tests.py in each module root" width="310" height="273"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I was now able to run tests for each module from the project root with a command like this: &lt;code&gt;python -m unittest backend.tests&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;But what about running all tests from all modules with a single command?&lt;/p&gt;

&lt;p&gt;With a tiny refactor of &lt;code&gt;backend/tests.py&lt;/code&gt; and &lt;code&gt;service/tests.py&lt;/code&gt; I was able to write a &lt;code&gt;tests.py&lt;/code&gt; in the root directory and combine the &lt;em&gt;backend&lt;/em&gt; and &lt;em&gt;service&lt;/em&gt; test suites to a larger &lt;code&gt;TestSuite&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;First I changed the &lt;code&gt;tests.py&lt;/code&gt; in each module so that the &lt;code&gt;TestSuite&lt;/code&gt; isn't created in the function but is an exported variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;unittest&lt;/span&gt;

    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;backend.test&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TestCalibrationChecks&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;backend.test&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TestCalibrationProcess&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;backend.test&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TestSystemHealthChecks&lt;/span&gt;

    &lt;span class="n"&gt;scale_test_suite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TestSuite&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TestLoader&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;loadTestsFromTestCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestCalibrationChecks&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TestLoader&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;loadTestsFromTestCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestCalibrationProcess&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TestLoader&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;loadTestsFromTestCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestSystemHealthChecks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_scale_suite&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TestResult&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;runner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TextTestRunner&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scale_test_suite&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;test_scale_suite&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I was able to define &lt;code&gt;tests.py&lt;/code&gt; in the project root like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;unittest&lt;/span&gt;

    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;backend&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;backend_tests&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;service_tests&lt;/span&gt;

    &lt;span class="n"&gt;complete_test_suite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TestSuite&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="n"&gt;backend_tests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scale_test_suite&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;service_tests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;service_test_suite&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;


    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_all_suites&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TestResult&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;runner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TextTestRunner&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;complete_test_suite&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;run_all_suites&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The finished project now looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fug2kgoosjdk8lsxnrd34.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fug2kgoosjdk8lsxnrd34.png" alt="Output of tree command on the root of the project directory containing my code with module tests, tests.py in each module root, and a global tests.py in the project root" width="313" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And from the project root I am able to run&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;all tests &lt;code&gt;python -m tests&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;backend tests &lt;code&gt;python -m unittest backend.tests&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;service tests &lt;code&gt;python -m unittest service.tests&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Mission Accomplished&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Notes
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;I am aware there is a function &lt;code&gt;unittest.TestLoader().discover()&lt;/code&gt; which will automatically discover all TestCases in a file tree. I chose not to use it because I like to explicitly declare what to run so I can see what to expect and avoid some tests not running because the discoverer missed them for some reason.&lt;/li&gt;
&lt;li&gt;I am not saying this is the right way to organize your tests in Python, it just seems to work very well for me so far. Happy to hear your comments on this :)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>beginners</category>
      <category>python</category>
      <category>testing</category>
    </item>
    <item>
      <title>From Network Response to Algebraic Data Type with Kotlin</title>
      <dc:creator>Daniel Waller (he/him)</dc:creator>
      <pubDate>Mon, 15 Jan 2018 13:10:36 +0000</pubDate>
      <link>https://forem.com/danielw/from-network-response-to-algebraic-data-type-10co</link>
      <guid>https://forem.com/danielw/from-network-response-to-algebraic-data-type-10co</guid>
      <description>&lt;p&gt;Or, what I learned from trying to stop handling HTTP error codes as exceptions. &lt;/p&gt;

&lt;p&gt;The other day I finally understood something that had eluded me for months now: How can we avoid using exceptions for handling HTTP error codes? More specifically, how can we cast them to something different before they are being thrown as exceptions?&lt;br&gt;
Here's what I learned.&lt;/p&gt;



&lt;p&gt;If you're using Kotlin you may have already heard about the concept of Algebraic Data Types (ADTs).&lt;br&gt;
If not, there is a cool &lt;a href="https://medium.com/car2godevs/kotlin-adt-74472319962a" rel="noopener noreferrer"&gt;post&lt;/a&gt; from &lt;a href="https://medium.com/@dmitry.zaicew" rel="noopener noreferrer"&gt;Dmitry Zaytsev&lt;/a&gt; and &lt;a href="http://engineering.pivotal.io/post/algebraic-data-types-in-kotlin/" rel="noopener noreferrer"&gt;another one&lt;/a&gt; by &lt;a href="http://engineering.pivotal.io/authors/mikegehard/" rel="noopener noreferrer"&gt;Mike Gehard&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In short, here's what we need to know about them for this post:&lt;br&gt;
The idea is to wrap a bunch of objects and data classes inside a sealed class which they all inherit from.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;typealias&lt;/span&gt; &lt;span class="nc"&gt;Second&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PowerUp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;FireFlower&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PowerUp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;SuperMushroom&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PowerUp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;SuperStar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PowerUp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then use them very effectively together with Kotlin's &lt;code&gt;when&lt;/code&gt; statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nc"&gt;Mario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;powerUp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PowerUp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="c1"&gt;// thanks to the 'return' a non-exhaustive 'when' will result in a compiler error&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;powerUp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;FireFlower&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;enableFlameThrowing&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;SuperMushroom&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;grow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="c1"&gt;// 'powerUp' is smart cast to 'SuperStar' so we can access duration on it&lt;/span&gt;
        &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;SuperStar&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;startGodMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;powerUp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cool thing here is that the compiler can infer whether a &lt;em&gt;when&lt;/em&gt; expression is exhaustive or not because the type being evaluated is a sealed class.&lt;br&gt;
Sealed classes can only be extended within their own &lt;code&gt;.kt&lt;/code&gt; file so the compiler can guarantee that &lt;code&gt;FireFlower&lt;/code&gt;, &lt;code&gt;SuperMushroom&lt;/code&gt;, and &lt;code&gt;SuperStar&lt;/code&gt; are the only possible values the &lt;code&gt;powerUp&lt;/code&gt; variable can take on.&lt;br&gt;
If we then use &lt;a href="https://kotlinlang.org/docs/reference/control-flow.html#when-expression" rel="noopener noreferrer"&gt;expression&lt;/a&gt; syntax rather than statement syntax for the &lt;em&gt;when&lt;/em&gt;, ie. &lt;code&gt;val result = when(..) {}&lt;/code&gt; or &lt;code&gt;return when(..) {}&lt;/code&gt;, the compiler will treat this as an error and not build your program.&lt;/p&gt;

&lt;p&gt;This can come in handy when you add more objects to your sealed class but forget to implement how to react to them in the &lt;em&gt;when&lt;/em&gt; expression. Instead of your app crashing or entering an undefined state you'll be notified of the error at compile time.&lt;/p&gt;

&lt;p&gt;Another powerful feature, &lt;a href="https://kotlinlang.org/docs/reference/typecasts.html#smart-casts" rel="noopener noreferrer"&gt;smart casting&lt;/a&gt;, can be seen on the last branch. Here &lt;code&gt;powerUp&lt;/code&gt; on the right-hand side of the branch is automatically cast to the type being checked for on the left-hand side of the branch. So we can use SuperStar's &lt;em&gt;duration&lt;/em&gt; property without explicitly writing &lt;code&gt;(powerUp as SuperStar).duration&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now as &lt;a href="https://medium.com/@collinflynn" rel="noopener noreferrer"&gt;Collinn Flynn&lt;/a&gt; &lt;a href="https://medium.com/livefront/tidy-up-your-observable-streams-with-kotlins-sealed-classes-ce7bdce9c270" rel="noopener noreferrer"&gt;explains&lt;/a&gt;, this &lt;em&gt;Algebraic Data Structure&lt;/em&gt; can be really useful for error handling without resorting to exceptions.&lt;/p&gt;
&lt;h2&gt;
  
  
  Error handling for network requests
&lt;/h2&gt;

&lt;p&gt;When we make network requests there are many things that can go wrong and many networking libraries react to any HTTP response code outside of the 200s with an exception. This is weird because in the context of most apps, codes such as 404 (Resource not found), 401 (Unauthorized) or 403 (Forbidden) are actually meaningful to our app's business logic and should be handled accordingly to enhance user experience, eg.:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;redirect to login on 401&lt;/li&gt;
&lt;li&gt;offer premium upgrade for some subscription service on 403&lt;/li&gt;
&lt;li&gt;show cutesy 404 page&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Traditionally we have to catch any exceptions resulting from our network calls and handle them in order to implement these cases. This can make our network calling code messy and tricky to adapt to changing requirements. &lt;/p&gt;

&lt;p&gt;Imagine you have network calling code in many places in your program and have to add a new HTTP code to react to. For example your team now also wants to show a cutesy &lt;em&gt;service unavailable&lt;/em&gt; page on 503. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fgjnbq5i32uv6orh2q5gu.png" 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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fgjnbq5i32uv6orh2q5gu.png" alt="503 status page of the university of oregon. The title says 'Service is unavailable'. The text below it says 'We are working to clear the ducks and get the service available again. Click the 'Service Status' button for more information. Below it there is a picture of a lot of cartoon rubber ducks sitting on and around a server."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This can become a fun source of errors especially when new developers, unfamiliar with the codebase make the change (or even just you 3 months later 😉).&lt;/p&gt;

&lt;p&gt;Also there are some people with opinions about driving control flow through exceptions 👀.&lt;/p&gt;



&lt;p&gt;&lt;em&gt;Context shift. We're now switching from Mario to a simple HackerNews reader&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Using Algebraic Data Types we can avoid this by defining any response that has meaning to our app inside a sealed response class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NetworkResult&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Payload&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;NetworkResult&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;AggregateArticles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;articleIds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Payload&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;SingleArticle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;article&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;HackerNewsItem&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Payload&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HttpError&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;NetworkResult&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;ResourceNotFound&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;HttpError&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;ServiceUnavailable&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;HttpError&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;UnknownError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;HttpError&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can use the when expression at the network call site like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;handleNetworkResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;NetworkResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;Payload&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;handleResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;HttpError&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;handleError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;handleResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;AggregateArticles&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;loadArticleDetails&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;articleIds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;SingleArticle&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;showArticle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// this would probably live in an injected global error handler&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;handleError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;HttpError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;ResourceNotFound&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;show404Screen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;ServiceUnavailable&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;show503Screen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;UnknownError&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;showError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unknown error ${result.code}: ${result.message}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is cool because as we've shown above you can't forget to implement a case now cause you'll get a compiler error on the &lt;code&gt;when&lt;/code&gt; expression where you're missing a branch.&lt;/p&gt;

&lt;p&gt;Great! But there's already loads of articles out there that talk about all this.&lt;br&gt;
How do we actually get from a network layer response object to our sealed class members!?&lt;/p&gt;
&lt;h2&gt;
  
  
  How to integrate with the network layer
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;The following code will be Android specific and assume a Dagger 2 setup&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In Android development we often use &lt;em&gt;OkHttp&lt;/em&gt; + &lt;em&gt;Retrofit&lt;/em&gt; to define our network layer and &lt;em&gt;RxJava&lt;/em&gt; to handle the asynchronous nature of network calls. &lt;br&gt;
A typical Retrofit initialization would look like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;
    &lt;span class="nd"&gt;@Provides&lt;/span&gt;
    &lt;span class="nd"&gt;@Singleton&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;provideRetrofit&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Retrofit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;rxCallAdapter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RxJava2CallAdapterFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createWithScheduler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Schedulers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;io&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Retrofit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://hacker-news.firebaseio.com/v0/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addConverterFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GsonConverterFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gson&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addCallAdapterFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rxCallAdapter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Here we add a call transformer that will enable us to have every network call wrapped in an RxJava Observable type and a converter to automatically parse the network response's JSON to a data type. &lt;br&gt;
A typical API interface defined with Retrofit, and a Service using it could then look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;NewsAggregateApi&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"topstories.json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;loadTopStories&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Single&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nd"&gt;@GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"newstories.json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;loadNewStories&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Single&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NewsAggregateService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;aggregateApi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;NewsAggregateApi&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;loadTopStories&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Single&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aggregateApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadTopStories&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;loadNewStories&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Single&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aggregateApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadNewStories&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything goes smoothly while calling &lt;code&gt;/topstories.json&lt;/code&gt; our Observable will get a List of &lt;code&gt;articleId: Long&lt;/code&gt; but if we encounter any HTTP code not in the 200s the Observables &lt;code&gt;onError&lt;/code&gt; method will be triggered. This is bad because it happens before we can wrap anything in our nice sealed class types.&lt;br&gt;
In an old answer to &lt;a href="https://github.com/square/retrofit/issues/1218" rel="noopener noreferrer"&gt;Retrofit Issue #1218&lt;/a&gt; Jake Wharton has a solution for our problem:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F1r63jq0lqnl140gyd8q2.png" 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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F1r63jq0lqnl140gyd8q2.png" alt="Jake Wharton says "&gt;&lt;/a&gt;, Observable&amp;gt;, or Observable&amp;gt;. For the first version, there's nowhere to hang non-200 response information so it is included in the exception passed to onError. For the latter two, the data is encapsulated in the Response object and can be accessed by calling errorBody().""/&amp;gt;&lt;/p&gt;

&lt;p&gt;So going by that, we can now change our API interface accordingly to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;NewsAggregateApi&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"topstories.json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;loadTopStories&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Single&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nd"&gt;@GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"newstories.json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;loadNewStories&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Single&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And make some changes to our service and our sealed class so that the return type of the service is always a &lt;code&gt;NetworkResult&lt;/code&gt; and we can map our &lt;code&gt;Response&lt;/code&gt; to one of the &lt;code&gt;NetworkResult&lt;/code&gt; subtypes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NewsAggregateService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;aggregateApi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;NewsAggregateApi&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;loadTopStories&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Single&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;NetworkResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;aggregateApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadTopStories&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toAggregateResult&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;loadNewStories&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Single&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;NetworkResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;aggregateApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadNewStories&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toAggregateResult&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;toAggregateResult&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isSuccessful&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nc"&gt;NetworkResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromAggregateResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="nf"&gt;emptyList&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nc"&gt;NetworkResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromErrorResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;code&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NetworkResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// unmodified code omitted&lt;/span&gt;

    &lt;span class="k"&gt;companion&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;fromAggregateResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;):&lt;/span&gt; &lt;span class="nc"&gt;NetworkResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AggregateArticles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;fromErrorResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?):&lt;/span&gt; &lt;span class="nc"&gt;NetworkResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="mi"&gt;404&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;HttpError&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ResourceNotFound&lt;/span&gt;
                &lt;span class="mi"&gt;503&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;HttpError&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ServiceUnavailable&lt;/span&gt;
                &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;HttpError&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;UnknownError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's all there is to it! Now every valid network response will be wrapped in your ADT class.&lt;br&gt;
There are some caveats though. This will only take care of valid responses that are successfully parsed. Any errors during JSON deserialization or network stack exceptions such as timeouts and SSL protocol exceptions will still be thrown and have to be handled elsewhere.&lt;/p&gt;

&lt;p&gt;But we now have a nice (and easily testable!) way to handle non-200 server responses that have meaning to our business logic.&lt;/p&gt;

&lt;p&gt;Thanks for reading :)&lt;/p&gt;

&lt;p&gt;P.S. If you would like to see the code samples with some more context check out &lt;a href="https://github.com/danielw93/hackernews-digest" rel="noopener noreferrer"&gt;this repo&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>android</category>
      <category>softwaredevelopment</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Happy Holidays! Have some graph theory...</title>
      <dc:creator>Daniel Waller (he/him)</dc:creator>
      <pubDate>Tue, 12 Dec 2017 17:10:42 +0000</pubDate>
      <link>https://forem.com/danielw/happy-holidays-have-some-graph-theory-c9h</link>
      <guid>https://forem.com/danielw/happy-holidays-have-some-graph-theory-c9h</guid>
      <description>&lt;p&gt;So its more or less Christmastime and I was reminded the other day of a popular childrens puzzle in German-speaking countries called &lt;em&gt;Haus des Nikolaus&lt;/em&gt; which roughly translates to &lt;em&gt;House of Santa Claus&lt;/em&gt;&lt;sup&gt;1&lt;/sup&gt;. &lt;br&gt;
Being a child I always solved it with one solution I had memorized but only a few years ago I learned about the theoretical basis of it during a university math class by accident.&lt;/p&gt;

&lt;p&gt;So having a little downtime and thinking it could be fun, welcome to my first ever post on the internet!&lt;/p&gt;

&lt;p&gt;The puzzle is to draw a house like the one in the title image, beginning in any one of its corners, never retracing a line you've already drawn and never lifting your pen.&lt;br&gt;
The end result should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Itg-ay4D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/0dnumwzbl1ljtupp87gv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Itg-ay4D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/0dnumwzbl1ljtupp87gv.png" alt="The end result of a *Haus des Nikolaus* puzzle"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Take out a piece of paper and try it! Can you solve it? Can you think of multiple solutions to the problem?&lt;/p&gt;




&lt;p&gt;Did it? Couldn't do it? &lt;strong&gt;I came here to read not to draw!&lt;/strong&gt;? &lt;br&gt;
Let's see how we can formalize and solve the problem using graphs!&lt;/p&gt;

&lt;h3&gt;
  
  
  For starters let's have some definitions
&lt;/h3&gt;

&lt;p&gt;A graph consists of a set of &lt;em&gt;nodes&lt;/em&gt; (or vertices) and &lt;em&gt;edges&lt;/em&gt; connecting them. In our special case we are talking about an &lt;em&gt;undirected graph&lt;/em&gt;, ie. a graph in which you can traverse the edges in either direction.&lt;br&gt;
Another definition we have to make is the &lt;em&gt;degree&lt;/em&gt; of a node, ie. the number of edges connected to a node.&lt;/p&gt;

&lt;p&gt;So with our new knowledge let's setup santa's house as a graph with each node's degree denoted next to it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--I-o78wac--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/e8ca3vx6v7idsb0go4h5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I-o78wac--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/e8ca3vx6v7idsb0go4h5.png" alt="The end result of a *Haus des Nikolaus* puzzle represented as a 5 node graph with each nodes degree denoted next to it."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Brief course in history
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ejao4PdQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/dltuy2g09y83hbqdonqi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ejao4PdQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/dltuy2g09y83hbqdonqi.png" alt="Old map of the city of Königsberg with its 4 landmasses and 7 bridges visible"&gt;&lt;/a&gt;&lt;br&gt;
The &lt;em&gt;Haus des Nikolaus&lt;/em&gt; problem could be interpreted as a variation of an early 18th century problem, the &lt;em&gt;Seven Bridges of Königsberg&lt;/em&gt;, addressed by mathematician Leonhard Euler (1707-1783). &lt;br&gt;
The question was whether it was possible to devise a round-trip through the then capital of East Prussia, the city of Königsberg, crossing all of its seven bridges exactly once and end up where you started your trip.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--I3UqC008--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/mruafit1q1f4axjjuu6z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I3UqC008--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/mruafit1q1f4axjjuu6z.png" alt="Seven Bridges of Königsberg problem in graph form"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Euler's solution to the problem is today regarded as the first ever published paper on graph theory. &lt;br&gt;
He observed that when you enter a landmass coming across a bridge you must also exit that landmass using a bridge. Since you aren't allowed to use the same bridge twice for either entering or exiting, each landmass must have an even number of bridges connected to it. One for entering and one for exiting the landmass.&lt;/p&gt;

&lt;p&gt;He concluded that the &lt;em&gt;Seven Bridges of Königsberg&lt;/em&gt; problem has no solution because each of the landmasses had an odd number of bridges connected to it.&lt;br&gt;
To state the problem and Eulers observation more generally:&lt;br&gt;
    Find a way through a graph that &lt;br&gt;
        1) visits all nodes, &lt;br&gt;
        2) traverses every edge exactly once,&lt;br&gt;
        3) and starts and ends in the same node. &lt;br&gt;
    This way is called a &lt;em&gt;Eulerian cycle&lt;/em&gt;.&lt;br&gt;
    An undirected graph has a Eulerian cycle if and only if every node has an even degree.&lt;/p&gt;

&lt;p&gt;For a more complete and coherent post about the &lt;em&gt;Seven Bridges of Königsberg&lt;/em&gt; problem check out &lt;a href="https://dev.to/vaidehijoshi"&gt;Vaidehi Joshi&lt;/a&gt;'s &lt;a href="https://dev.to/vaidehijoshi/knigsberg-seven-small-bridges-one-giant-graph-problem"&gt;awesome post&lt;/a&gt; on it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Back to our house
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--I-o78wac--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/e8ca3vx6v7idsb0go4h5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I-o78wac--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/e8ca3vx6v7idsb0go4h5.png" alt="The end result of a 'Haus des Nikolaus' puzzle represented as a 5 node graph with each nodes degree denoted next to it."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The constraints for our house drawing stated earlier can be rephrased as a weaker variation of the Eulerian cycle:&lt;br&gt;
Find a way through a graph that&lt;br&gt;
    1) visits all nodes, &lt;br&gt;
    2) traverses every edge exactly once, &lt;br&gt;
    3) BUT start and end node need not be the same. &lt;br&gt;
    This way is called a &lt;em&gt;Eulerian trail&lt;/em&gt;. &lt;br&gt;
From our observations above we can derive that a Eulerian trail is present in a graph if and only if exactly zero or two nodes have an odd degree. &lt;/p&gt;

&lt;p&gt;As we can see from our &lt;em&gt;Haus des Nikolaus&lt;/em&gt; graph representation luckily we have exactly 2 nodes with odd degree in our graph so we can conclude that a solution is in fact possible!&lt;br&gt;
But there's a catch. &lt;br&gt;
The only way you can make your drawing work is when you start and end your path in one of the odd degree nodes. &lt;/p&gt;

&lt;p&gt;To make this fact more apparent, let's tag each edge with either 'in' or 'out' depending on which direction you want to use them in your path. &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0bnw23wC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/spj9qa9hjzznz90epxb4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0bnw23wC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/spj9qa9hjzznz90epxb4.png" alt="Haus des Nikolaus, each edge connected to a node tagged with 'in' or 'out'"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we can immediately see that one of the bottom nodes has 2 &lt;em&gt;inputs&lt;/em&gt; but only 1 &lt;em&gt;output&lt;/em&gt;, meaning you'll inevitably end up getting trapped in it. This naturally must be your end node. &lt;br&gt;
The other of the two bottom nodes has 2 &lt;em&gt;outputs&lt;/em&gt; but just 1 &lt;em&gt;input&lt;/em&gt;, meaning that in order to traverse all the edges in the graph one of the 2 outputs needs to be the first edge you traverse. Making that node your start node.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QXBXfYWF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://upload.wikimedia.org/wikipedia/commons/1/11/Blender3D_HouseOfStNiclas.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QXBXfYWF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://upload.wikimedia.org/wikipedia/commons/1/11/Blender3D_HouseOfStNiclas.gif" alt="Wikimedia gif of a Haus des Nikolaus being drawn"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So there we have it. A simple childrens puzzle solved with far too much effort!&lt;br&gt;
I have the feeling that this is the kind of useless information that tends to stick with you forever. Also it's always interesting to encounter and identify variations of famous mathematical problems in the wild.&lt;/p&gt;

&lt;p&gt;Hope you had fun :)&lt;/p&gt;

&lt;h4&gt;
  
  
  Extra Credit
&lt;/h4&gt;

&lt;p&gt;How many possible solutions to this problem are there? &lt;/p&gt;




&lt;p&gt;&lt;sup&gt;1&lt;sup&gt;&lt;/sup&gt; That's a lie &lt;em&gt;Nikolaus&lt;/em&gt; does not translate to &lt;em&gt;Santa Clause&lt;/em&gt;. He is &lt;a href="https://en.wikipedia.org/wiki/Saint_Nicholas_Day"&gt;Saint Nicholas&lt;/a&gt;. The rhyme just works better like that :P&lt;/sup&gt;&lt;/p&gt;

</description>
      <category>graphtheory</category>
      <category>brainteaser</category>
      <category>fun</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>Hi, I'm Daniel Waller</title>
      <dc:creator>Daniel Waller (he/him)</dc:creator>
      <pubDate>Wed, 24 May 2017 14:22:12 +0000</pubDate>
      <link>https://forem.com/danielw/hi-im-daniel-waller</link>
      <guid>https://forem.com/danielw/hi-im-daniel-waller</guid>
      <description></description>
      <category>introduction</category>
    </item>
  </channel>
</rss>
