<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>joeellis.la</title>
    <description>Tech musings on all things Elixir, Ruby, and Javascript.</description>
    <link>http://joeellis.la/</link>
    <atom:link href="http://joeellis.la/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Sat, 16 Sep 2017 15:27:53 -0500</pubDate>
    <lastBuildDate>Sat, 16 Sep 2017 15:27:53 -0500</lastBuildDate>
    <generator>Jekyll v3.5.2</generator>
    
      <item>
        <title>Using CircleCI 2.0 with Elixir and Phoenix</title>
        <description>&lt;p&gt;I recently took advantage of CircleCI’s new 2.0 features in a Phoenix application and thought it was worth sharing here.  CircleCI 2.0 &lt;a href=&quot;https://circleci.com/docs/2.0/#features&quot;&gt;boasts many new features&lt;/a&gt; but the most interesting ones to me were the native support for Docker images and their advanced caching features.&lt;/p&gt;

&lt;p&gt;It turns out to be a great move - both of these features helped cut our build times by at least &lt;em&gt;50%&lt;/em&gt;. Using a Docker image meant CircleCI no longer needed to compile Elixir, Erlang, and Node for each job. And the advanced caching features went a step further by giving us control over how our &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;deps&lt;/code&gt; directories were cached, saving us on the compilation time between jobs. It did take some research through their docs and forums to figure out how to create a complete, working CircleCI yaml file, so I wanted to write up what I did in case my example helps save time for other people.&lt;/p&gt;

&lt;p&gt;To start, my example config assumes the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Elixir 1.4.2, Erlang 19.x, Node 7.x, and &lt;code class=&quot;highlighter-rouge&quot;&gt;yarn&lt;/code&gt; compiled into a single Docker image&lt;/li&gt;
  &lt;li&gt;Phoenix 1.3.x app&lt;/li&gt;
  &lt;li&gt;PostgreSQL 9.6&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your stack may differ from the above, and that’s ok. The strategy outlined here should still work for the majority of Phoenix and Elixir projects. Just know there may be a few things you’ll have to change to make this config work for your project.&lt;/p&gt;

&lt;p&gt;To start, here is what the full CircleCI yaml config file looks like:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# .circleci/config.yml&lt;/span&gt;

&lt;span class=&quot;s&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;docker&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;joeellis/elixir-phoenix-node:1.0&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;MIX_ENV=test&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres:9.6.2-alpine&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;POSTGRES_USER=my-database-user&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;POSTGRES_PASSWORD=my-database-password&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;POSTGRES_HOST=localhost&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;working_directory&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;~/app&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;checkout&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;restore_cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-mix-cache-{{ .Branch }}-{{ checksum &quot;mix.lock&quot; }}&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-mix-cache-{{ .Branch }}&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-mix-cache&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;restore_cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-build-cache-{{ .Branch }}&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-build-cache&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mix do deps.get, compile&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;save_cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-mix-cache-{{ .Branch }}-{{ checksum &quot;mix.lock&quot; }}&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;deps&quot;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;save_cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-mix-cache-{{ .Branch }}&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;deps&quot;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;save_cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-mix-cache&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;deps&quot;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;save_cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-build-cache-{{ .Branch }}&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;_build&quot;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;save_cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-build-cache&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;_build&quot;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;restore_cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-yarn-cache-{{ .Branch }}-{{ checksum &quot;assets/yarn.lock&quot; }}&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-yarn-cache-{{ .Branch }}&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-yarn-cache&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;working_directory&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;assets&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;yarn install &amp;amp;&amp;amp; yarn test&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;save_cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-yarn-cache-{{ .Branch }}-{{ checksum &quot;assets/yarn.lock&quot; }}&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;assets/node_modules&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;save_cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-yarn-cache-{{ .Branch }}&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;assets/node_modules&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;save_cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-yarn-cache&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;assets/node_modules&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mix ecto.create &amp;amp;&amp;amp; mix ecto.migrate&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mix phoenix.digest&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mix test&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;That is a bunch of config, so let’s break it down piece by piece and see what’s going on:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# .circleci/config.yml&lt;/span&gt;

&lt;span class=&quot;s&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;docker&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;joeellis/elixir-phoenix-node:1.0&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;MIX_ENV=test&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres:9.6.2-alpine&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;POSTGRES_USER=my-database-user&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;POSTGRES_PASSWORD=my-database-password&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;POSTGRES_HOST=localhost&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;working_directory&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;~/app&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;First we tell CircleCI that we’d like our build to execute under the &lt;a href=&quot;https://hub.docker.com/r/joeellis/elixir-phoenix-node/&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;joeellis/elixir-phoenix-node:1.0&lt;/code&gt; docker image&lt;/a&gt;.  This is a simple docker image I built with Elixir 1.4.2, Erlang 19.x, node 7.x, and &lt;code class=&quot;highlighter-rouge&quot;&gt;yarn&lt;/code&gt; already installed. Any Docker image will do though - CircleCI even offers &lt;a href=&quot;https://circleci.com/docs/2.0/circleci-images/#elixir&quot;&gt;pre-built Elixir images&lt;/a&gt; if you’d rather not create your own.&lt;/p&gt;

&lt;p&gt;The config also downloads a second docker image, &lt;a href=&quot;https://hub.docker.com/_/postgres/&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;postgres:9.6.2-alpine&lt;/code&gt;&lt;/a&gt; to create a database container and with our app’s database credentials (see &lt;a href=&quot;https://hub.docker.com/_/postgres/&quot;&gt;the official docker image docs&lt;/a&gt; for more supported options).  Lastly, it sets a working directory folder called &lt;code class=&quot;highlighter-rouge&quot;&gt;app&lt;/code&gt; in the CircleCI user’s home directory.&lt;/p&gt;

&lt;p&gt;Next, the build checks out our git repo, and restores any caches that may already exist:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;checkout&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;restore_cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-mix-cache-{{ .Branch }}-{{ checksum &quot;mix.lock&quot; }}&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-mix-cache-{{ .Branch }}&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-mix-cache&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;restore_cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-build-cache-{{ .Branch }}&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-build-cache&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mix do deps.get, compile&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;restore_cache&lt;/code&gt; and cache keys here make look funny to you. What are they and where do they come from?  In short, this is part of CircleCI’s new caching mechanism, and before you read the rest of this article, I &lt;em&gt;highly&lt;/em&gt; recommend you &lt;a href=&quot;https://circleci.com/docs/2.0/caching/&quot;&gt;read and understand their caching docs&lt;/a&gt; because you will need to understand it to create the best caching strategy for your own app.  After reading that, read below about the &lt;code class=&quot;highlighter-rouge&quot;&gt;save_cache&lt;/code&gt; steps first and we’ll cirle back to how this &lt;code class=&quot;highlighter-rouge&quot;&gt;restore_cache&lt;/code&gt; stuff works in a minute.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;s&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;save_cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-mix-cache-{{ .Branch }}-{{ checksum &quot;mix.lock&quot; }}&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;deps&quot;&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;save_cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-mix-cache-{{ .Branch }}&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;deps&quot;&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;save_cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-mix-cache&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;deps&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;For our &lt;code class=&quot;highlighter-rouge&quot;&gt;deps&lt;/code&gt; directory, we create three types of CircleCI caches:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;A cache keyed against the branch name &lt;em&gt;and&lt;/em&gt; the &lt;code class=&quot;highlighter-rouge&quot;&gt;mix.lock&lt;/code&gt; checksum.
    &lt;ul&gt;
      &lt;li&gt;This cache is used for most commits to a branch.&lt;/li&gt;
      &lt;li&gt;It also uses a &lt;code class=&quot;highlighter-rouge&quot;&gt;mix.lock&lt;/code&gt; checksum as part of its key. If new dependencies are added and the &lt;code class=&quot;highlighter-rouge&quot;&gt;mix.lock&lt;/code&gt; file changes, this will make sure to cause a cache miss and force a recompilation of the new dependencies.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;A cache keyed against just the branch name.
    &lt;ul&gt;
      &lt;li&gt;CircleCI uses this cache as a fallback if the first cache can’t be found, usually when the &lt;code class=&quot;highlighter-rouge&quot;&gt;mix.lock&lt;/code&gt; file has changed.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;A cache with just a generic key of &lt;code class=&quot;highlighter-rouge&quot;&gt;v1-mix-cache&lt;/code&gt;.
    &lt;ul&gt;
      &lt;li&gt;This cache is used if CircleCI can’t find any of the above caches, like in the case of the first commit of a new branch. Also, if you are creating small commits often, then you’ll find this cache is very useful at saving on compilation times between branches.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For our &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; directory, you can see it’s very similar to the &lt;code class=&quot;highlighter-rouge&quot;&gt;deps&lt;/code&gt; caching strategy:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;save_cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-build-cache-{{ .Branch }}&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;_build&quot;&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;save_cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-build-cache&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;_build&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;One small difference is that we only use two types of caches as there is no ‘lockfile’ for the build directory to cache against.  This makes sense though, as we want your project to always recompile itself with the new changes made in each commit.&lt;/p&gt;

&lt;p&gt;After you understand how the &lt;code class=&quot;highlighter-rouge&quot;&gt;save_cache&lt;/code&gt; works, then the &lt;code class=&quot;highlighter-rouge&quot;&gt;restore_cache&lt;/code&gt; keys in the steps above make more sense:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;checkout&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;restore_cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-mix-cache-{{ .Branch }}-{{ checksum &quot;mix.lock&quot; }}&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-mix-cache-{{ .Branch }}&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-mix-cache&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;restore_cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-build-cache-{{ .Branch }}&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-build-cache&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mix do deps.get, compile&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;All we are doing here is declaring which of our saved caches to check and which order should check them.&lt;/p&gt;

&lt;p&gt;Next, we use the exact same caching strategy to install our frontend &lt;code class=&quot;highlighter-rouge&quot;&gt;node_modules&lt;/code&gt; using &lt;code class=&quot;highlighter-rouge&quot;&gt;yarn&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;restore_cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-yarn-cache-{{ .Branch }}-{{ checksum &quot;assets/yarn.lock&quot; }}&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-yarn-cache-{{ .Branch }}&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-yarn-cache&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;working_directory&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;assets&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;yarn install &amp;amp;&amp;amp; yarn test&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;save_cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-yarn-cache-{{ .Branch }}-{{ checksum &quot;assets/yarn.lock&quot; }}&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;assets/node_modules&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;save_cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-yarn-cache-{{ .Branch }}&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;assets/node_modules&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;save_cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1-yarn-cache&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;assets/node_modules&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If you are using &lt;code class=&quot;highlighter-rouge&quot;&gt;npm&lt;/code&gt;, the setup is roughly the same - instead of keying against a &lt;code class=&quot;highlighter-rouge&quot;&gt;yarn.lock&lt;/code&gt; checksum, you would key against &lt;code class=&quot;highlighter-rouge&quot;&gt;package-lock.json&lt;/code&gt;. I just prefer &lt;code class=&quot;highlighter-rouge&quot;&gt;yarn&lt;/code&gt; as it is very fast and deterministic. Also, the latest &lt;code class=&quot;highlighter-rouge&quot;&gt;npm&lt;/code&gt; (&lt;code class=&quot;highlighter-rouge&quot;&gt;5.3.0&lt;/code&gt; at the time of this writing) was having some issues compiling correctly in production.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mix ecto.create &amp;amp;&amp;amp; mix ecto.migrate&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mix phoenix.digest&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mix test&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This last step should look familiar to anyone who has run a Phoenix application before.  Just create the database, create a digest for your assets if needed, and finally run &lt;code class=&quot;highlighter-rouge&quot;&gt;mix test&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Hopefully, this rundown of CircleCI’s 2.0 features helps someone out there. The config file looks long, but as you can see, the bulk of it is just repeated use of the same caching pattern. Give it a try, and if you run into trouble, free to &lt;a href=&quot;http://twitter.com/notjoeellis&quot;&gt;tweet at me&lt;/a&gt; or ping me on the &lt;a href=&quot;https://elixir-slackin.herokuapp.com/&quot;&gt;elixir-lang Slack channel&lt;/a&gt;!&lt;/p&gt;
</description>
        <pubDate>Tue, 12 Sep 2017 00:00:00 -0500</pubDate>
        <link>http://joeellis.la/using-circle-2-0-with-elixir/</link>
        <guid isPermaLink="true">http://joeellis.la/using-circle-2-0-with-elixir/</guid>
        
        <category>elixir</category>
        
        <category>phoenix</category>
        
        <category>circleci</category>
        
        
      </item>
    
      <item>
        <title>Debugging Mix Dependencies Locally</title>
        <description>&lt;p&gt;Have you ever needed to debug a dependency, perhaps to track down a bug or maybe just to see how it works? In scripted languages like Ruby or JS, you can usually add debug statements, like &lt;code class=&quot;highlighter-rouge&quot;&gt;puts &quot;test&quot;&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;console.log(&quot;test&quot;)&lt;/code&gt;, to the dependency’s code and see the output the next time you run your app.&lt;/p&gt;

&lt;p&gt;This does not quite work in an Elixir / Mix project as dependencies are compiled and usually compiled just once, when they are fetched. Fortunately for us, &lt;a href=&quot;https://hexdocs.pm/mix/Mix.html&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mix&lt;/code&gt;&lt;/a&gt; (Elixir’s build tool) gives us an option called &lt;code class=&quot;highlighter-rouge&quot;&gt;path&lt;/code&gt;, to automatically recompile a dependency whenever it changes. Simply declare the &lt;code class=&quot;highlighter-rouge&quot;&gt;path&lt;/code&gt; option with your dependency in your project’s &lt;code class=&quot;highlighter-rouge&quot;&gt;mix.exs&lt;/code&gt; like so:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elixir&quot; data-lang=&quot;elixir&quot;&gt;&lt;span class=&quot;k&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deps&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:my_dep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'~&amp;gt; 1.0.0'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;path:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'deps/my_dep'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This tells &lt;code class=&quot;highlighter-rouge&quot;&gt;mix&lt;/code&gt; to look at the &lt;code class=&quot;highlighter-rouge&quot;&gt;deps/my_dep&lt;/code&gt; folder for that dependency when compiling, and recompile it (and related project files) anytime a change is detected. Now you can open up the code at &lt;code class=&quot;highlighter-rouge&quot;&gt;deps/my_dep&lt;/code&gt;, add your debug statements, and explore away!&lt;/p&gt;

&lt;h3 id=&quot;absolute--relative-paths&quot;&gt;Absolute &amp;amp; Relative Paths&lt;/h3&gt;

&lt;p&gt;Note that the &lt;code class=&quot;highlighter-rouge&quot;&gt;path&lt;/code&gt; option isn’t limited to looking at the &lt;code class=&quot;highlighter-rouge&quot;&gt;deps&lt;/code&gt; folder, and also accepts relative and absolute system paths, like so:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elixir&quot; data-lang=&quot;elixir&quot;&gt;&lt;span class=&quot;k&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deps&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:my_dep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'~&amp;gt; 1.0.0'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;path:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'../path/to/my/local/my_dep'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This is particularly useful if you are developing a new Hex package along side your project, or if you want to clone a package locally to track your changes and submit patches to the original author later.&lt;/p&gt;

&lt;h3 id=&quot;reset-the-dependency&quot;&gt;Reset the Dependency&lt;/h3&gt;

&lt;p&gt;Once you’re finished debugging and want to restore the dependency to it’s normal state, simply clean it out and re-fetch it by running:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elixir&quot; data-lang=&quot;elixir&quot;&gt;&lt;span class=&quot;n&quot;&gt;mix&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deps&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_dep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deps&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compile&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And everything will be reset back to how it was in the beginning.&lt;/p&gt;
</description>
        <pubDate>Sat, 08 Jul 2017 00:00:00 -0500</pubDate>
        <link>http://joeellis.la/debugging-mix-dependencies-locally/</link>
        <guid isPermaLink="true">http://joeellis.la/debugging-mix-dependencies-locally/</guid>
        
        <category>elixir</category>
        
        
      </item>
    
      <item>
        <title>Testing Redux: Async Action Creators</title>
        <description>&lt;p&gt;One of the biggest (and often overlooked) advantages of Redux apps is how inherently testable they are. Redux apps are composed of mostly plain old JavaScript objects and functions, so testing rarely requires special tricks or methods.&lt;/p&gt;

&lt;p&gt;And probably the most valuable tests you can write for a Redux app are action creator tests. Actions tend to collect most of the messy, complex code in a Redux app, and learning how to test them well can make future maintenance and refactoring easier for you and your team.&lt;/p&gt;

&lt;p&gt;In this article, I’ll explain the pattern I use for testing Redux actions (&lt;a href=&quot;http://redux.js.org/docs/advanced/AsyncActions.html#async-action-creators&quot;&gt;asynchronous action creators&lt;/a&gt; in particular) and hopefully show that testing even complex actions can be pretty easy!&lt;/p&gt;

&lt;h3 id=&quot;the-setup&quot;&gt;The Setup&lt;/h3&gt;

&lt;h4 id=&quot;packages-to-install&quot;&gt;Packages to install&lt;/h4&gt;

&lt;p&gt;For action testing, I use the following packages:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/mochajs/mocha&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mocha&lt;/code&gt;&lt;/a&gt; - my JS testing framework of choice. If you don’t use &lt;code class=&quot;highlighter-rouge&quot;&gt;mocha&lt;/code&gt;, however, keep reading - the strategies in this article are not &lt;code class=&quot;highlighter-rouge&quot;&gt;mocha&lt;/code&gt; specific and can work with any JS testing framework.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/node-nock/nock&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;nock&lt;/code&gt;&lt;/a&gt; - an HTTP mocking library. This is only used if your app is making HTTP requests (but at some point, your app probably will).&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://arnaudbenard.com/redux-mock-store/&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;redux-mock-store&lt;/code&gt;&lt;/a&gt; - a simple, but effective Redux store mock.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;configuring-nock&quot;&gt;Configuring nock&lt;/h4&gt;

&lt;p&gt;I usually create a base &lt;code class=&quot;highlighter-rouge&quot;&gt;nock&lt;/code&gt; setup file in my app that I can import when needed.  It looks something like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;nock&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'nock'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;nock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;disableNetConnect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Configure the base url to your endpoint here&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;nock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://localhost/api/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I recommend adding &lt;code class=&quot;highlighter-rouge&quot;&gt;nock.disableNetConnect()&lt;/code&gt; as it will force disable all non-mocked HTTP requests during your test. This ensures your tests won’t accidentally hit a real API endpoint during a test run, a bad practice that is both dangerous and slow.&lt;/p&gt;

&lt;h3 id=&quot;an-example-action-creator&quot;&gt;An Example Action Creator&lt;/h3&gt;

&lt;p&gt;Let’s start with a look at a real world example, this set of action creators for fetching users from an API endpoint:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kr&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fetchUsersRequest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;FETCH_USERS_REQUEST&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kr&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fetchUsersSuccess&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;FETCH_USERS_SUCCESS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;users&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kr&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fetchUsersFailure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;FETCH_USERS_FAILURE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kr&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fetchUsers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fetchUsersRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/users&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(({&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;users&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fetchUsersSuccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fetchUsersFailure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Here, &lt;code class=&quot;highlighter-rouge&quot;&gt;fetchUsers&lt;/code&gt; is the main action creator we want to test. When executed, it will send an HTTP request to the API, and dispatch two out of these three actions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;fetchUsersRequest&lt;/code&gt; - an action used to represent a request in progress (handy if your UI needs to show some kind of loading state)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;fetchUsersSuccess&lt;/code&gt; - an action that fires if the API call succeeds&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;fetchUsersFailure&lt;/code&gt; - an action that fires if the API call fails&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We want our test to check that these action creators are all invoked in the correct order, with the correct data, for both successful and unsuccessful API calls. To do this, we’ll do the following:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Setup a mock response for our HTTP API endpoint. If the action makes a request to &lt;code class=&quot;highlighter-rouge&quot;&gt;/users/123&lt;/code&gt;, then we should make sure it hits our &lt;code class=&quot;highlighter-rouge&quot;&gt;nock&lt;/code&gt; server and have &lt;code class=&quot;highlighter-rouge&quot;&gt;nock&lt;/code&gt; return a mock response that we can control and test against.&lt;/li&gt;
  &lt;li&gt;Setup an ordered array of the actions we expect our action creator to dispatch and send to the reducers.&lt;/li&gt;
  &lt;li&gt;Dispatch the action creator function, assert it received our mock data, and assert it called all of our expected actions in the correct order.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;testing-the-happy-path&quot;&gt;Testing The Happy Path&lt;/h3&gt;

&lt;p&gt;Given the strategy above, here is what testing the success path of &lt;code class=&quot;highlighter-rouge&quot;&gt;fetchUsers&lt;/code&gt; might look like:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;assert&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;mockStore&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;redux-mock-store&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;nock&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;../path/to/nockSetup&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Constants&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;../path/to/Constants&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fetchUsers&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;../path/to/UserActions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;UserActions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;beforeEach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;store&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;mockStore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({});&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;usersData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;#fetchUser&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;can fetch users from the api&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;nock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/users&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;reply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;usersData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

      &lt;span class=&quot;kr&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;expectedActions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Constants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;FETCH_USERS_REQUEST&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Constants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;FETCH_USERS_SUCCESS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;usersData&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;store&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fetchUsers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;deepEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getActions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;expectedActions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Let’s break this test down into the important parts:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;beforeEach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;store&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;mockStore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({});&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;usersData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Here, we setup both our mock store and some fake data that we’ll use later for our mock HTTP response.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;#fetchUser&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;can fetch users from the api&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;nock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/users&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;reply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;usersData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Next, we use &lt;code class=&quot;highlighter-rouge&quot;&gt;nock&lt;/code&gt; to create a mock HTTP route for our Redux app to hit. This will return back our mock user data (&lt;code class=&quot;highlighter-rouge&quot;&gt;this.usersData&lt;/code&gt;) whenever a successful GET request is made to the &lt;code class=&quot;highlighter-rouge&quot;&gt;/users&lt;/code&gt; route.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;expectedActions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Constants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;FETCH_USERS_REQUEST&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Constants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;FETCH_USERS_SUCCESS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;usersData&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Then we create an &lt;code class=&quot;highlighter-rouge&quot;&gt;expectedActions&lt;/code&gt; array, an array of the actions we expect to be dispatched in the order we expect them to be dispatched in. The first object is the action created when &lt;code class=&quot;highlighter-rouge&quot;&gt;fetchUsersRequest&lt;/code&gt; is invoked, and the second object is the action created when &lt;code class=&quot;highlighter-rouge&quot;&gt;fetchUsersSuccess&lt;/code&gt; is invoked. Note: the &lt;code class=&quot;highlighter-rouge&quot;&gt;expectedActions&lt;/code&gt; array here does not contain the action for &lt;code class=&quot;highlighter-rouge&quot;&gt;fetchUsersFailure&lt;/code&gt; as we are only testing the success path right now.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;store&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fetchUsers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;deepEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getActions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;expectedActions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Finally, we kick off the test by dispatching our &lt;code class=&quot;highlighter-rouge&quot;&gt;fetchUsers&lt;/code&gt; action creator using our mock store’s &lt;code class=&quot;highlighter-rouge&quot;&gt;dispatch&lt;/code&gt; function. &lt;code class=&quot;highlighter-rouge&quot;&gt;redux-mock-store&lt;/code&gt; comes with a handy &lt;code class=&quot;highlighter-rouge&quot;&gt;store.getActions&lt;/code&gt; function, so we can see which actions were called, and make sure it equals our &lt;code class=&quot;highlighter-rouge&quot;&gt;expectedActions&lt;/code&gt; array.&lt;/p&gt;

&lt;h3 id=&quot;testing-the-not-so-happy-path&quot;&gt;Testing The Not So Happy Path&lt;/h3&gt;

&lt;p&gt;We should also test that our action creators handle errors appropriately. Here is what testing the failure path of &lt;code class=&quot;highlighter-rouge&quot;&gt;fetchUsers&lt;/code&gt; might look like:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;handle errors when fetching users from the api&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;nock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/users&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;reply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;usersData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;kr&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;expectedActions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Constants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;FETCH_USERS_REQUEST&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Constants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;FETCH_USERS_FAILURE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;store&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fetchUsers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;This test should not have succeeded.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;deepEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getActions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;expectedActions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;It has a very similar structure to the success test above. The main difference is our HTTP request mock now replies with a HTTP &lt;code class=&quot;highlighter-rouge&quot;&gt;500&lt;/code&gt; status code (used for internal server errors) and our &lt;code class=&quot;highlighter-rouge&quot;&gt;expectedActions&lt;/code&gt; array includes the results from the &lt;code class=&quot;highlighter-rouge&quot;&gt;fetchUsersFailure&lt;/code&gt; action creator. Also, we run our &lt;code class=&quot;highlighter-rouge&quot;&gt;assert&lt;/code&gt; in the &lt;code class=&quot;highlighter-rouge&quot;&gt;catch&lt;/code&gt; portion of our promise (as we expect a failure here), and we throw an error in the success path in case our action creator incorrectly manages to succeed.&lt;/p&gt;

&lt;p&gt;And that’s it! You just learned how to fully test an async action creator for a Redux app!&lt;/p&gt;

&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;With this testing strategy, even as your action creators become more complex, the testing strategy itself stays the same. They still follow the same pattern of creating a mock HTTP response, creating a list of expected actions, and comparing both to the result of the action creator function call. I recommend giving this testing strategy a try, especially if you’ve never tested your Redux actions before - I think you’ll find them to be invaluable addition to your Redux app.&lt;/p&gt;

</description>
        <pubDate>Wed, 05 Jul 2017 00:00:00 -0500</pubDate>
        <link>http://joeellis.la/testing-redux-actions/</link>
        <guid isPermaLink="true">http://joeellis.la/testing-redux-actions/</guid>
        
        <category>react</category>
        
        <category>redux</category>
        
        <category>testing</category>
        
        
      </item>
    
      <item>
        <title>Redux Architecture Guidelines</title>
        <description>&lt;p&gt;I’ve written many Redux apps over the past couple of years, and it is by far my favorite JS framework. The only downside is, unlike other frameworks, Redux is far less opinionated in how to structure an app. I prefer this freedom, but it does make for a steeper learning curve, especially if you’re new to Redux.  So I decided to write up some of the higher level thinking and structure I’ve picked up and often use when building a Redux app. Hopefully it comes in handy for someone out there.&lt;/p&gt;

&lt;h3 id=&quot;state&quot;&gt;State&lt;/h3&gt;

&lt;h5 id=&quot;plan-your-state-shape&quot;&gt;Plan your state shape&lt;/h5&gt;

&lt;p&gt;In terms of saving time down the road, planning the structure of your state object upfront is most valuable thing you can do for your app. A poorly formed state object will make your app difficult to maintain and is avoidable with some planning. I run through this quick checklist when planning out state objects:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;How will it store multiple resources from an API (users, accounts, items, etc)?&lt;/li&gt;
  &lt;li&gt;How will it handle loading states (showing loading spinners when fetching /updating data)?&lt;/li&gt;
  &lt;li&gt;How will it handle showing and clearing of UI success and error notifications?&lt;/li&gt;
  &lt;li&gt;Does it feel consistent and predictable? Could another team member easily work with it?&lt;/li&gt;
  &lt;li&gt;Is it easy to access data within it? Does it nest properties unnecessarily?&lt;/li&gt;
  &lt;li&gt;Is it serializable? Could it easily be stored away in localstorage or in a database?&lt;/li&gt;
  &lt;li&gt;Are there any properties you could pull from the URL instead of in the state?&lt;/li&gt;
  &lt;li&gt;Is there any duplicated data in here? If so, is that really needed?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are &lt;strong&gt;many&lt;/strong&gt; different ways to answer these questions - it depends on your app. But in my experience, having at least an answer for each will save you time in the long run.&lt;/p&gt;

&lt;h5 id=&quot;avoid-nesting-state-objects&quot;&gt;Avoid nesting state objects&lt;/h5&gt;

&lt;p&gt;Some Redux apps have deeply nested state structures, i.e. shapes that look like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;baz&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nl&quot;&gt;qux&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This often happens when we work with relational data as it feels natural to use nesting to represent those relationships. Unfortunately, nested data structures create complexity. At the component level, you’ll have to reach even deeper into the state to get certain information. And at the reducer level, merging new data into your state will become far more complex. On top of all of that, nested data can even cause performance issues with React / Redux itself.&lt;/p&gt;

&lt;p&gt;Consider instead to flatten and &lt;a href=&quot;http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html&quot;&gt;normalize&lt;/a&gt; your state shape. In Redux land, the shallower the nesting, the easier it is is to fetch and update state data in your app. Normalized states help solve the problems listed above, and make your state much more flexible overall.&lt;/p&gt;

&lt;h5 id=&quot;storing-only-raw-data-in-the-state&quot;&gt;Storing only raw data in the state&lt;/h5&gt;

&lt;p&gt;It’s tempting to use Redux’s state as a vehicle to store any and all information you think you may need later. Yet, doing so will increase your app’s complexity in the form of state bloat and redundant properties. This, in turn, increases complexity in your actions, reducers, and tests. So what should and shouldn’t be stored?&lt;/p&gt;

&lt;p&gt;In Redux apps, there are really two types of data. The first is raw data, data your app requires to run. User data fetched from an API is an example of raw data - without it, your app won’t have the information it needs to run. The second is derived data, or data created from other existing data. Using the &lt;code class=&quot;highlighter-rouge&quot;&gt;firstName&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;lastName&lt;/code&gt; properties to display a user’s name as &lt;code class=&quot;highlighter-rouge&quot;&gt;Jane Doe&lt;/code&gt; is an example of derived data.&lt;/p&gt;

&lt;p&gt;I recommend persisting &lt;strong&gt;only&lt;/strong&gt; raw data in your state. It helps reduces state bloat and makes it easier to reason about what data is important in your app. All other derived data should be created using functions that accept that raw data from the state return back the information you need.&lt;/p&gt;

&lt;p&gt;Before adding something new to the state object, ask yourself this question, “Can I create this from data that already exists in the state?”  If the answer is “yes”, then create that data with a function. If the answer is “no”, then you may have a good case to add this data to the state.  You may be surprised over time how often the answer is “yes.”&lt;/p&gt;

&lt;h5 id=&quot;prefer-redux-state-over-react-state&quot;&gt;Prefer Redux state over React state&lt;/h5&gt;

&lt;p&gt;React comes with &lt;a href=&quot;https://facebook.github.io/react/docs/react-component.html#state&quot;&gt;its own system for managing state&lt;/a&gt; inside of components. In a Redux app, though, prefer to use Redux’s state for the majority of your app data and inter-component communication. It is overall much easier to reason about your app when there is one accepted way for components to set and access state, especially if you are working within a team.&lt;/p&gt;

&lt;p&gt;Note that there are reasonable exceptions to this guideline. It can be beneficial for complex UI components to persist local properties using React component state, especially when those properties aren’t globally important to the app. When doing this, just try to keep that React state management localized to that component. Using two separate state systems too much, especially for inter-component communication, is likely to cause confusion for the developer after you.&lt;/p&gt;

&lt;h3 id=&quot;actions&quot;&gt;Actions&lt;/h3&gt;

&lt;h5 id=&quot;standardize-action-payloads&quot;&gt;Standardize action payloads&lt;/h5&gt;

&lt;p&gt;When working with a team, having a standard object shape for your actions is very helpful. Doing so reduces &lt;a href=&quot;http://bikeshed.org/&quot;&gt;bikeshedding&lt;/a&gt; and creates maintainable and testable code. I highly recommend adopting some kind of standard with your team. I use the &lt;a href=&quot;https://github.com/acdlite/flux-standard-action&quot;&gt;Flux Standard Action spec&lt;/a&gt; because it is straightforward and simple to understand. But whatever you use, make sure it’s consistent and easy to work with.&lt;/p&gt;

&lt;h5 id=&quot;ensure-action-creators-are-composable&quot;&gt;Ensure action creators are composable&lt;/h5&gt;

&lt;p&gt;Many example apps and tutorials I run across use simple action creator functions when teaching Redux concepts. This is great for illustrating a point, but real world apps are complex. It’s inevitable that you will need to compose higher-level complex actions, preferably from existing action creators you have already written.&lt;/p&gt;

&lt;p&gt;Start a habit of making sure all your action creator functions are composable in some way. It’s a simple rule that really pays off when you need it. I personally wrap each action creator in a &lt;a href=&quot;https://github.com/then/promise&quot;&gt;promise&lt;/a&gt; so they can be easily chained together using the &lt;code class=&quot;highlighter-rouge&quot;&gt;then&lt;/code&gt; function.&lt;/p&gt;

&lt;h3 id=&quot;component-architecture&quot;&gt;Component Architecture&lt;/h3&gt;

&lt;h5 id=&quot;containers--presentational-components&quot;&gt;Containers &amp;amp; presentational components&lt;/h5&gt;

&lt;p&gt;The most useful concept I’ve come across for building stable and easily maintainable Redux apps is the &lt;a href=&quot;http://redux.js.org/docs/basics/UsageWithReact.html#presentational-and-container-components&quot;&gt;container &amp;amp; presentational component&lt;/a&gt; paradigm as described by Dan Abramov in the official Redux documentation. I will not dive into it here as the docs already do a great job at explaining the concept with great examples. But understanding this paradigm may be one of the most useful things you can learn about in Redux land.  It is very difficult to maintain and iterate on an app of even moderate complexity without it. Learn it well.&lt;/p&gt;

&lt;h5 id=&quot;use-intermediary-containers&quot;&gt;Use intermediary containers&lt;/h5&gt;

&lt;p&gt;While the container / presentational component paradigm works, it’s not always clear when containers should be introduced. I’ve seen (and written) apps with a single top-level container that fetches the whole world and then passes down everything to its component’s children and their children’s children. This results in props ‘passing through’ multiple components before they are ever even used. As your app grows, this becomes an annoying problem as even simple changes, like renaming props, involves changing many other non-related components. Definitely a code smell that something is not right.&lt;/p&gt;

&lt;p&gt;Instead, create containers when you notice multiple props ‘passing through’ multiple components. There is no need to pass props from one end to the other when a container in the middle can access the state and create those props for you. Intermediary containers also have added benefits, such as encapsulating sections of your component tree making their children easier to maintain and test. Don’t be afraid to use them if the situation calls for it.&lt;/p&gt;

&lt;h3 id=&quot;there-are-no-rules&quot;&gt;There are No Rules&lt;/h3&gt;

&lt;p&gt;All the guidelines I’ve listed are just patterns I’ve found worth repeating. However, do not consider any these points as the &lt;em&gt;only&lt;/em&gt; way to do things. After all, one of biggest advantages of Redux is its free form structure, so know when you should ‘break’ the rules and try something new.&lt;/p&gt;
</description>
        <pubDate>Tue, 20 Jun 2017 00:00:00 -0500</pubDate>
        <link>http://joeellis.la/redux-architecture/</link>
        <guid isPermaLink="true">http://joeellis.la/redux-architecture/</guid>
        
        <category>react</category>
        
        <category>redux</category>
        
        
      </item>
    
      <item>
        <title>Connect to Running Elixir Applications with IEx Remote Shell</title>
        <description>&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;code class=&quot;highlighter-rouge&quot;&gt;--remsh&lt;/code&gt; has security issues you should be aware of when using it on your local machine. Please be sure to read the &lt;strong&gt;security section&lt;/strong&gt; at the bottom of this article.&lt;/p&gt;

&lt;p&gt;Recently I learned about a fun option built into IEx called &lt;code class=&quot;highlighter-rouge&quot;&gt;--remsh&lt;/code&gt; or “remote shell”. It creates an IEx shell in the context of an Elixir node, allowing you to debug and reproduce issues inside a running application! Also note that despite the name, &lt;code class=&quot;highlighter-rouge&quot;&gt;--remsh&lt;/code&gt; can connect IEx to either local or remote nodes / applications. Here’s an example of how it works:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;&lt;span class=&quot;c&quot;&gt;# when running Elixir app on a server called example.com...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt; &lt;/span&gt;iex --name &lt;span class=&quot;s2&quot;&gt;&quot;joe@iex_test&quot;&lt;/span&gt; --cookie secret_cookie --remsh &lt;span class=&quot;s2&quot;&gt;&quot;node@example.com&quot;&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;iex(node@example.com)1&amp;gt; &lt;/span&gt;Node.list
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;:joe@iex_test]&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;--remsh&lt;/code&gt; works in combination with two other options, &lt;code class=&quot;highlighter-rouge&quot;&gt;--name&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;--cookie&lt;/code&gt;. The first is required and creates a named IEx shell to identify yourself to other nodes in the cluster. The second is not required but usually needed - it’s the &lt;a href=&quot;http://erlang.org/doc/reference_manual/distributed.html#id88372&quot;&gt;the security cookie&lt;/a&gt; needed to access the node (often found under your home directory at &lt;code class=&quot;highlighter-rouge&quot;&gt;~/.erlang.cookie&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;You could also wrap this up in a simple bash script to make it even easier to use:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;reiex&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;staging&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;; &lt;span class=&quot;k&quot;&gt;then
  &lt;/span&gt;iex --name &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;whoami&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@reiex&quot;&lt;/span&gt; --cookie secret_cookie --remsh &lt;span class=&quot;s2&quot;&gt;&quot;node@staging.example.com&quot;&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;prod&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;; &lt;span class=&quot;k&quot;&gt;then
  &lt;/span&gt;iex --name &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;whoami&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@reiex&quot;&lt;/span&gt; --cookie secret_cookie --remsh &lt;span class=&quot;s2&quot;&gt;&quot;node@prod.example.com&quot;&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;else
  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Environment not found!&quot;&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Just run &lt;code class=&quot;highlighter-rouge&quot;&gt;reiex &amp;lt;environment name&amp;gt;&lt;/code&gt; to connect to your staging or production servers.&lt;/p&gt;

&lt;h4 id=&quot;security-issues&quot;&gt;Security Issues&lt;/h4&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;--remsh&lt;/code&gt; has security implications you should be aware of before using it. Remember - Elixir / Erlang nodes have complete access to all other nodes in a given cluster. So when you connect from your laptop via &lt;code class=&quot;highlighter-rouge&quot;&gt;--remsh&lt;/code&gt;, other nodes in that cluster &lt;strong&gt;can now access and make RPC calls on your local workstation&lt;/strong&gt;. If you connect to a remote node that has been compromised from your local machine, your private files (even private SSH keys) would be up for grabs.&lt;/p&gt;

&lt;p&gt;If you plan to do any serious work with remsh, I recommend reading &lt;a href=&quot;https://broot.ca/erlang-remsh-is-dangerous&quot;&gt;Alex Weber’s writeup&lt;/a&gt; which goes in further detail about this issue. Alex also offers us a more secure alternative: create an SSH connection to your remote machine first and &lt;code class=&quot;highlighter-rouge&quot;&gt;remsh&lt;/code&gt; locally to the node from there. This can at least prevent node RPC calls from being made against your workstation as well as establish a more secure connection between your machine and the node.&lt;/p&gt;

&lt;p&gt;Happy &lt;code class=&quot;highlighter-rouge&quot;&gt;--remsh&lt;/code&gt;-ing everyone!&lt;/p&gt;
</description>
        <pubDate>Sat, 10 Jun 2017 00:00:00 -0500</pubDate>
        <link>http://joeellis.la/iex-remsh-shells/</link>
        <guid isPermaLink="true">http://joeellis.la/iex-remsh-shells/</guid>
        
        <category>elixir</category>
        
        
      </item>
    
      <item>
        <title>Streaming Data with Ecto</title>
        <description>&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: if you are not familiar with Elixir streams, you should &lt;a href=&quot;http://elixir-lang.org/getting-started/enumerables-and-streams.html#streams&quot;&gt;read the documentation&lt;/a&gt; on streams first or this post may be hard to follow.&lt;/p&gt;

&lt;p&gt;It’s a given that in your programming career, you will need to fetch a dataset and run some set of operations on it. With small datasets, you can often get away with fetching the records at once and doing what you need to do. But if you’re trying to fetch a large dataset, you will run into some common problems. It’s &lt;strong&gt;very&lt;/strong&gt; slow, creates a blocking database connection, and most database libraries will load the dataset into memory which can slow down or kill your server.&lt;/p&gt;

&lt;p&gt;Fortunately, Ecto &lt;a href=&quot;https://hexdocs.pm/ecto/Ecto.Repo.html#c:stream/2&quot;&gt;has a fantastic tool&lt;/a&gt; for handling this situation, &lt;a href=&quot;https://hexdocs.pm/ecto/Ecto.Repo.html#c:stream/2&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Repo.stream/2&lt;/code&gt;&lt;/a&gt;. &lt;code class=&quot;highlighter-rouge&quot;&gt;Repo.stream/2&lt;/code&gt; avoids fetching everything at once and instead fetches data in iterative cycles, performing operations on each record along the way.&lt;/p&gt;

&lt;p&gt;In this example, we will work through a typical app feature: exporting database records as CSV files. It’s a useful feature and is a good example to show how versatile Elixir streams can be.&lt;/p&gt;

&lt;p&gt;First, let’s look at some code:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elixir&quot; data-lang=&quot;elixir&quot;&gt;&lt;span class=&quot;k&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;UserExporter&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;@columns&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inserted_at&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;updated_at&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sd&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/tmp/users.csv&quot;&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;Repo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transaction&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Repo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stream&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse_line&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CSV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;encode&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;into&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stream!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:utf8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parse_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# order our data to match our column order&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;@columns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;amp;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Here we have a module with one public function, &lt;code class=&quot;highlighter-rouge&quot;&gt;export/1&lt;/code&gt;, that accepts an Ecto query and returns back a file stream.&lt;/p&gt;

&lt;p&gt;At the top, we declare a &lt;code class=&quot;highlighter-rouge&quot;&gt;@columns&lt;/code&gt; module attribute to define the user data to show in our file. We’ll need to refer to this &lt;code class=&quot;highlighter-rouge&quot;&gt;@columns&lt;/code&gt; list later to reorder our data for the CSV file.&lt;/p&gt;

&lt;p&gt;Next is the meat of the exporter. The first thing we do is open up a database transaction. This is a good idea when iterating over a large dataset and also a requirement when using &lt;code class=&quot;highlighter-rouge&quot;&gt;Repo.stream/2&lt;/code&gt; with either a MySQL or Postgres. All work using &lt;code class=&quot;highlighter-rouge&quot;&gt;Repo.stream/2&lt;/code&gt; needs to take place inside a transaction.&lt;/p&gt;

&lt;p&gt;Having opened a transaction, we pass our query to &lt;code class=&quot;highlighter-rouge&quot;&gt;Repo.stream/2&lt;/code&gt; which creates the initial stream. Remember, this only creates the stream; streams are lazily evaluated, so we haven’t starting making any calls to the database yet.&lt;/p&gt;

&lt;p&gt;We then pass the stream to a &lt;code class=&quot;highlighter-rouge&quot;&gt;Stream.map&lt;/code&gt; function which will fetch and transform data from each user struct into a list of user data matching the order of our columns.&lt;/p&gt;

&lt;p&gt;Using the wonderful &lt;a href=&quot;https://github.com/beatrichartz/csv&quot;&gt;CSV library&lt;/a&gt;, we then pass the stream to the &lt;code class=&quot;highlighter-rouge&quot;&gt;CSV.encode/1&lt;/code&gt; function to do all the hard work. &lt;code class=&quot;highlighter-rouge&quot;&gt;CSV.encode/1&lt;/code&gt; both accepts and return streams, so it fits right into our pipeline setup here.&lt;/p&gt;

&lt;p&gt;Finally, a call to &lt;code class=&quot;highlighter-rouge&quot;&gt;Enum.into&lt;/code&gt; runs the whole stream, and the actual database calls start. By default, &lt;code class=&quot;highlighter-rouge&quot;&gt;Repo.stream/2&lt;/code&gt; pulls down 500 records at a time, and each is parsed and written to a file at &lt;code class=&quot;highlighter-rouge&quot;&gt;tmp/users.csv&lt;/code&gt;. Lastly, our function returns a file stream that points to our newly created file.  We can use this new stream to do more file-based operations or start some other stream-based work, like uploading the file to S3, etc.&lt;/p&gt;

&lt;p&gt;As a test, we can see the results of our CSV in an iex console:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elixir&quot; data-lang=&quot;elixir&quot;&gt;&lt;span class=&quot;n&quot;&gt;iex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;UserExporter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;line_or_bytes:&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;modes:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:encoding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:utf8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:binary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;path:&lt;/span&gt; &lt;span class=&quot;sd&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/tmp/users.csv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;raw:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;iex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#=&amp;gt; &quot;1,joe@example.com,2017-04-27 21:57:42.972524,2017-05-20 18:48:16.235083\r\n2,jane@example.com,2017-04-27 18:36:32.053556,2017-04-27 18:36:32.065434\r\n3,jill@example.com,2017-04-27 18:37:43.503567,2017-04-27 18:37:43.503575\r\n&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Pretty neat, eh? Using streams, we created a simple and ordered pipeline to efficiently fetch data, transform it, and write it into a file - 500 records at a time. &lt;code class=&quot;highlighter-rouge&quot;&gt;Repo.stream/2&lt;/code&gt; is a very handy tool dealing with large datasets, I highly recommend you give it a try if you haven’t heard of it before.&lt;/p&gt;

</description>
        <pubDate>Sun, 21 May 2017 00:00:00 -0500</pubDate>
        <link>http://joeellis.la/streaming-with-ecto/</link>
        <guid isPermaLink="true">http://joeellis.la/streaming-with-ecto/</guid>
        
        <category>elixir</category>
        
        <category>ecto</category>
        
        
      </item>
    
      <item>
        <title>Nested Modules and Auto-Aliasing in Elixir</title>
        <description>&lt;p&gt;Recently, I was coding a module to create CSV files from database records when I came across an unexpected error in Elixir. Using the &lt;a href=&quot;https://github.com/beatrichartz/csv&quot;&gt;CSV&lt;/a&gt; library, I wrote something like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elixir&quot; data-lang=&quot;elixir&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# mix.exs&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deps&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sd&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;~&amp;gt; 1.4.2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# csv.ex&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CSV&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;CSV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I expected &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo.CSV.export(file)&lt;/code&gt; to trigger the &lt;code class=&quot;highlighter-rouge&quot;&gt;CSV&lt;/code&gt; library to encode the file I passed into the function.  To my surprise, the app came back with this error:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elixir&quot; data-lang=&quot;elixir&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;UndefinedFunctionError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CSV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;encode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;undefined&lt;/span&gt;
&lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;private&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This error kind of makes sense. I’m calling a function on a &lt;code class=&quot;highlighter-rouge&quot;&gt;CSV&lt;/code&gt; module, and I &lt;em&gt;just&lt;/em&gt; defined a named &lt;code class=&quot;highlighter-rouge&quot;&gt;CSV&lt;/code&gt; module right above it. Naturally, the app should be confused as to which &lt;code class=&quot;highlighter-rouge&quot;&gt;CSV&lt;/code&gt; module I’m referring to. But I really didn’t want to change my module’s name, so how can I tell the app which module I want to use?&lt;/p&gt;

&lt;p&gt;After some experimenting, I found something even more interesting - switching from a nested module to a single namespaced module made it work:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elixir&quot; data-lang=&quot;elixir&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# csv.ex&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CSV&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;CSV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;What?? I thought nested modules and namespaced modules compiled to the same thing? Shouldn’t the first example also compile to &lt;code class=&quot;highlighter-rouge&quot;&gt;Foo.CSV&lt;/code&gt;? And why does the second example not throw the same error as the first one?&lt;/p&gt;

&lt;h3 id=&quot;why-this-happens&quot;&gt;Why this happens&lt;/h3&gt;

&lt;p&gt;After asking around, I found this behavior puzzled other Elixir developers as well. Thankfully, &lt;a href=&quot;http://github.com/bryanjos&quot;&gt;Bryan Joseph&lt;/a&gt; helped me figure out what was happening and also pointed me to part in &lt;a href=&quot;https://hexdocs.pm/elixir/Kernel.html#defmodule/2-nesting&quot;&gt;the docs&lt;/a&gt; that explains this behavior.&lt;/p&gt;

&lt;p&gt;While compiling, when Elixir reaches a nested module, it creates an “auto-alias” for that nested module.  In other words, this code:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elixir&quot; data-lang=&quot;elixir&quot;&gt;&lt;span class=&quot;k&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CSV&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;CSV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;actually compiles to something more like this behind the scenes:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elixir&quot; data-lang=&quot;elixir&quot;&gt;&lt;span class=&quot;k&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CSV&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;CSV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CSV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;as:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CSV&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This is why my original example threw an error - when &lt;code class=&quot;highlighter-rouge&quot;&gt;CSV.encode(file)&lt;/code&gt; is called, the auto-alias directs the app to instead look for an &lt;code class=&quot;highlighter-rouge&quot;&gt;encode/1&lt;/code&gt; function on the &lt;code class=&quot;highlighter-rouge&quot;&gt;Elixir.Foo.CSV&lt;/code&gt; module.&lt;/p&gt;

&lt;p&gt;Yet, when modules are not nested, the compiler does not create an auto-alias, so there is no confusion about which &lt;code class=&quot;highlighter-rouge&quot;&gt;CSV&lt;/code&gt; module I’m trying to reference.&lt;/p&gt;

&lt;p&gt;So if you are hitting a similar error with nested modules, consider either changing the name or switching over to a namespaced module instead.&lt;/p&gt;
</description>
        <pubDate>Wed, 10 May 2017 00:00:00 -0500</pubDate>
        <link>http://joeellis.la/elixir-nested-modules/</link>
        <guid isPermaLink="true">http://joeellis.la/elixir-nested-modules/</guid>
        
        <category>elixir</category>
        
        
      </item>
    
  </channel>
</rss>
