<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Michael Jones</title>
    <description>The latest articles on DEV Community by Michael Jones (@michaeljones).</description>
    <link>https://dev.to/michaeljones</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F19434%2F09dfb736-92df-4c41-8c92-6cc445406336.jpg</url>
      <title>DEV Community: Michael Jones</title>
      <link>https://dev.to/michaeljones</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/michaeljones"/>
    <language>en</language>
    <item>
      <title>Adding a custom watcher to Phoenix</title>
      <dc:creator>Michael Jones</dc:creator>
      <pubDate>Wed, 29 Apr 2020 17:47:48 +0000</pubDate>
      <link>https://dev.to/contact-stack/adding-a-custom-watcher-to-phoenix-1e10</link>
      <guid>https://dev.to/contact-stack/adding-a-custom-watcher-to-phoenix-1e10</guid>
      <description>&lt;p&gt;At &lt;a href="http://www.contact-stack.com"&gt;Contact-Stack&lt;/a&gt;, we've recently added &lt;a href="https://elm-lang.org/"&gt;Elm&lt;/a&gt; to one of our &lt;a href="http://www.phoenixframework.org/"&gt;Phoenix&lt;/a&gt; projects. Elm is a fantastic language with a clear syntax, functional design, immutable data and a helpful compiler. &lt;/p&gt;

&lt;p&gt;For reasons of mild personal preference, we opted to not use &lt;a href="https://github.com/elm-community/elm-webpack-loader"&gt;elm-webpack-loader&lt;/a&gt; when integrating the Elm code with the current Javascript setup that we already have from Phoenix. Though ideally, we would still like the experience we have when we edit the Javascript, ie. it is rebuilt and Phoenix reloads the current browser with the newly built assets.&lt;/p&gt;

&lt;p&gt;Unfortunately for us the Elm compiler doesn't have a 'watch' mode so we can't rely on that. We need a separate process to run the Elm compiler whenever there is a change. I cannot find it now but I've seen comments from &lt;a href="https://twitter.com/rtfeldman"&gt;Richard Feldman&lt;/a&gt;, a prominent member of the Elm community, suggesting that the &lt;a href="https://github.com/paulmillr/chokidar"&gt;Chokidar&lt;/a&gt; project can be used to set up a simple watcher which runs the Elm compiler. Chokidar is a node project that does a great job of wrapping some of the node standard library functionality to provide a robust watcher. It is used by a number of high profile node projects, including &lt;a href="https://webpack.js.org/"&gt;Webpack&lt;/a&gt;, to provide file watching functionality.&lt;/p&gt;

&lt;p&gt;For reference, the exact build command that I would like run is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;elm make src/Main.elm --output=../priv/static/js/elm.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From within the &lt;code&gt;assets&lt;/code&gt; directory in the standard Phoenix project layout.&lt;/p&gt;

&lt;p&gt;Now to start, we might consider adding the &lt;a href="https://github.com/kimmobrunfeldt/chokidar-cli"&gt;&lt;code&gt;chokidar-cli&lt;/code&gt;&lt;/a&gt; which allows you to set up watchers with a simple command. We can add chokidar-cli with &lt;code&gt;yarn add -D chokidar&lt;/code&gt; and then run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chokidar "**/*.elm" -i node_modules -c "elm make src/Main.elm --output=../priv/static/js/elm.js"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we run that in the &lt;code&gt;assets&lt;/code&gt; directory it works great so maybe that's a good start. We add &lt;code&gt;-i node_modules&lt;/code&gt; as for some reason chokidar starts tracking some files in &lt;code&gt;node_modules&lt;/code&gt; too and we don't need it to. &lt;/p&gt;

&lt;p&gt;How do we go about adding it to Phoenix? If we look in the &lt;code&gt;config/dev.exs&lt;/code&gt; we see a block that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;config :contact_stack, ContactStackWeb.Endpoint,                                                                                                           
  http: [port: 4000],                                                                                                                                      
  debug_errors: true,                                                                                                                                      
  code_reloader: true,                                                                                                                                     
  check_origin: false,                                                                                                                                     
  watchers: [                                                                                                                                              
    node: [                                                                                                                                                
      "node_modules/webpack/bin/webpack.js",                                                                                                               
      "--mode",                                                                                                                                            
      "development",                                                                                                                                       
      "--watch-stdin",                                                                                                                                     
      cd: Path.expand("../assets", __DIR__)                                                                                                                
    ]                                                                                                                                                      
  ]                                                                                                                                                        
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The relevant entry, as you might guess, is the &lt;code&gt;watchers&lt;/code&gt; list. This is a list of key-value pairs that each provide a program and a set of arguments for Phoenix to run as part of its watcher functionality. So in this case, it is going to run &lt;code&gt;node&lt;/code&gt; with that list of arguments which will result in it running &lt;code&gt;webpack&lt;/code&gt; in development mode. The last part is to ask Phoenix to run it in the &lt;code&gt;assets&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;So we could try to extend this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;   watchers: [
     node: [
       "node_modules/webpack/bin/webpack.js",
       "--mode",
       "development",
       "--watch-stdin",
       cd: Path.expand("../assets", __DIR__)
&lt;span class="gi"&gt;+    ],
+    node: [
+      "node_modules/.bin/chokidar",
+      "**/*.elm",
+      "-i",
+      "node_modules",
+      "-c",
+      "elm make src/Main.elm --output=../priv/static/js/elm.js",
+      cd: Path.expand("../assets", __DIR__)
&lt;/span&gt;     ]
   ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And actually, this seems to work great. We run this and, every time we save an Elm file, Phoenix runs the Elm compiler with the command we've provided. &lt;/p&gt;

&lt;p&gt;Unfortunately, if we kill the Phoenix server and check the running processes on our machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;^C
BREAK: (a)bort (A)bort with dump (c)ontinue (p)roc info (i)nfo
       (l)oaded (v)ersion (k)ill (D)b-tables (d)istribution
$ ps -ef | grep chokidar
michael  17499     1  0 16:16 ?        00:00:00 /home/michael/.nave/installed/12.14.0/bin/node node_modules/.bin/chokidar "**/*.elm" -c "elm make src/Main.elm --output=../priv/static/js/elm.js"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see that the Chokidar process is still running. This is not great. We want to be able to freely restart our Phoenix dev server as often as we like without building up a back log of Chokidar processes which are all watching our Elm files and each running the Elm compiler on every change.&lt;/p&gt;

&lt;p&gt;So why is this happening? Well, I'm guessing that Phoenix uses &lt;a href="https://hexdocs.pm/elixir/Port.html"&gt;Elixir's Port functionality&lt;/a&gt; to run the watcher subprocesses and Elixir's Ports functionality as a &lt;a href="https://hexdocs.pm/elixir/Port.html#module-zombie-operating-system-processes"&gt;big warning about zombie processes&lt;/a&gt; that are left over when the main Elixir system process has stopped. It seems that Elixir doesn't, or perhaps can't, proactivity kill subprocesses that it has started when closing down. Rather it relies on those subprocesses noticing that their standard input has been closed and exiting themselves. I haven't come across this mechanism outside of Elixir but it might well be common. And if we glance up at the &lt;code&gt;webpack&lt;/code&gt; watcher config again we'll see that they're using a &lt;a href="https://webpack.js.org/api/cli/#watch-options"&gt;&lt;code&gt;--watch-stdin&lt;/code&gt;&lt;/a&gt; flag. Coincidence? Probably not.&lt;/p&gt;

&lt;p&gt;Unfortunately, &lt;code&gt;chokidar-cli&lt;/code&gt; doesn't have a &lt;code&gt;--watch-stdin&lt;/code&gt; flag nor any search results for &lt;code&gt;stdin&lt;/code&gt; &lt;a href="https://github.com/kimmobrunfeldt/chokidar-cli/search?q=stdin&amp;amp;type=Code"&gt;in the code&lt;/a&gt; so it seems like we can't rely on that. &lt;/p&gt;

&lt;p&gt;But &lt;code&gt;webpack&lt;/code&gt; is written in Javascript running on node so it must be possible and the main &lt;a href="https://www.npmjs.com/package/chokidar"&gt;&lt;code&gt;chokidar&lt;/code&gt; package&lt;/a&gt; is a library that allows you to easily access the file-watching functionality.&lt;/p&gt;

&lt;p&gt;If we go splunking through the webpack code looking for references to standard input then we come across &lt;a href="https://github.com/webpack/webpack-cli/blob/145c552d1ace3303607fe4d204106fe9437e24a0/packages/webpack-cli/lib/utils/Compiler.js#L176-L181"&gt;these lines&lt;/a&gt; in the webpack-cli project. Now I don't perfectly understand what is going on here but it seems that it is listening for a &lt;code&gt;'end'&lt;/code&gt; event on the standard input stream and using &lt;code&gt;process.exit()&lt;/code&gt; to close the whole program if it happens. That seems to fit with what Elixir's Ports expect.&lt;/p&gt;

&lt;p&gt;If we combine that with some relatively basic &lt;code&gt;chokidar&lt;/code&gt; library usage as they outline in the README then we get something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chokidar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chokidar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;execSync&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;child_process&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Exit the process when standard input closes due to:&lt;/span&gt;
&lt;span class="c1"&gt;//   https://hexdocs.pm/elixir/1.10.2/Port.html#module-zombie-operating-system-processes&lt;/span&gt;
&lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;standard input end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Set up chokidar to watch all elm files and rebuild the elm app ignoring process errors&lt;/span&gt;
&lt;span class="nx"&gt;chokidar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;**/*.elm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ignored&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node_modules&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;all&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;execSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./node_modules/.bin/elm make src/Main.elm --output=../priv/static/js/elm.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if we save it in a file called &lt;code&gt;assets/watch-elm.js&lt;/code&gt;. And then we change our &lt;code&gt;config/dev.exs&lt;/code&gt; config to read:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    node: [
      "./watch-elm.js",
       cd: Path.expand("../assets", __DIR__)
    ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can run &lt;code&gt;mix phx.server&lt;/code&gt; and see that not only does the Elm compiler get run correctly on changes but when we kill our dev server the watcher process dies too. Success!&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Adding new watcher processes to Phoenix is relatively easy in some ways but this matter of watching standard-input is a bit confusing and is probably handled quite differently in different languages.&lt;/p&gt;

&lt;h1&gt;
  
  
  Notes
&lt;/h1&gt;

&lt;p&gt;The Phoenix documentation does provide a helper bash script that you can use to wrap an executable and which does the 'listening for standard input to close' for you. I have used that successfully when having to run a subprocess during Elixir tests but I wasn't unable to get it working on first try in this situation. Possibly the complexities of the extra escaping of the command line arguments got the better of me. I'm not sure.      &lt;/p&gt;

</description>
      <category>elixir</category>
      <category>phoenix</category>
      <category>node</category>
      <category>chokidar</category>
    </item>
    <item>
      <title>Mixing Gleam &amp; Elixir</title>
      <dc:creator>Michael Jones</dc:creator>
      <pubDate>Fri, 20 Mar 2020 12:33:26 +0000</pubDate>
      <link>https://dev.to/contact-stack/mixing-gleam-elixir-3fe3</link>
      <guid>https://dev.to/contact-stack/mixing-gleam-elixir-3fe3</guid>
      <description>&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: This was very much an initial exploration. There is now an official project with a &lt;a href="https://github.com/gleam-lang/mix_gleam/"&gt;mix_gleam&lt;/a&gt; plugin. Please check that out!&lt;/p&gt;




&lt;p&gt;At &lt;a href="https://www.contact-stack.com"&gt;Contact Stack&lt;/a&gt;, we've chosen Elixir &amp;amp; Phoenix as the core of our tech stack and we're happy with the choice. But... we do miss having a really good type system and a helpful compiler (like with &lt;a href="https://elm-lang.org/"&gt;Elm&lt;/a&gt;) to guide us through development.&lt;/p&gt;

&lt;p&gt;Elixir targets the BEAM virtual machine and there are few statically typed languages that target the BEAM as well. Here we're going to look into a simple set up for integrating &lt;a href="https://gleam.run/"&gt;Gleam&lt;/a&gt; into your Elixir project using a &lt;a href="https://hexdocs.pm/mix/1.10.2/Mix.Task.Compiler.html"&gt;Mix Compiler Task&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Gleam is a statically typed functional programming language which uses a syntax that is more approachable to, say, Javascript, C or Java programmers than some ML flavoured languages. The Gleam compiler is written in Rust and outputs Erlang source files which can then be compiled to BEAM by the Erlang compiler.&lt;/p&gt;

&lt;p&gt;Mix is a multi-functional build tool for Elixir projects. It can manage dependencies, run tests, execute tasks and compile your Elixir code. It also supports compiling erlang files by default and can be extended with Mix Compiler Tasks to compile other languages. We're going to create a Mix Compiler Task for Gleam so that Mix can use the Gleam compiler to build Gleam files.&lt;/p&gt;

&lt;p&gt;You can try the steps below yourself and check out the demonstration repository: &lt;a href="https://github.com/michaeljones/gleam-phoenix-mix"&gt;https://github.com/michaeljones/gleam-phoenix-mix&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Here we go!&lt;/p&gt;

&lt;p&gt;We're assuming you have the &lt;code&gt;gleam&lt;/code&gt; compiler available in your environment to execute. Run this to check.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gleam --version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don't, then you can follow the &lt;a href="https://gleam.run/getting-started/installing-gleam.html"&gt;installation instructions&lt;/a&gt; over in the Gleam docs.&lt;/p&gt;

&lt;p&gt;Now we're going to go through setting up a &lt;a href="http://www.phoenixframework.org/"&gt;Phoenix&lt;/a&gt; project from scratch. We won't go into the details but just so we have a clear base to work with.&lt;/p&gt;

&lt;p&gt;Run the following commands to set up an empty phoenix project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir gleam-phoenix-mix
cd gleam-phoenix-mix
mix phx.new . --app my_app
mix ecto.create
cd assets 
npm install
cd ..
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to create our mix compiler task. We put this in a &lt;code&gt;lib/mix/tasks/compile&lt;/code&gt; directory to reflect the module name that we need to give it which is &lt;code&gt;Mix.Tasks.Compile.Gleam&lt;/code&gt;. So we create our new directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir -p lib/mix/tasks/compile/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And add the follow code in a file called &lt;code&gt;lib/mix/tasks/compile/gleam.ex&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Mix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Tasks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Compile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Gleam&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Mix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Compiler&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"gleam"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="ss"&gt;:ok&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All this is really doing is running the gleam compiler directly with the 'build' argument to get it to build all the gleam files it can find. &lt;/p&gt;

&lt;p&gt;Run the elixir compiler so that we compile that mix task before we try to use it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mix compile.elixir
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now to make gleam happy we need to set up a &lt;code&gt;.toml&lt;/code&gt; config file for it. So we create a file called &lt;code&gt;gleam.toml&lt;/code&gt; in the root of the project with the follow content.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name = "my_app"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we create a &lt;code&gt;src&lt;/code&gt; directory for the gleam files. The Gleam compiler expects the gleam files to be in a &lt;code&gt;src&lt;/code&gt; directory. This is different to elixir/mix which expects them in the &lt;code&gt;lib&lt;/code&gt; directory by default. Elixir/mix can be configured but Gleam can't at the time of writing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir src
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then create our Gleam module by making a file called &lt;code&gt;src/hello_world.gleam&lt;/code&gt; with the following code in it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pub fn hello() {
  "Hello, from gleam!"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we make the following change to the &lt;code&gt;mix.exs&lt;/code&gt; file to add our compiler task to the list of compilers being run when you do &lt;code&gt;mix compile&lt;/code&gt; and to make sure mix's erlang compiler looks in the &lt;code&gt;gen&lt;/code&gt; folder&lt;br&gt;
for erlang files. The &lt;code&gt;gleam&lt;/code&gt; compiler compiles &lt;code&gt;.gleam&lt;/code&gt; files from the &lt;code&gt;src&lt;/code&gt; folder into &lt;code&gt;.erl&lt;/code&gt; files in the &lt;code&gt;gen&lt;/code&gt; folder so we need the erlang compiler to find them there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;       elixir: "~&amp;gt; 1.5",
       elixirc_paths: elixirc_paths(Mix.env()),
&lt;span class="gi"&gt;+      erlc_paths: ["src", "gen"],
&lt;/span&gt;&lt;span class="gd"&gt;-      compilers: [:phoenix, :gettext] ++ Mix.compilers(),
&lt;/span&gt;&lt;span class="gi"&gt;+      compilers: [:phoenix, :gettext, :gleam] ++ Mix.compilers(),
&lt;/span&gt;       start_permanent: Mix.env() == :prod,
       aliases: aliases(),
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I assume that Mix runs the compilers from the start to the end of the list. So we want the &lt;code&gt;:gleam&lt;/code&gt; entry to be before the &lt;code&gt;:erlang&lt;/code&gt; entry that's in &lt;code&gt;Mix.compilers()&lt;/code&gt; so that the Erlang compiler will run after the Gleam compile and compile the Erlang files that the Gleam compiler creates.&lt;/p&gt;

&lt;p&gt;As a simple demonstration of interop between Elixir &amp;amp; the compiled gleam files, make the following change to &lt;code&gt;lib/my_app_web/controllers/page_controller.ex&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; defmodule MyAppWeb.PageController do
   use MyAppWeb, :controller

   def index(conn, _params) do
&lt;span class="gd"&gt;-    render(conn, "index.html")
&lt;/span&gt;&lt;span class="gi"&gt;+    render(conn, "index.html", title: :hello_world.hello())
&lt;/span&gt;   end
 end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works because Gleam modules are compiled to Erlang modules and, in Elixir, Erlang modules are accessible via the atom that is their name.&lt;/p&gt;

&lt;p&gt;And then we make the following change to &lt;code&gt;lib/my_app_web/templates/page/index.html.eex&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; &amp;lt;section class="phx-hero"&amp;gt;
&lt;span class="gd"&gt;-  &amp;lt;h1&amp;gt;&amp;lt;%= gettext "Welcome to %{name}!", name: "Phoenix" %&amp;gt;&amp;lt;/h1&amp;gt;
&lt;/span&gt;&lt;span class="gi"&gt;+  &amp;lt;h1&amp;gt;&amp;lt;%= @title %&amp;gt;&amp;lt;/h1&amp;gt;
&lt;/span&gt;   &amp;lt;p&amp;gt;A productive web framework that&amp;lt;br/&amp;gt;does not compromise speed or maintainability.&amp;lt;/p&amp;gt;
 &amp;lt;/section&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally we can run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mix compile
mix phx.server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And load &lt;code&gt;http://localhost:4000&lt;/code&gt; in our browser to see "Hello, from gleam!" in the centre of the standard Phoenix welcome page.&lt;/p&gt;

&lt;p&gt;Remember, if you're curious you can check out the final result at this demonstration repo on Github: &lt;a href="https://github.com/michaeljones/gleam-phoenix-mix"&gt;https://github.com/michaeljones/gleam-phoenix-mix&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;It is exciting to see that it isn't too hard to incorporate Gleam code into an Elixir project using Mix. It is great that Mix is built with this kind of extensibility in mind. There are definitely more questions to answer and rough edges to smooth out but it is exciting to have an avenue to using a strongly typed language on the BEAM.  &lt;/p&gt;

</description>
      <category>elixir</category>
      <category>gleam</category>
      <category>mix</category>
    </item>
    <item>
      <title>Introducing Dialyzer &amp; type-specs to an Elixir Project</title>
      <dc:creator>Michael Jones</dc:creator>
      <pubDate>Tue, 17 Mar 2020 14:35:45 +0000</pubDate>
      <link>https://dev.to/contact-stack/introducing-dialyzer-type-specs-to-an-elixir-project-312d</link>
      <guid>https://dev.to/contact-stack/introducing-dialyzer-type-specs-to-an-elixir-project-312d</guid>
      <description>&lt;p&gt;At &lt;a href="https://www.contact-stack.com"&gt;Contact Stack&lt;/a&gt; we use &lt;a href="https://elixir-lang.org/"&gt;Elixir&lt;/a&gt; &amp;amp; &lt;a href="http://www.phoenixframework.org/"&gt;Phoenix&lt;/a&gt; as the core of the backend. I am fascinated by the design &amp;amp; power of the BEAM, the virtual machine that Elixir targets, and I want to learn more about it. I also love strongly &amp;amp; statically typed languages, particularly due to my recent history working with &lt;a href="https://elm-lang.org/"&gt;Elm&lt;/a&gt; which has a great type system and a helpful compiler.&lt;/p&gt;

&lt;p&gt;Elixir is dynamically typed language which leaves me a little nervous about the long term future of the project. I really like types and I find them incredibly useful when the code base grows larger and you're returning to parts of it that you haven't looked at in a while. Often I can't remember exactly the kind of data that I'm passing around and strong types really help with that understanding. Plus I've grown to love having a compiler guide you through the code base when doing refactoring as it points out all the places you need to change.&lt;/p&gt;

&lt;p&gt;One way to bring a little more type safety to Elixir projects is to use &lt;a href="http://erlang.org/doc/man/dialyzer.html"&gt;Dialyzer&lt;/a&gt;. Dialyzer performs static analysis on your code and tries to warn you about anything that is almost certainly going to fail. It can be helped by providing type annotations and it also derives its own from inspecting the code. It isn't as good as a solid type system but it is better than nothing.&lt;/p&gt;

&lt;p&gt;So, how do we go about introducing Dialzyer and type annotations through out our codebase? Here are the steps that I've followed for Contact Stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Dialyzer
&lt;/h2&gt;

&lt;p&gt;Maybe a bit of an obvious first step! We can use the &lt;a href="https://github.com/jeremyjh/dialyxir"&gt;dialxyir&lt;/a&gt; project to add a dialyzer task to mix. At the time of writing that involves adding this line to your &lt;code&gt;mix.exs&lt;/code&gt; &lt;code&gt;deps&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{:dialyxir, "~&amp;gt; 1.0", only: [:dev], runtime: false},
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Run Dialyzer &amp;amp; Fix issues
&lt;/h2&gt;

&lt;p&gt;We can now run dialyzer with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mix dialyzer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the command line. This can take a long time as dialyzer analyses and creates &lt;code&gt;PLT&lt;/code&gt; caches for all the Elixir &amp;amp; Erlang standard libraries. Fortunately it is much quicker on that next run though still not as fast a many compilers out there.&lt;/p&gt;

&lt;p&gt;The errors can honestly be quite hard to understand. I would recommend googling as much as possible. I've learned a few of things though that I'll share.&lt;/p&gt;

&lt;p&gt;Firstly, in my projects I've had to config dialyzer to make it away of &lt;code&gt;ex_unit&lt;/code&gt; and &lt;code&gt;mix&lt;/code&gt; otherwise it issues warnings. I suspect I only needed to add &lt;code&gt;mix&lt;/code&gt; because I have a custom mix task. You can configure dialyzer by adding the following to your the &lt;code&gt;project&lt;/code&gt; config in mix.exs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dialyzer: [plt_add_apps: [:ex_unit, :mix], ignore_warnings: "config/dialyzer.ignore"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we add &lt;code&gt;ex_unit&lt;/code&gt; &amp;amp; &lt;code&gt;mix&lt;/code&gt; and also indicate an ignore file.&lt;/p&gt;

&lt;p&gt;Secondly, some errors don't seem to be clearly fixable by code that is under our control. In which case we want to add those errors to the ignore file. If you run dialyzer with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mix dialyzer --format dialyzer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you can copy the start or all of the error that you want to ignore onto a new line in the ignore file we've just set up and dialyzer will skip it on future runs. &lt;/p&gt;

&lt;p&gt;Thirdly, if you get a warning that says that some function has &lt;code&gt;no local return&lt;/code&gt; then it normally means there is another warning within the function itself that makes dialzyer think that the function will never work. You can skip the 'no local return' warning at look at the next one in the output so see what the issue might be.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduce Credo
&lt;/h2&gt;

&lt;p&gt;Credo is a linting tool for Elixir that can warn if you don't provide a type annotation on a function. Part of our goal here is to make sure that we provide type annotations for all the functions in our code base. This helps Dialyzer understand our intent and is self-documenting which helps ourselves and others on the code base.&lt;/p&gt;

&lt;p&gt;So we add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{:credo, "~&amp;gt; 1.2", only: [:dev, :test], runtime: false}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To our &lt;code&gt;mix.exs&lt;/code&gt; &lt;code&gt;deps&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure &amp;amp; Fix Credo Warnings
&lt;/h2&gt;

&lt;p&gt;Credo checks for a lot of different things. It does not check for type annotations by default and we're going to enable that but first, as long as they are manageable, fix any current warnings that Credo has in place. You can do this by changing your code or suppressing some of Credo's warnings. I have the following config for my preferences:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%{
  configs: [
    %{
      name: "default",
      files: %{
        included: ["lib/"],
        excluded: []
      },
      checks: [
        {Credo.Check.Design.AliasUsage, false},
        {Credo.Check.Readability.AliasOrder, false}
      ]
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Extend Credo Rules to Include Type Annotations
&lt;/h2&gt;

&lt;p&gt;We extend our config to include the &lt;code&gt;Specs&lt;/code&gt; check by adding this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;      checks: [
        {Credo.Check.Design.AliasUsage, false},
        {Credo.Check.Readability.AliasOrder, false}
&lt;span class="gi"&gt;+       {Credo.Check.Readability.Specs, []}
&lt;/span&gt;      ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Silence the Huge Number of Credo Warnings
&lt;/h2&gt;

&lt;p&gt;Now we can run credo again to get a sense of the number of missing type annotations. If you have a small number then go ahead and try to add them now. If, like me, you have something like 200 warnings or more then you might prefer to silence them for now and attend to them gradually.&lt;/p&gt;

&lt;p&gt;To do this we can run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mix credo --strict | grep lib | cut -d " " -f 8 | cut -d ":" -f 1 | sort -u | xargs sed -i '1s/^/# credo:disable-for-this-file Credo.Check.Readability.Specs\n/'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is going to add &lt;code&gt;# credo:disable-for-this-file Credo.Check.Readability.Specs&lt;/code&gt; to the top of every file in your codebase that has missing type annotations.&lt;/p&gt;

&lt;p&gt;When we run &lt;code&gt;mix credo --strict&lt;/code&gt; again we should see a clean sheet. Now you can go into the files at your leisure and remove the comment at the top and add type annotations. Credo will warn about any that are missing and warn by default for any new files we add to the system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;I worry that dialyzer is not very useful in comparison to the type systems &amp;amp; compilers that I have used before but it feels good to be trying to make the code as tight and error free as possible. I am at the beginning of this journey so I can't really report on whether it is worth it long term but we'll see. &lt;/p&gt;

</description>
      <category>elixir</category>
      <category>dialyzer</category>
      <category>types</category>
    </item>
    <item>
      <title>Moving from elm-validate to elm-verify</title>
      <dc:creator>Michael Jones</dc:creator>
      <pubDate>Fri, 16 Feb 2018 14:50:06 +0000</pubDate>
      <link>https://dev.to/zaptic/moving-from-elm-validate-to-elm-verify--19pc</link>
      <guid>https://dev.to/zaptic/moving-from-elm-validate-to-elm-verify--19pc</guid>
      <description>&lt;p&gt;At &lt;a href="https://www.zaptic.com"&gt;Zaptic&lt;/a&gt;, we use Elm for our business administration website to allow customers to configure the processes that they want to carry out. The site contains a number of different forms for adding &amp;amp; editing various entities in our system. All of these forms benefit from client side validation before the data is submitted to our servers. &lt;/p&gt;

&lt;p&gt;When we started writing our Elm code, the recommended approach to form validation was the &lt;a href="https://github.com/rtfeldman/elm-validate"&gt;rtfeldman/elm-validate&lt;/a&gt; library. For a form backed by a data structure like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;ProductGroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="kt"&gt;Mode&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;selectedProducts&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Dict&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt; &lt;span class="kt"&gt;Product&lt;/span&gt;

    &lt;span class="c1"&gt;-- Used for storing validation errors&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We might have a validation function like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;validateProductGroup&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Validator&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;ProductGroup&lt;/span&gt;
&lt;span class="n"&gt;validateProductGroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Validate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ifBlank&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;NameField&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please provide a name"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ifNothing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;ModeField&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please select a mode"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selectedProducts&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ifEmptyDict&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;ProductsField&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please select at least one product"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This checks that the &lt;code&gt;name&lt;/code&gt; field isn't blank, that a &lt;code&gt;mode&lt;/code&gt; has been selected and that some products have been selected. The result is a list of errors corresponding to the criteria that aren't met:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;validateProductGroup&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="kt"&gt;MultiMode&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;selectedProducts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Dict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;-- [(NameField, "Please provide a name"), (ProductsField, "Please select at least one product")] &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The list can be saved in the model and then the view searches the list for relevant entries when displaying the various input fields in order to display the error state to the user if there is one.&lt;/p&gt;

&lt;p&gt;With this in mind, we can create our form to send a &lt;code&gt;SaveProductGroup&lt;/code&gt; message when the form is submitted and we can handle this message in our &lt;code&gt;update&lt;/code&gt; function with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;        &lt;span class="kt"&gt;SaveProductGroup&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;validateProductGroup&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;productGroupData&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
                &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;productGroupData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;productGroupData&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Nothing&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
                        &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;postProductGroup&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;productGroupData&lt;/span&gt;
                          &lt;span class="p"&gt;]&lt;/span&gt;

                &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;productGroupData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;productGroupData&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we run the &lt;code&gt;validateProductGroup&lt;/code&gt; function and if we get an empty list then we know that there are no errors and we can post our data to the server using &lt;code&gt;postProductGroup&lt;/code&gt; that will create a command for us to carry out the necessary HTTP request.&lt;/p&gt;

&lt;p&gt;The problem we encounter is that the &lt;code&gt;postProductGroup&lt;/code&gt; needs to encode the &lt;code&gt;ProductGroup&lt;/code&gt; structure to JSON and when it does that it has to navigate the data that we've given it. We know that the &lt;code&gt;mode&lt;/code&gt; value is a &lt;code&gt;Just&lt;/code&gt; because we've validated it but the convert to JSON function cannot take any shortcuts because Elm doesn't let us (which is a good thing!) We can try to write the encoding function like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;encodeProductGroup&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ProductGroup&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Encode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Value&lt;/span&gt;
&lt;span class="n"&gt;encodeProductGroup&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;selectedProducts&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Encode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Encode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mode"&lt;/span&gt;
          &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
                &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="n"&gt;encodeMode&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;

                &lt;span class="kt"&gt;Nothing&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="c1"&gt;-- Help! What do we put here?&lt;/span&gt;
          &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;products"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Encode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;encodeProduct&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt; &lt;span class="kt"&gt;Dict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="n"&gt;selectedProducts&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see from the comment, it is hard to figure out what to write there. Elm gives us some escape hatches like &lt;code&gt;Debug.crash&lt;/code&gt; but that feels wrong. We could not include the &lt;code&gt;mode&lt;/code&gt; key if we don't have a value but then we're knowingly allowing a code path that sends incorrect data to the server.&lt;/p&gt;

&lt;p&gt;So what do we do? Well, we need to recognise that this function shouldn't be making this decision. It shouldn't have to deal with the &lt;code&gt;Maybe&lt;/code&gt;, especially when we have already validated that it has a &lt;code&gt;Just&lt;/code&gt; value.&lt;/p&gt;

&lt;p&gt;So we create a new type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;ProductGroupUploadData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Mode&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;selectedProducts&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="kt"&gt;Product&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which is the same data that we're interested in but with the &lt;code&gt;Maybe&lt;/code&gt; resolved to an actual value and the &lt;code&gt;Dict.values&lt;/code&gt; transformation already applied to &lt;code&gt;selectedProducts&lt;/code&gt;. If we change our &lt;code&gt;encodeProductGroup&lt;/code&gt; function to expect this type then the implementation becomes trivial:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;encodeProductGroup&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ProductGroupUploadData&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Encode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Value&lt;/span&gt;
&lt;span class="n"&gt;encodeProductGroup&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;selectedProducts&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Encode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Encode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mode"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encodeMode&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;products"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Encode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;encodeProduct&lt;/span&gt; &lt;span class="n"&gt;selectedProducts&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But how do we convert our &lt;code&gt;ProductGroup&lt;/code&gt; to &lt;code&gt;ProductGroupUploadData&lt;/code&gt;? This is where the &lt;a href="https://github.com/stoeffel/elm-verify"&gt;stoeffel/elm-verify&lt;/a&gt; library comes in. It allows us to both validate our data and transform it to another structure in the same operation. It does this by using the &lt;code&gt;Result&lt;/code&gt; type to allow it to report validation errors, if any are encountered, or the new data structure for us to use. And it does this with a &lt;code&gt;Json.Decode.Pipeline&lt;/code&gt;-like interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;validateProductGroup&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ProductGroup&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Result&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="kt"&gt;ProductGroupUploadData&lt;/span&gt;
&lt;span class="n"&gt;validateProductGroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt;
        &lt;span class="n"&gt;validateProducts&lt;/span&gt; &lt;span class="n"&gt;productGroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kt"&gt;Dict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isEmpty&lt;/span&gt; &lt;span class="n"&gt;productGroup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selectedProducts&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
                &lt;span class="kt"&gt;Err&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;ProductsField&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please select at least one product"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;
                &lt;span class="kt"&gt;Ok&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Dict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="n"&gt;productGroup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selectedProducts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="kt"&gt;V&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="kt"&gt;ProductGroupUploadData&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;V&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;verify&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Verify&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;notBlank&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;NameField&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please provide a name"&lt;/span&gt; &lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;V&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;verify&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Maybe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Verify&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isJust&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;ConfigField&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please select a mode"&lt;/span&gt; &lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;V&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;custom&lt;/span&gt; &lt;span class="n"&gt;validateProducts&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;V&lt;/code&gt; is the result of &lt;code&gt;import Verify as V&lt;/code&gt;. You can see the "pipeline" approach that might be familiar from &lt;code&gt;Json.Decode.Pipeline&lt;/code&gt;. That means we're using &lt;code&gt;ProductGroupUploadData&lt;/code&gt; as a constructor and each step of the pipeline is providing an argument to complete the data. We use &lt;code&gt;String.Verify&lt;/code&gt; to check that the &lt;code&gt;name&lt;/code&gt; isn't blank and &lt;code&gt;Maybe.Verify&lt;/code&gt; to check that the &lt;code&gt;mode&lt;/code&gt; is specified. Then we use &lt;code&gt;Verify.custom&lt;/code&gt; to provide a slight more complex check for the &lt;code&gt;selectedProducts&lt;/code&gt;. &lt;code&gt;Verify.custom&lt;/code&gt; allows us to write a function that takes the incoming data and returns a &lt;a href="http://package.elm-lang.org/packages/elm-lang/core/latest/Result"&gt;&lt;code&gt;Result&lt;/code&gt;&lt;/a&gt; with either an &lt;code&gt;Err&lt;/code&gt; with an array of errors or an &lt;code&gt;Ok&lt;/code&gt; with the valid value. We use it to not only check that the dictionary is empty but also extract just the values from the dictionary. We don't have to run &lt;code&gt;Dict.values&lt;/code&gt; here, we could also do that in the &lt;code&gt;encodeProductGroup&lt;/code&gt; function when generating the JSON but I have a personal preference for the &lt;code&gt;UploadData&lt;/code&gt; to match the JSON closely if possible.&lt;/p&gt;

&lt;p&gt;With that in place, we can change our &lt;code&gt;SaveProductGroup&lt;/code&gt; case in our &lt;code&gt;update&lt;/code&gt; function to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;        &lt;span class="kt"&gt;SaveProductGroup&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;validateProductGroup&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;productGroupData&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
                &lt;span class="kt"&gt;Ok&lt;/span&gt; &lt;span class="n"&gt;uploadData&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;productGroupData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;productGroupData&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Nothing&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
                        &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;postProductGroup&lt;/span&gt; &lt;span class="n"&gt;uploadData&lt;/span&gt;
                          &lt;span class="p"&gt;]&lt;/span&gt;

                &lt;span class="kt"&gt;Err&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;productGroupData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;productGroupData&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which means that the &lt;code&gt;postProductGroup&lt;/code&gt; is given a nice &lt;code&gt;ProductGroupUploadData&lt;/code&gt; record and no longer has to worry about the &lt;code&gt;Maybe&lt;/code&gt; type.&lt;/p&gt;

&lt;p&gt;Prior to using the &lt;code&gt;elm-verify&lt;/code&gt; library, we used a separate function to convert between the types and we only called &lt;code&gt;postProductGroup&lt;/code&gt; if both the validation &amp;amp; the conversion functions were successful. The conversion function always felt a little strange though and switching to &lt;code&gt;elm-verify&lt;/code&gt; cleans that up nicely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Further note&lt;/strong&gt;: Whilst the &lt;code&gt;elm-verify&lt;/code&gt; interface is similar to &lt;code&gt;Json.Decode.Pipeline&lt;/code&gt; it isn't quite the same. It has a version of &lt;code&gt;andThen&lt;/code&gt; but it doesn't provide some ways to combine operations like &lt;code&gt;Json.Decode.oneOf&lt;/code&gt; or helper functions like &lt;code&gt;Json.Decode.map&lt;/code&gt;. This is partly to keep the interface simple and partly because it is always manipulating &lt;code&gt;Result&lt;/code&gt; values so with a bit of thought you can use helper functions like &lt;code&gt;Result.map&lt;/code&gt; quite easily. &lt;/p&gt;

&lt;p&gt;I only include this as originally sought to use &lt;code&gt;elm-verify&lt;/code&gt; exactly as I would &lt;code&gt;Json.Decode.Pipeline&lt;/code&gt; and ended up writing a bunch of unnecessary functions in order to allow me to do just that. I should have spent more time understanding the types in the interface and working with those.&lt;/p&gt;

</description>
      <category>elm</category>
      <category>validation</category>
    </item>
    <item>
      <title>Right clicks &amp; Elm</title>
      <dc:creator>Michael Jones</dc:creator>
      <pubDate>Fri, 08 Dec 2017 14:04:08 +0000</pubDate>
      <link>https://dev.to/zaptic/right-clicks--elm-2ll</link>
      <guid>https://dev.to/zaptic/right-clicks--elm-2ll</guid>
      <description>&lt;p&gt;At &lt;a href="https://www.zaptic.com"&gt;Zaptic&lt;/a&gt;, we allow our customers to create a flow chart describing the business processes that they would like to complete. We draw the flow chart in SVG using a pan &amp;amp; zoom interface to make it easy to look around. We track mouse down, mouse drag &amp;amp; mouse up events to manage this UI.&lt;/p&gt;

&lt;p&gt;Unfortunately, in development, the experience was a little annoying as every time we loaded the browser's inspector on an element the &lt;code&gt;mousedown&lt;/code&gt; of the right-click would be tracked by the UI but the &lt;code&gt;mouseup&lt;/code&gt; would be over the context menu and so would not register. The result? The UI gets left in "pan" mode and attempts to follow your mouse everywhere even though you don't have any mouse buttons pressed any more.&lt;/p&gt;

&lt;p&gt;To overcome this, we needed to look into tracking which mouse button was being used so that we could ignore the right-clicks. We are using the &lt;a href="http://package.elm-lang.org/packages/elm-lang/mouse/latest"&gt;elm-lang/mouse&lt;/a&gt; package which includes a &lt;code&gt;position&lt;/code&gt; decoder that can be used for &lt;code&gt;mousedown&lt;/code&gt; events. That decoder only provides the mouse location as &lt;code&gt;pageX&lt;/code&gt; and &lt;code&gt;pageY&lt;/code&gt;, it doesn't provide any information about which button was pressed so we need to go a little deeper.&lt;/p&gt;

&lt;p&gt;If we take a look at the &lt;a href="https://github.com/elm-lang/mouse/blob/1.0.1/src/Mouse.elm"&gt;source code&lt;/a&gt; for the module we can see that the position decoder is doing the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Decoder&lt;/span&gt; &lt;span class="kt"&gt;Position&lt;/span&gt;
&lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="kt"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map2&lt;/span&gt; &lt;span class="kt"&gt;Position&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pageX"&lt;/span&gt; &lt;span class="kt"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pageY"&lt;/span&gt; &lt;span class="kt"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is looking for the &lt;code&gt;pageX&lt;/code&gt; and &lt;code&gt;pageY&lt;/code&gt; properties, as we suspected, and it expects them to be integers. If we look at the lovely &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Events/mousedown"&gt;MDN documentation&lt;/a&gt; for a mousedown event we can see that it has lots of other properties including a integer &lt;code&gt;button&lt;/code&gt; property which contains some indication of which button is being used. We can set up our own type &amp;amp; decoder as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;MouseClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;pageX&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pageY&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;button&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;mouseClickDecoder&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Decoder&lt;/span&gt; &lt;span class="kt"&gt;PFM&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;MouseClick&lt;/span&gt;
&lt;span class="n"&gt;mouseClickDecoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map3&lt;/span&gt; &lt;span class="kt"&gt;PFM&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;MouseClick&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pageX"&lt;/span&gt; &lt;span class="kt"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pageY"&lt;/span&gt; &lt;span class="kt"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button"&lt;/span&gt; &lt;span class="kt"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;And adapt our event handler to use it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;DragStart&lt;/span&gt; &lt;span class="kt"&gt;MouseClick&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;onMouseDown&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Attribute&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
&lt;span class="n"&gt;onMouseDown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mousedown"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="kt"&gt;DragStart&lt;/span&gt; &lt;span class="n"&gt;mouseClickDecoder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in our &lt;code&gt;update&lt;/code&gt; function that is going to handle the &lt;code&gt;DragStart&lt;/code&gt; message we can check the value of &lt;code&gt;button&lt;/code&gt; and ignore right-clicks. There are some inconsistencies between browsers on how different buttons are represented as integers which you can read about on &lt;a href="https://www.quirksmode.org/js/events_properties.html#button"&gt;quirksmode&lt;/a&gt;. Fortunately they all align for the 'right-click' which is represented as &lt;code&gt;2&lt;/code&gt;. So we can ignore that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="kt"&gt;DragStart&lt;/span&gt; &lt;span class="n"&gt;click&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;-- Ignore right-clicks which are given value '2' by browsers&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt; &lt;span class="o"&gt;/=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt;
            &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pageX&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pageY&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;in&lt;/span&gt;
        &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;drag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And there we have it! We're free of the right-clicks and can happily jump into the browser dev-tools whenever we like.&lt;/p&gt;

&lt;p&gt;If you have any thoughts or advice, please let me know! Always happy to learn.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update&lt;/em&gt;: Checkout Ilias' comment below. It really cleans up the message handling in the update:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-        DragStart click -&amp;gt;
-            -- Ignore right-clicks which are given value '2' by browsers
-            if click.button /= 2 then
-                let
-                    pos =
-                        { x = click.pageX, y = click.pageY }
-                in
-                { model | drag = Just { start = pos, current = pos } } ! []
-            else
-                model ! []
&lt;/span&gt;&lt;span class="gi"&gt;+        DragStart pos -&amp;gt;
+            { model | drag = Just { start = pos, current = pos } } ! []
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>elm</category>
    </item>
    <item>
      <title>Django to Phoenix - Part 4: Sessions</title>
      <dc:creator>Michael Jones</dc:creator>
      <pubDate>Sun, 12 Nov 2017 16:24:22 +0000</pubDate>
      <link>https://dev.to/michaeljones/django-to-phoenix---part-4-sessions-e86</link>
      <guid>https://dev.to/michaeljones/django-to-phoenix---part-4-sessions-e86</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;I have a website built with &lt;a href="https://www.djangoproject.com/"&gt;Django&lt;/a&gt; and I would like to start writing parts of it in &lt;a href="http://phoenixframework.org/"&gt;Phoenix&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/michaeljones/django-to-phoenix-part-0-6ij"&gt;Part 0: Background&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/michaeljones/django-to-phoenix-part-1-56h"&gt;Part 1: Docker for Dev&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/michaeljones/django-to-phoenix---part-2-nginx-9kg"&gt;Part 2: Nginx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/michaeljones/django-to-phoenix---part-3-initial-phoenix-setup-4ma"&gt;Part 3: Initial Phoenix Setup&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;I am a beginner at Phoenix. I would like help &amp;amp; advice if I am doing something wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting Point
&lt;/h2&gt;

&lt;p&gt;In the last post we set up Phoenix inside our &lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt; container. We have &lt;a href="https://www.nginx.com/resources/wiki/"&gt;nginx&lt;/a&gt; splitting traffic between Phoenix &amp;amp; Django. All requests go to Django except for the temporary &lt;code&gt;/elixir&lt;/code&gt; path which we direct to Phoenix for experimenting.&lt;/p&gt;

&lt;p&gt;In this post, we're going to look at accessing the Django sessions information so that our Phoenix app can know if the user is logged in or not. We're going to have to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check the session cookie&lt;/li&gt;
&lt;li&gt;Check the sessions table in the database&lt;/li&gt;
&lt;li&gt;Add some information to the request data to indicate if there is a current user session or not.&lt;/li&gt;
&lt;li&gt;Displaying some indication of the session on the page.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Checking the Session Cookie
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://docs.djangoproject.com/en/1.11/topics/http/sessions"&gt;official documentation&lt;/a&gt; tells us that Django uses cookies for tracking the session IDs. If we log in to our Django app in Chrome, open up the &lt;a href="https://developers.google.com/web/tools/chrome-devtools/"&gt;inspector&lt;/a&gt;, go to the Application tab and then click on the &lt;code&gt;localhost&lt;/code&gt; entry under &lt;a href="https://developers.google.com/web/tools/chrome-devtools/manage-data/cookies"&gt;Cookies&lt;/a&gt;, then we can see a cookie called &lt;code&gt;sessionid&lt;/code&gt; which contains a string.&lt;/p&gt;

&lt;p&gt;I'm not cookie expert but my understanding is that any available cookies for the domain are automatically sent along with each standard web request. So when we navigate to &lt;code&gt;/elixir&lt;/code&gt; the browser will be sending the same cookies over.&lt;/p&gt;

&lt;p&gt;Phoenix is built on top of a layer called &lt;a href="https://hexdocs.pm/plug/readme.html"&gt;Plug&lt;/a&gt;. Looking at the &lt;a href="https://hexdocs.pm/plug/Plug.Conn.html#module-fetchable-fields"&gt;documentation for Plug&lt;/a&gt; we can see that the cookies are available as an Elixir &lt;a href="https://hexdocs.pm/elixir/Map.html"&gt;Map&lt;/a&gt; on the &lt;code&gt;conn&lt;/code&gt; data. This is the standard connection data that is fed to functions that are dealing with requests.&lt;/p&gt;

&lt;p&gt;In order to access that cookie we're going to set up a new Phoenix controller called &lt;code&gt;TimetableWeb.Session&lt;/code&gt;. To do this we create a file called &lt;code&gt;session.ex&lt;/code&gt; in the directory &lt;code&gt;lib/timetable_web/controllers/&lt;/code&gt; in our Phoenix project. It will have the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;TimetableWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Session&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Plug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Conn&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"sessionid"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"No sessionid found"&lt;/span&gt;
      &lt;span class="n"&gt;session_id&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Session ID: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c1"&gt;# Return conn&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is standard Plug format. For the moment, we don't need to do anything in the &lt;code&gt;init&lt;/code&gt; function but it still required. The &lt;code&gt;call&lt;/code&gt; function requires two arguments in order to receive the data from &lt;code&gt;init&lt;/code&gt; but we don't need it so we use a simple &lt;code&gt;_&lt;/code&gt; as the argument name.&lt;/p&gt;

&lt;p&gt;As for the rest of the Plug, we look into the &lt;code&gt;conn.cookies&lt;/code&gt; Map for the &lt;code&gt;sessionid&lt;/code&gt; entry and &lt;a href="http://elixir-lang.github.io/getting-started/pattern-matching.html"&gt;pattern match&lt;/a&gt; on the result. If we get &lt;code&gt;nil&lt;/code&gt; then we know we don't have an active session at the moment. If we get something other than &lt;code&gt;nil&lt;/code&gt; then we know we must have the session ID. For the moment, we print an appropriate message and return the &lt;code&gt;conn&lt;/code&gt; at the end of the function. In Elixir the result of the last expression is returned from the function. There is no need for an explicit &lt;code&gt;return&lt;/code&gt; keyword in that situation.&lt;/p&gt;

&lt;p&gt;Now we need to add our plug into the Phoenix request pipeline so that it can process incoming requests. To do this we edit our &lt;code&gt;lib/timetable_web/router.ex&lt;/code&gt; file with the following change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;   pipeline :browser do
     plug :accepts, ["html"]
     plug :fetch_session
     plug :fetch_flash
     plug :protect_from_forgery
     plug :put_secure_browser_headers
&lt;span class="gi"&gt;+    plug TimetableWeb.Session, repo: Timetable.Repo
&lt;/span&gt;   end

   pipeline :api do
     plug :accepts, ["json"]
   end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we are adding our plug to the end of the browser pipeline. We're not using the &lt;code&gt;api&lt;/code&gt; pipeline at the moment so we ignore that.&lt;/p&gt;

&lt;p&gt;Now, if we go to our Django app, log in and visit &lt;code&gt;/elixir&lt;/code&gt; then we see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[info] GET /elixir
Session ID: boj26dqs1v0okevigm2vnsu11gephx21
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our console output from the Phoenix app inside our Docker container.&lt;/p&gt;

&lt;p&gt;If we now go to our Django app, log out and visit &lt;code&gt;/elixir&lt;/code&gt;, then we see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[info] GET /elixir
No sessionid found
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we seem to be successfully getting the session cookie.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checking the Sessions Database Table
&lt;/h2&gt;

&lt;p&gt;Django doesn't store meaningful data in the session cookie. It is just an ID to access the data from the sessions table in the database. The &lt;a href="https://docs.djangoproject.com/en/1.11/topics/http/sessions/#technical-details"&gt;documentation&lt;/a&gt; tells us that the session data is stored in a table called &lt;code&gt;django_session&lt;/code&gt;. If we run &lt;code&gt;python manage.py dbshell&lt;/code&gt; and in the &lt;a href="https://www.postgresql.org/"&gt;Postgresql&lt;/a&gt; prompt run: &lt;code&gt;\d django_session&lt;/code&gt; then we get a description of the table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;            Table "public.django_session"
    Column    |           Type           | Modifiers 
-------------------+--------------------------+-----------
 session_key  | character varying(40)    | not null
 session_data | text                     | not null
 expire_date  | timestamp with time zone | not null
Indexes:
    "django_session_pkey" PRIMARY KEY, btree (session_key)
    "django_session_expire_date" btree (expire_date)
    "django_session_session_key_like" btree (session_key varchar_pattern_ops)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see that there are 3 columns. The &lt;code&gt;session_key&lt;/code&gt; most likely maps to our &lt;code&gt;session_id&lt;/code&gt;. The &lt;code&gt;session_data&lt;/code&gt; most likely includes the useful information about the session. Finally, there is an &lt;code&gt;expire_date&lt;/code&gt; to indicate when the session should be considered 'stale' and no longer honoured. We can also see that the &lt;code&gt;session_key&lt;/code&gt; field is the primary key for this table.&lt;/p&gt;

&lt;p&gt;We want to get to a position in which our Phoenix app can check this database table to retrieve the session information. Phoenix uses a layer called &lt;a href="https://hexdocs.pm/ecto/Ecto.html"&gt;Ecto&lt;/a&gt; to model database tables and talk to databases. We are going to use a &lt;a href="https://hexdocs.pm/mix/Mix.html"&gt;mix&lt;/a&gt; task to help generate Ecto schemas. The command we're going to run is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;mix phx.gen.schema Session sessions session_key:string session_data:string expire_date:date
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generates an Ecto schema that is almost correct for what we need. The problem is that it assumes that we're going to want to have an extra &lt;code&gt;id&lt;/code&gt; column to use as the primary key. This makes sense for a lot of tables but not our &lt;code&gt;django_session&lt;/code&gt; table where the primary key is the &lt;code&gt;session_key&lt;/code&gt; column. So we have to adjust the final schema to inform Ecto of our preferred primary key. Here is what it is going to look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Timetable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Session&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;
  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Changeset&lt;/span&gt;
  &lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="no"&gt;Timetable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Session&lt;/span&gt;

  &lt;span class="nv"&gt;@primary_key&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:session_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;autogenerate:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="s2"&gt;"django_session"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:session_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:expire_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:utc_datetime&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nv"&gt;@doc&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;(%&lt;/span&gt;&lt;span class="no"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:session_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:session_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:expire_date&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;validate_required&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="ss"&gt;:session_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:session_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:expire_date&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To check if this works, we can try it in the Elixir interactive shell: &lt;a href="https://hexdocs.pm/iex/IEx.html"&gt;iex&lt;/a&gt;. If we run &lt;code&gt;iex -S mix&lt;/code&gt; from Phoenix project root directory, then we can do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iex(1)&amp;gt; Timetable.Session |&amp;gt; Timetable.Repo.get_by(session_key: "boj26dqs1v0okevigm2vnsu11gephx21")                                                       
[debug] QUERY OK source="django_session" db=131.4ms                          
SELECT d0."session_key", d0."session_data", d0."expire_date" FROM "django_session" AS d0 WHERE (d0."session_key" = $1) ["boj26dqs1v0okevigm2vnsu11gephx21"]                                     
%Timetable.Session{__meta__: #Ecto.Schema.Metadata&amp;lt;:loaded, "django_session"&amp;gt;,                                                                            
 expire_date: #DateTime&amp;lt;2017-11-21 08:31:06.774119Z&amp;gt;,                        
 session_data: "ODNjNWFkMDJh....cl9pZCI6IjEifQ==",  
 session_key: "boj26dqs1v0okevigm2vnsu11gephx21"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see that we received a record with the session data that we're interested in.&lt;/p&gt;

&lt;p&gt;If we want to check the &lt;code&gt;expire_date&lt;/code&gt; as well we can do this using the Ecto query syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iex(1)&amp;gt; import Ecto.Query, only: [from: 2]
Ecto.Query
iex(2)&amp;gt; now = DateTime.utc_now()
#DateTime&amp;lt;2017-11-12 10:31:16.153448Z&amp;gt;
iex(3)&amp;gt; (from s in Timetable.Session, where: s.session_key == "boj26dqs1v0okevigm2vnsu11gephx21" and s.expire_date &amp;gt;= ^now) |&amp;gt; Timetable.Repo.one
[debug] QUERY OK source="django_session" db=24.0ms
SELECT d0."session_key", d0."session_data", d0."expire_date" FROM "django_session" AS d0 WHERE ((d0."session_key" = 'boj26dqs1v0okevigm2vnsu11gephx21') AND (d0."expire_date" &amp;gt;= $1)) [{{2017, 11, 12}, {10, 31, 16, 153448}}]
%Timetable.Session{__meta__: #Ecto.Schema.Metadata&amp;lt;:loaded, "django_session"&amp;gt;,
  expire_date: #DateTime&amp;lt;2017-11-21 08:31:06.774119Z&amp;gt;,
  session_data: "ODNjNWFkMDJh....cl9pZCI6IjEifQ==",
  session_key: "boj26dqs1v0okevigm2vnsu11gephx21"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And to check that it returns nothing when the session has expired we can do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iex(1)&amp;gt; import Ecto.Query, only: [from: 2]
Ecto.Query
iex(2)&amp;gt; {:ok, later, 0} = DateTime.from_iso8601("2017-11-22T00:00:00Z")                                                                            
{:ok, #DateTime&amp;lt;2017-11-22 00:00:00Z&amp;gt;, 0}
iex(3)&amp;gt; (from s in Timetable.Session, where: s.session_key == "boj26dqs1v0okevigm2vnsu11gephx21" and s.expire_date &amp;gt;= ^later) |&amp;gt; Timetable.Repo.one
[debug] QUERY OK source="django_session" db=2.9ms
SELECT d0."session_key", d0."session_data", d0."expire_date" FROM "django_session" AS d0 WHERE ((d0."session_key" = 'boj26dqs1v0okevigm2vnsu11gephx21') AND (d0."expire_date" &amp;gt;= $1)) [{{2017, 11, 22}, {0, 0, 0, 0}}]
nil
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We get back &lt;code&gt;nil&lt;/code&gt; when providing a date after the session &lt;code&gt;expire_date&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Session Information to the Request
&lt;/h2&gt;

&lt;p&gt;Now that we have a way to extract information from the session table, we can update our Session plug to check for a valid session. We're going to update it to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;TimetableWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Session&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Plug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Conn&lt;/span&gt;
  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;from:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Keyword&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetch!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:repo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc_now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"sessionid"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:logged_in&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;session_key&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="no"&gt;Timetable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;where:&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_key&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;session_key&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expire_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
          &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:logged_in&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:logged_in&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;We have done two main adjustments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;init&lt;/code&gt; function has been updated to look for and return the &lt;code&gt;:repo&lt;/code&gt; reference from the &lt;code&gt;opts&lt;/code&gt;. The &lt;code&gt;call&lt;/code&gt; function has been updated to receive the &lt;code&gt;repo&lt;/code&gt; as the second argument. &lt;/li&gt;
&lt;li&gt;If the &lt;code&gt;call&lt;/code&gt; function has found a session key in the cookie, we check the sessions table for that session. If the session is valid, then we return the &lt;code&gt;conn&lt;/code&gt; data with a new &lt;code&gt;logged_in&lt;/code&gt; field set to true. In both of the other cases where no valid cookie or session is found, we return the &lt;code&gt;conn&lt;/code&gt; with a &lt;code&gt;logged_in&lt;/code&gt; field set to false.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This isn't very advanced yet. A normal system would want to access the &lt;code&gt;session_data&lt;/code&gt; from the session table as well but we'll leave that for the next post.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Displaying Session Information on the Page
&lt;/h2&gt;

&lt;p&gt;For the final step, we want to display whether or not the user is logged in. We're going to keep it very basic for now and update the template for the Phoenix landing page which is &lt;code&gt;lib/timetable_web/templates/page/index.html.eex&lt;/code&gt;. We're going to make the follow change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; &amp;lt;div class="row marketing"&amp;gt;
&lt;span class="gi"&gt;+
+  &amp;lt;div&amp;gt;
+    &amp;lt;%= if @logged_in do %&amp;gt;
+      Logged In!
+    &amp;lt;% else %&amp;gt;
+      Not Logged In
+    &amp;lt;% end %&amp;gt;
+  &amp;lt;/div&amp;gt;
+
&lt;/span&gt;   &amp;lt;div class="col-lg-6"&amp;gt;
     &amp;lt;h4&amp;gt;Resources&amp;lt;/h4&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This uses &lt;a href="https://hexdocs.pm/eex/EEx.html"&gt;EEx&lt;/a&gt; - Embedded Elixir - to display different content depending on the value of &lt;code&gt;logged_in&lt;/code&gt;. As a field on &lt;code&gt;conn&lt;/code&gt;, the &lt;code&gt;logged_in&lt;/code&gt; value is accessible in our templates.  &lt;/p&gt;

&lt;p&gt;Now when we log in to our Django app and navigate to our &lt;code&gt;/elixir&lt;/code&gt; page, we're going to see &lt;code&gt;Logged In!&lt;/code&gt; displayed to us on the page. And if we log out of our Django app and come back to this page we see &lt;code&gt;Not Logged In&lt;/code&gt; instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We have our Phoenix app reading the session information from the Django sessions table. We are able to update the web page in a basic manner to reflect the logged-in status of the current user. &lt;/p&gt;

&lt;p&gt;In the next post, we'll extend this approach to also check the user table for the user name. &lt;/p&gt;

</description>
      <category>django</category>
      <category>phoenix</category>
    </item>
    <item>
      <title>Django to Phoenix - Part 3: Initial Phoenix Setup</title>
      <dc:creator>Michael Jones</dc:creator>
      <pubDate>Tue, 31 Oct 2017 20:41:03 +0000</pubDate>
      <link>https://dev.to/michaeljones/django-to-phoenix---part-3-initial-phoenix-setup-4ma</link>
      <guid>https://dev.to/michaeljones/django-to-phoenix---part-3-initial-phoenix-setup-4ma</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;I have a website built with &lt;a href="https://www.djangoproject.com/"&gt;Django&lt;/a&gt; and I would like to start writing parts of it in &lt;a href="http://phoenixframework.org/"&gt;Phoenix&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/michaeljones/django-to-phoenix-part-0-6ij"&gt;Part 0: Background&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/michaeljones/django-to-phoenix-part-1-56h"&gt;Part 1: Docker for Dev&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/michaeljones/django-to-phoenix---part-2-nginx-9kg"&gt;Part 2: Nginx&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Starting Point
&lt;/h2&gt;

&lt;p&gt;In the last section we added &lt;a href="https://www.nginx.com/resources/wiki/"&gt;nginx&lt;/a&gt; to our &lt;a href="https://docs.docker.com/engine/reference/builder/"&gt;Dockerfile&lt;/a&gt; so that we could introduce Phoenix into our container and use nginx to split incoming requests between Phoenix &amp;amp; Django depending on the URL path.&lt;/p&gt;

&lt;p&gt;Our next step is to get a basic Phoenix set up working and have it accessible via the Docker container. There are a few steps to this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install &lt;a href="https://elixir-lang.org/"&gt;Elixir&lt;/a&gt; &amp;amp; Phoenix&lt;/li&gt;
&lt;li&gt;Create a Phoenix project&lt;/li&gt;
&lt;li&gt;Add Elixir &amp;amp; Phoenix to our Docker image&lt;/li&gt;
&lt;li&gt;Add a nginx config section for our Phoenix app&lt;/li&gt;
&lt;li&gt;Get our Phoenix app running in the container&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Install Elixir &amp;amp; Phoenix
&lt;/h2&gt;

&lt;p&gt;In order to create a new Phoenix project and interact with it on our development machine, we need to install Elixir &amp;amp; Phoenix. We're not going to cover it in depth here but following the relevant install guides (&lt;a href="https://elixir-lang.org/install.html"&gt;Elixir&lt;/a&gt; &amp;amp; &lt;a href="https://hexdocs.pm/phoenix/installation.html#content"&gt;Phoenix&lt;/a&gt;) should get you set up. &lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Phoenix Project
&lt;/h2&gt;

&lt;p&gt;Phoenix comes with a &lt;a href="https://hexdocs.pm/phoenix/phoenix_mix_tasks.html#phoenix-specific-mix-tasks"&gt;mix task&lt;/a&gt; for creating a new Phoenix project. &lt;a href="https://hexdocs.pm/mix/Mix.html"&gt;Mix&lt;/a&gt; is a build tool &amp;amp; task runner for the Elixir ecosystem. So common jobs like creating a project, adding modules or running tests often have mix tasks to help you do them.&lt;/p&gt;

&lt;p&gt;In this case we're interested in &lt;code&gt;mix phx.new&lt;/code&gt; which creates a new project. We'd like the Phoenix project to live in a directory alongside the Django project code. Fortunately for me, the Django code is already in a &lt;code&gt;dancetimetable&lt;/code&gt; folder rather than directly in the project root, so we can create the Phoenix project in a folder called &lt;code&gt;timetable&lt;/code&gt; (naming is hard) in the project root as well. &lt;/p&gt;

&lt;p&gt;In order to do that we're going to run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mix phx.new timetable &lt;span class="nt"&gt;--no-brunch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use the &lt;code&gt;--no-brunch&lt;/code&gt; option because our Django project already uses &lt;a href="https://webpack.js.org/"&gt;Webpack&lt;/a&gt; for compiling the client side applications so we don't need &lt;a href="http://brunch.io/"&gt;Brunch&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The command asks us if we want to 'fetch and install dependencies' and we answer 'yes' because we do. Once it has finished installing &amp;amp; compiling, it prints the following helpful message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;We are all set! Go into your application by running:

    $ cd timetable

Then configure your database in config/dev.exs and run:

    $ mix ecto.create

Start your Phoenix app with:

    $ mix phx.server

You can also run your app inside IEx (Interactive Elixir) as:

    $ iex -S mix phx.server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But we need to change these steps for our set up. The &lt;code&gt;mix ecto.create&lt;/code&gt; command is responsible for creating the &lt;a href="https://www.postgresql.org/"&gt;Postgresql&lt;/a&gt; database for the Phoenix project but we want Phoenix to use the database that already exists for our Django app. So we're not going to run &lt;code&gt;mix ecto.create&lt;/code&gt;, instead we're going to edit &lt;code&gt;config/dev.exs&lt;/code&gt; to reference our development database. The change looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gi"&gt;+config :timetable, Timetable.Repo,
&lt;/span&gt;   adapter: Ecto.Adapters.Postgres,
   username: "postgres",
   password: "postgres",
&lt;span class="gd"&gt;-  database: "testing_dev",
-  hostname: "localhost",
&lt;/span&gt;&lt;span class="gi"&gt;+  database: "tangotimetable",
+  hostname: System.get_env("DOCKER_HOST") || "localhost",
&lt;/span&gt;   pool_size: 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We prepare ourselves by adding the same &lt;code&gt;DOCKER_HOST&lt;/code&gt; lookup that we have discussed in a &lt;a href="https://dev.to/michaeljones/django-to-phoenix-part-1-56h"&gt;previous post&lt;/a&gt; for getting our Django server to talk to our Postgres database from inside the Docker container.&lt;/p&gt;

&lt;p&gt;With that in place, we can run &lt;code&gt;mix phx.server&lt;/code&gt;. Once that is running we can visit &lt;code&gt;http://localhost:4000&lt;/code&gt; in our browser and see the default Phoenix new project landing page.&lt;/p&gt;

&lt;p&gt;The rest of this post is about getting to see that page again, but having it served from inside the Docker container. &lt;/p&gt;

&lt;h2&gt;
  
  
  Add Elixir &amp;amp; Phoenix to our Docker image
&lt;/h2&gt;

&lt;p&gt;To install Elixir &amp;amp; Phoenix into our Docker image, we use the &lt;a href="https://hub.docker.com/r/marcelocg/phoenix/~/dockerfile/"&gt;marcelocg/phoenix&lt;/a&gt; Dockerfile as reference. After some experimentation, the start of our Dockerfile now looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;
&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="s"&gt; ubuntu:14.04&lt;/span&gt;

&lt;span class="k"&gt;run &lt;/span&gt;apt-get update &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="k"&gt;run &lt;/span&gt;apt-get upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;span class="c"&gt;# (1) Updated - Add locales &amp;amp; inotify-tools&lt;/span&gt;
&lt;span class="k"&gt;run &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; git python python-pip make g++ wget curl libxml2-dev &lt;span class="se"&gt;\
&lt;/span&gt;        libxslt1-dev libjpeg-dev libenchant-dev libffi-dev supervisor &lt;span class="se"&gt;\
&lt;/span&gt;        libpq-dev python-dev &lt;span class="nb"&gt;realpath &lt;/span&gt;nginx apt-transport-https locales &lt;span class="se"&gt;\
&lt;/span&gt;        inotify-tools

&lt;span class="c"&gt;# (2) Set locale information&lt;/span&gt;
&lt;span class="k"&gt;run &lt;/span&gt;locale-gen en_US.UTF-8
&lt;span class="k"&gt;env&lt;/span&gt;&lt;span class="s"&gt; LANG en_US.UTF-8&lt;/span&gt;
&lt;span class="k"&gt;env&lt;/span&gt;&lt;span class="s"&gt; LANGUAGE en_US:en&lt;/span&gt;
&lt;span class="k"&gt;env&lt;/span&gt;&lt;span class="s"&gt; LC_ALL en_US.UTF-8&lt;/span&gt;

&lt;span class="c"&gt;# Install yarn as before (unchanged)&lt;/span&gt;
&lt;span class="k"&gt;run &lt;/span&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"deb https://dl.yarnpkg.com/debian/ stable main"&lt;/span&gt; | &lt;span class="nb"&gt;tee&lt;/span&gt; /etc/apt/sources.list.d/yarn.list

&lt;span class="c"&gt;# (3) Install erlang apt repository&lt;/span&gt;
&lt;span class="k"&gt;run &lt;/span&gt;wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; dpkg &lt;span class="nt"&gt;-i&lt;/span&gt; erlang-solutions_1.0_all.deb

&lt;span class="c"&gt;# (4) Update apt information &amp;amp; install erlang &amp;amp; elixir&lt;/span&gt;
&lt;span class="k"&gt;run &lt;/span&gt;apt-get update &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="k"&gt;run &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; yarn esl-erlang elixir

&lt;span class="c"&gt;# (5) Use mix to install Phoenix&lt;/span&gt;
&lt;span class="k"&gt;env&lt;/span&gt;&lt;span class="s"&gt; PHOENIX_VERSION 1.3.0&lt;/span&gt;
&lt;span class="k"&gt;run &lt;/span&gt;mix archive.install &lt;span class="nt"&gt;--force&lt;/span&gt; https://github.com/phoenixframework/archives/raw/master/phx_new-&lt;span class="nv"&gt;$PHOENIX_VERSION&lt;/span&gt;.ez

&lt;span class="c"&gt;# (6) Install hex &amp;amp; rebar locally&lt;/span&gt;
&lt;span class="k"&gt;run &lt;/span&gt;mix local.hex &lt;span class="nt"&gt;--force&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; mix local.rebar &lt;span class="nt"&gt;--force&lt;/span&gt;

&lt;span class="c"&gt;# Same as before from here on down&lt;/span&gt;
&lt;span class="k"&gt;add&lt;/span&gt;&lt;span class="s"&gt; https://raw.githubusercontent.com/isaacs/nave/master/nave.sh ./nave.sh&lt;/span&gt;

&lt;span class="k"&gt;run &lt;/span&gt;bash nave.sh usemain 6.9.1

&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Going through those changes one at a time:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We add &lt;code&gt;inotify-tools&lt;/code&gt; and &lt;code&gt;locales&lt;/code&gt; to the install apt-get install. Phoenix uses &lt;code&gt;inotify-tools&lt;/code&gt; to check for file changes in Linux which allows it to reload the development server when the code has changed. As for &lt;code&gt;locales&lt;/code&gt;, it seems that Erlang &amp;amp; Elixir like the Unix locale to be properly set before they are installed. I don't know why, but it complains if we don't do it.&lt;/li&gt;
&lt;li&gt;We set the locale information as shown in the &lt;a href="https://hub.docker.com/r/marcelocg/phoenix/~/dockerfile/"&gt;marcelocg/phoenix&lt;/a&gt; Dockerfile.&lt;/li&gt;
&lt;li&gt;Install the &lt;a href="https://www.erlang-solutions.com/"&gt;erlang-solutions&lt;/a&gt; apt repository package. This looks like we're installing a package, and we are, but the result of the package is to make our system aware of the erlang-solutions apt repository. This allows apt to install the latest packages from the erlang-solutions repository instead of the potentially-less-up-to-date Ubuntu repositories.&lt;/li&gt;
&lt;li&gt;Refresh apt's cache of information and install Erlang &amp;amp; Elixir. If you're unfamiliar with Erlang, it is the base environment upon with Elixir runs. &lt;/li&gt;
&lt;li&gt;Install Phoenix via the mix command. This installs Phoenix from the referenced archive file on &lt;a href="https://github.com/"&gt;Github&lt;/a&gt;. This is the recommended way to install Phoenix. &lt;/li&gt;
&lt;li&gt;We install hex &amp;amp; rebar locally. Hex is a package manager tool for Erlang &amp;amp; Elixir and rebar is a build tool. I do not know if these are necessary in our image, I haven't experimented with removing them yet.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now we can rebuild our Docker image and it is ready to run our Phoenix development server. The rebuild may take some time due to the nature of the changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add a nginx config section for our Phoenix app
&lt;/h2&gt;

&lt;p&gt;The change to the nginx config is quite simple. We already have a section for the Django app. We just want another one for the Phoenix app. At the moment all requests go to the Django app. As an experimental step, we're going to redirect requests for &lt;code&gt;/elixir&lt;/code&gt; to our Phoenix app. This is never going to make it to production, but we're still getting set up. &lt;/p&gt;

&lt;p&gt;So our change looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;    # server_name www.tangotimetable.com;
    server_name localhost;

+   location /elixir {
&lt;span class="gi"&gt;+
+       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+
+       proxy_set_header Host $http_host;
+
+       # we don't want nginx trying to do something clever with
+       # redirects, we set the Host: header above already.
+       proxy_redirect off;
+
+       proxy_pass http://localhost:4000;
+   }
+
&lt;/span&gt;    location / {

        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is identical to the Django section below except that we're redirecting to port 4000 instead of port 7000. I am not sure, but I believe it is important for this section to appear above the Django section in the file. This means that the Django section acts as a catch-all for any requests not sent to the Phoenix app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get our Phoenix app running in the container
&lt;/h2&gt;

&lt;p&gt;Now we're ready to get the Phoenix development server up and running in our container. We're going to follow a similar pattern to our Django app. We're going to add this section to our &lt;a href="http://supervisord.org/"&gt;supervisord&lt;/a&gt; configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; stderr_logfile=/dev/stderr
 stderr_logfile_maxbytes=0
&lt;span class="gi"&gt;+
+[program:phoenix]
+command=/src/dev/run-phoenix
+stdout_logfile=/dev/stdout
+stdout_logfile_maxbytes=0
+stderr_logfile=/dev/stderr
+stderr_logfile_maxbytes=0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we're going to add a &lt;code&gt;run-phoenix&lt;/code&gt; script which looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;cd&lt;/span&gt; /src/timetable

&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DOCKER_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;netstat &lt;span class="nt"&gt;-nr&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'^0\.0\.0\.0'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;

mix phx.server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See a &lt;a href="https://dev.to/michaeljones/django-to-phoenix-part-1-56h"&gt;previous post&lt;/a&gt; for discussion of the &lt;code&gt;DOCKER_HOST&lt;/code&gt; environment variable. It allows our Phoenix app to find the Postgres database which is on the host machine, not inside the container.&lt;/p&gt;

&lt;p&gt;I leave it as an exercise to make sure that this &lt;code&gt;run-phoenix&lt;/code&gt; file is accounted for in the Dockerfile and makes it in the Docker image in the correct place. It can be very confusing when a file isn't included in the Docker image when you expect it to be.&lt;/p&gt;

&lt;p&gt;The final change to make is to &lt;code&gt;mount&lt;/code&gt; the Phoenix app source folder into the Docker container at run time. This is because we haven't added the source code to the image. If we did, we'd have to rebuild the image on every change which would be painful.&lt;/p&gt;

&lt;p&gt;So we're going to change our &lt;code&gt;run-docker-dev&lt;/code&gt; script, from the &lt;a href="https://dev.to/michaeljones/django-to-phoenix-part-1-56h"&gt;first post&lt;/a&gt;, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;    -v `pwd`/logs/nginx:/var/log/nginx:rw \
    -v `pwd`/logs/cron:/var/log/cron:rw \
    -v `pwd`/dancetimetable:/src/dancetimetable:rw \
&lt;span class="gi"&gt;+   -v `pwd`/timetable:/src/timetable:rw \
&lt;/span&gt;    -e DANCETIMETABLE_SETTINGS=development \
    --rm \
    --interactive \
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when we run our Docker container and visit &lt;code&gt;http://localhost:8000&lt;/code&gt; we see the front page of our Django app but if we visit &lt;code&gt;http://localhost:8000/elixir&lt;/code&gt; we see the Phoenix new project landing page.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update 2017-11-12&lt;/em&gt;: I forgot to include a necessary change to the Phoenix router. We have to change the &lt;code&gt;timetable_web/router.ex&lt;/code&gt; file with the following diff:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;   scope "/", TimetableWeb do
     pipe_through :browser # Use the default browser stack

-    get "/", PageController, :index
&lt;span class="gi"&gt;+    get "/elixir", PageController, :index
&lt;/span&gt;   end

   # Other scopes may use custom stacks.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So that Phoenix is expecting to be asked about &lt;code&gt;/elixir&lt;/code&gt; and not &lt;code&gt;/&lt;/code&gt;. We could instead change nginx to strip the &lt;code&gt;/elixir&lt;/code&gt; from the route before it reaches Phoenix but that is not the eventual goal I have for the routes that Phoenix should handle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We now have our Django app &amp;amp; a new Phoenix app running side by side in the Docker container. We can make the Django app responsible for some URL paths and the Phoenix app responsible for others. We have a way to go before getting this into production but it is an important step!&lt;/p&gt;

&lt;p&gt;If you have any questions or advice, please comment below.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>phoenix</category>
      <category>nginx</category>
    </item>
    <item>
      <title>Django to Phoenix - Part 2: Nginx</title>
      <dc:creator>Michael Jones</dc:creator>
      <pubDate>Sat, 28 Oct 2017 14:09:20 +0000</pubDate>
      <link>https://dev.to/michaeljones/django-to-phoenix---part-2-nginx-9kg</link>
      <guid>https://dev.to/michaeljones/django-to-phoenix---part-2-nginx-9kg</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;I have a website built with &lt;a href="https://www.djangoproject.com/"&gt;Django&lt;/a&gt; and I would like to start writing parts of it in &lt;a href="http://phoenixframework.org/"&gt;Phoenix&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/michaeljones/django-to-phoenix-part-0-6ij"&gt;Part 0: Background&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/michaeljones/django-to-phoenix-part-1-56h"&gt;Part 1: Docker for Dev&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Starting Point
&lt;/h2&gt;

&lt;p&gt;In the last section we set up a development Dockerfile &amp;amp; image so that we could run our Django &lt;a href="https://docs.djangoproject.com/en/1.11/ref/django-admin/#runserver"&gt;runserver&lt;/a&gt; from inside a &lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt; container. We did this so that we could add &lt;a href="https://www.nginx.com/resources/wiki/"&gt;nginx&lt;/a&gt; to the container in order to split requests between our Django development server and our future Phoenix development server. &lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Nginx to our Dockerfile
&lt;/h2&gt;

&lt;p&gt;In our last post we covered the Dockerfile &lt;code&gt;copy&lt;/code&gt; command that allows us to copy files from our working directory into the Docker image. So we're going to use that to add nginx configuration to our image. There are 4 steps to this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Install nginx with apt-get
&lt;/h3&gt;

&lt;p&gt;Near the top of our Dockerfile, we have a &lt;code&gt;run&lt;/code&gt; command that runs &lt;a href="https://en.wikipedia.org/wiki/APT_(Debian)"&gt;apt-get&lt;/a&gt; to install the various Ubuntu packages that we need. We need to update the line to add the 'nginx' package. So it is going to look something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install dependencies for builds&lt;/span&gt;
&lt;span class="k"&gt;run &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; git python python-pip make g++ wget curl libxml2-dev &lt;span class="se"&gt;\
&lt;/span&gt;        libxslt1-dev libjpeg-dev libenchant-dev libffi-dev supervisor &lt;span class="se"&gt;\
&lt;/span&gt;        libpq-dev python-dev &lt;span class="nb"&gt;realpath &lt;/span&gt;apt-transport-https nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;nginx&lt;/code&gt; at the end of the install line. Note that changing such an early and important line like this requires quite a long rebuild for the Docker image so bear that in mind. &lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Add &lt;code&gt;nginx.conf&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This is the nginx base configuration. Like Apache, nginx has a pattern of having a base configuration and then site specific config files. I'll include the base config here for completeness but I'm pretty sure I haven't made any changes to it from the standard nginx config (though I have stripped comments for conciseness):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="s"&gt;www-data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;worker_processes&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;pid&lt;/span&gt; &lt;span class="n"&gt;/run/nginx.pid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;daemon&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;events&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;worker_connections&lt;/span&gt; &lt;span class="mi"&gt;768&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;http&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="kn"&gt;sendfile&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;tcp_nopush&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;tcp_nodelay&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;keepalive_timeout&lt;/span&gt; &lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;types_hash_max_size&lt;/span&gt; &lt;span class="mi"&gt;2048&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kn"&gt;include&lt;/span&gt; &lt;span class="n"&gt;/etc/nginx/mime.types&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;default_type&lt;/span&gt; &lt;span class="nc"&gt;application/octet-stream&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kn"&gt;access_log&lt;/span&gt; &lt;span class="n"&gt;/var/log/nginx/access.log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;error_log&lt;/span&gt; &lt;span class="n"&gt;/var/log/nginx/error.log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kn"&gt;gzip&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;gzip_disable&lt;/span&gt; &lt;span class="s"&gt;"msie6"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kn"&gt;include&lt;/span&gt; &lt;span class="n"&gt;/etc/nginx/conf.d/*.conf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;include&lt;/span&gt; &lt;span class="n"&gt;/etc/nginx/sites-enabled/*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we add this to our Dockefile file with a line like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;copy&lt;/span&gt;&lt;span class="s"&gt; ./dev/nginx.conf /etc/nginx/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we're building up an Ubuntu image inside Docker, we copy the config into the standard place that Ubuntu expects to see the nginx config. I've added that line quite close to the bottom of my Dockerfile but it doesn't really matter where it goes as long as it is after the apt-get command that installs nginx.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: As I write this I realise that this config is probably the same as one that comes with the nginx install in the container so this is probably redundant. Yay, for writing!&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Add our site config
&lt;/h3&gt;

&lt;p&gt;At the bottom of the above config you can see the line: &lt;code&gt;include /etc/nginx/sites-enabled/*;&lt;/code&gt;. This means that any additional config files in that system directory will be read by nginx when it starts up. So we add our site config to that directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;copy&lt;/span&gt;&lt;span class="s"&gt; ./dev/site.conf /etc/nginx/sites-enabled/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where our &lt;code&gt;site.conf&lt;/code&gt; looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;
&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$http_host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;# we don't want nginx trying to do something clever with&lt;/span&gt;
        &lt;span class="c1"&gt;# redirects, we set the Host: header above already.&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_redirect&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://localhost:7000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first important part is: &lt;code&gt;listen 8000&lt;/code&gt; which &lt;a href="http://nginx.org/en/docs/http/ngx_http_core_module.html#listen"&gt;tells nginx&lt;/a&gt; to listen on that port for incoming requests. Note, that is the port that we're running our Django runserver on so we're going to have move that to another port as you can't have two programs listening on the same port. &lt;/p&gt;

&lt;p&gt;The next important part is: &lt;code&gt;location /&lt;/code&gt;. This creates a config block that only applies to requests that match the specified location. The matching is done such that requests to any paths starting with &lt;code&gt;/&lt;/code&gt; are matched. As all paths start with &lt;code&gt;/&lt;/code&gt;, this will match all of them. &lt;/p&gt;

&lt;p&gt;Note, when I say "path", what I mean is part of the URL after the host. So the path for &lt;code&gt;https://www.tangotimetable.com/events&lt;/code&gt; would be &lt;code&gt;/events&lt;/code&gt;. As this starts with &lt;code&gt;/&lt;/code&gt;, it will match our location block.&lt;/p&gt;

&lt;p&gt;Inside the location block, the most important part is &lt;code&gt;proxy_pass http://localhost:7000;&lt;/code&gt;. This tells nginx to forward all the requests through to the process listening on port &lt;code&gt;7000&lt;/code&gt;. This will be our Django runserver as soon as we change it to listen on that port. &lt;/p&gt;

&lt;p&gt;The config is based on my production config where the other &lt;code&gt;proxy_&lt;/code&gt; options are required. I've honestly forgotten what they do. &lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Remove the default site config
&lt;/h3&gt;

&lt;p&gt;When you install nginx on an Ubuntu system, it comes with a default site config that listens on port 80 and displays an nginx landing page. This isn't a problem for our development set up as we're not using port 80 but it is a problem in production when we might want to use that. It is also something that caught me out so I wanted to highlight it. To get rid of it, we add the following lines to our Dockerfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Remove default nginx config which attempts to bind to port 80&lt;/span&gt;
&lt;span class="k"&gt;run &lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; /etc/nginx/sites-enabled/default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Changing our Django runserver port
&lt;/h2&gt;

&lt;p&gt;As noted above, we're switching from the Django runserver listening on port &lt;code&gt;8000&lt;/code&gt; to listening on port &lt;code&gt;7000&lt;/code&gt;. This is so that we can have nginx listening on port &lt;code&gt;8000&lt;/code&gt;. So we need to update the &lt;code&gt;run-django&lt;/code&gt; script that launches the server. This script was introduced &lt;a href="https://dev.to/michaeljones/django-to-phoenix-part-1-56h"&gt;in the last post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here we change it like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- python manage.py z runserver 0.0.0.0:8000
&lt;/span&gt;&lt;span class="gi"&gt;+ python manage.py z runserver 0.0.0.0:7000
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Launching nginx in the container
&lt;/h2&gt;

&lt;p&gt;Finally, we need to make sure that our nginx process is launched when the container is started. If you remember, we're using a program called &lt;a href="http://supervisord.org/"&gt;supervisord&lt;/a&gt; to manage the processes that run in the container so we have to add a config entry for nginx to our supervisord config.&lt;/p&gt;

&lt;p&gt;This looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[program:nginx]
command=/usr/sbin/nginx
stdout_events_enabled=true
stderr_events_enabled=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I put this above the &lt;code&gt;[program:django]&lt;/code&gt; block but I doubt it matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That's it! If I run our &lt;code&gt;docker run&lt;/code&gt; script from the last post I see this output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;2017-10-28 09:18:42,008 CRIT Supervisor running as root (no user in config file)
2017-10-28 09:18:42,012 INFO supervisord started with pid 7
2017-10-28 09:18:43,015 INFO spawned: 'nginx' with pid 10
2017-10-28 09:18:43,017 INFO spawned: 'django' with pid 11
&lt;/span&gt;&lt;span class="gp"&gt;2017-10-28 09:18:44,034 INFO success: nginx entered RUNNING state, process has stayed up for &amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;than 1 seconds &lt;span class="o"&gt;(&lt;/span&gt;startsecs&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;2017-10-28 09:18:44,034 INFO success: django entered RUNNING state, process has stayed up for &amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;than 1 seconds &lt;span class="o"&gt;(&lt;/span&gt;startsecs&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which shows supervisord starting both nginx &amp;amp; Django and being happy that they stay up and running for more than 1 sec. If I visit &lt;code&gt;http://localhost:8000&lt;/code&gt; I see my Django apps home page as I would expect. &lt;/p&gt;

&lt;p&gt;If you have any questions or advice, please comment below.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>nginx</category>
    </item>
    <item>
      <title>Django to Phoenix - Part 1: Docker for Dev</title>
      <dc:creator>Michael Jones</dc:creator>
      <pubDate>Thu, 26 Oct 2017 19:50:28 +0000</pubDate>
      <link>https://dev.to/michaeljones/django-to-phoenix-part-1-56h</link>
      <guid>https://dev.to/michaeljones/django-to-phoenix-part-1-56h</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;I have a website built with &lt;a href="https://www.djangoproject.com/"&gt;Django&lt;/a&gt; and I would like to start writing parts of it in &lt;a href="http://phoenixframework.org/"&gt;Phoenix&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting Point
&lt;/h2&gt;

&lt;p&gt;I deploy the website using &lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt; but when it comes to development, I run Django's local server directly. I have been advised that the best strategy for introducing a new language &amp;amp; framework into a website is to use &lt;a href="https://www.nginx.com/resources/wiki/"&gt;nginx&lt;/a&gt; to split the incoming requests based on URLs. &lt;/p&gt;

&lt;p&gt;This means that we could run &amp;amp; configure nginx on the local machine, or as we are already using Docker for deployment, we could start doing development using a Docker container as well and then add the nginx layer into that. That feels a lot more contained &amp;amp; less messy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up Django in Docker
&lt;/h2&gt;

&lt;p&gt;In order to take that step, we're going to want to transition to getting the current Django set up to work in Docker. &lt;/p&gt;

&lt;p&gt;I think that ideally I'd be able to use the same Dockerfile for both development &amp;amp; production in order to have the environments match as closely as possible. However when I look at my production Dockerfile, I'm not sure how to make that happen.&lt;/p&gt;

&lt;p&gt;So we're going to start with a new development Dockerfile which we're going to call &lt;code&gt;Dockerfile.dev&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="s"&gt; ubuntu:14.04&lt;/span&gt;

&lt;span class="k"&gt;run &lt;/span&gt;apt-get update &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;span class="c"&gt;# Install dependencies for builds&lt;/span&gt;
&lt;span class="k"&gt;run &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; git python python-pip make g++ wget curl libxml2-dev &lt;span class="se"&gt;\
&lt;/span&gt;        libxslt1-dev libjpeg-dev libenchant-dev libffi-dev supervisor &lt;span class="se"&gt;\
&lt;/span&gt;        libpq-dev python-dev &lt;span class="nb"&gt;realpath &lt;/span&gt;apt-transport-https

&lt;span class="c"&gt;# Install yarn from website&lt;/span&gt;
&lt;span class="k"&gt;run &lt;/span&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
&lt;span class="k"&gt;run &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"deb https://dl.yarnpkg.com/debian/ stable main"&lt;/span&gt; | &lt;span class="nb"&gt;tee&lt;/span&gt; /etc/apt/sources.list.d/yarn.list

&lt;span class="k"&gt;run &lt;/span&gt;apt-get update &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="k"&gt;run &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; yarn

&lt;span class="c"&gt;# Install node-js via the nave.sh helper script so that we &lt;/span&gt;
&lt;span class="c"&gt;# can build our client side applications &lt;/span&gt;
&lt;span class="k"&gt;add&lt;/span&gt;&lt;span class="s"&gt; https://raw.githubusercontent.com/isaacs/nave/master/nave.sh ./nave.sh&lt;/span&gt;

&lt;span class="k"&gt;run &lt;/span&gt;bash nave.sh usemain 6.9.1

&lt;span class="c"&gt;# Install python packages&lt;/span&gt;
&lt;span class="k"&gt;workdir&lt;/span&gt;&lt;span class="s"&gt; /src&lt;/span&gt;
&lt;span class="k"&gt;copy&lt;/span&gt;&lt;span class="s"&gt; ./requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;run &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; pip
&lt;span class="k"&gt;run &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="c"&gt;# Create source code directory&lt;/span&gt;
&lt;span class="k"&gt;run &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;dancetimetable

&lt;span class="c"&gt;# Copy over various config files &amp;amp; scripts&lt;/span&gt;
&lt;span class="k"&gt;copy&lt;/span&gt;&lt;span class="s"&gt; ./dev/supervisord.conf /etc/supervisor/conf.d/supervisord.conf&lt;/span&gt;

&lt;span class="k"&gt;run &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; /src/dev

&lt;span class="k"&gt;copy&lt;/span&gt;&lt;span class="s"&gt; ./dev/run-* /src/dev/&lt;/span&gt;
&lt;span class="k"&gt;copy&lt;/span&gt;&lt;span class="s"&gt; ./meta.json /src/meta.json&lt;/span&gt;

&lt;span class="k"&gt;expose&lt;/span&gt;&lt;span class="s"&gt; 8000&lt;/span&gt;

&lt;span class="c"&gt;# Run supervisord on launch&lt;/span&gt;
&lt;span class="k"&gt;cmd&lt;/span&gt;&lt;span class="s"&gt; /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a fairly standard Dockerfile to my understanding. For those unfamiliar with Docker, the file starts with &lt;code&gt;from ubuntu:14.04&lt;/code&gt; which says 'give me a blank ubuntu 14.04' image to begin with and then change it with the commands in the rest of the file. The &lt;a href="https://docs.docker.com/engine/reference/builder/#run"&gt;run command&lt;/a&gt; runs a particular command within that image, saving any effects on the system. The &lt;a href="https://docs.docker.com/engine/reference/builder/#copy"&gt;copy command&lt;/a&gt; allows you to copy files from your working directory on your machine into the Docker image.&lt;/p&gt;

&lt;p&gt;Interestingly whilst we copy in dependencies &amp;amp; configuration files, we don't copy in any of the website source code here. This is because when we &lt;a href="https://docs.docker.com/engine/reference/commandline/run/"&gt;run&lt;/a&gt; the Docker container we 'mount' folders from our local machine into the container. This is important so that when we edit &amp;amp; save a file on our machine, the processes inside the Docker container see it straight away and can use it. Without this we would have to rebuild the Docker image each time we make a change.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update 28-10-2017&lt;/em&gt;: Someone noted that I'm still using Ubuntu 14.04 which is quite old at this point. I'm doing this as my production environment still uses 14.04. I do plan to update but for the moment that version is still fully maintained and I don't need anything more from it.&lt;/p&gt;

&lt;p&gt;So how do we mount these directories when running the Docker container? With a command like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

docker run &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-p&lt;/span&gt; 127.0.0.1:8000:8000 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;/logs/gunicorn:/var/log/gunicorn:rw &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;/logs/nginx:/var/log/nginx:rw &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;/logs/cron:/var/log/cron:rw &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;/dancetimetable:/src/dancetimetable:rw &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;DANCETIMETABLE_SETTINGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;development &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--interactive&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--tty&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--name&lt;/span&gt; dance &lt;span class="se"&gt;\&lt;/span&gt;
    michaeljones/dancetimetable:dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important line is &lt;code&gt;-v&lt;/code&gt;pwd&lt;code&gt;/dancetimetable:/src/dancetimetable:rw&lt;/code&gt; which is where we say 'please make the &lt;code&gt;/src/dancetimetable&lt;/code&gt; directory in the running container, reflect the contents of the local &lt;code&gt;dancetimetable&lt;/code&gt; directory, in a read &amp;amp; write manner.'&lt;/p&gt;

&lt;p&gt;The other important line is &lt;code&gt;-p 127.0.0.1:8000:8000&lt;/code&gt; which says 'please expose port &lt;code&gt;8000&lt;/code&gt; from inside the container as the port &lt;code&gt;8000&lt;/code&gt; on our machine.' This means that we can visit &lt;code&gt;http://localhost:8000&lt;/code&gt; and view our app. If we don't tell Docker to do this then we can't access our website.&lt;/p&gt;

&lt;p&gt;You might notice a couple of details are missing here. For example, how are we running the Django development server inside the container? The answer to that lies in the last line of the Dockerfile. This specifies the command we want to run when launching the Docker container. In our case it is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;cmd&lt;/span&gt;&lt;span class="s"&gt; /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This runs the &lt;a href="http://supervisord.org/"&gt;supervisord&lt;/a&gt; program with our specific configuration. Supervisord is a program that can be instructed to launch other programs and monitor them, making sure that they are restarted if they fail. This seems to be the recommended way to run multiple programs in a Docker container as the Dockerfile only allows you to specify one &lt;a href="https://docs.docker.com/engine/reference/builder/#cmd"&gt;cmd&lt;/a&gt; entry at the end.&lt;/p&gt;

&lt;p&gt;Whilst we don't strictly need supervisord at the moment as we've only got one command to run, I'm already familiar with supervisord from my production setup and we're going to need to run another command soon anyway.&lt;/p&gt;

&lt;p&gt;So what does the supervisord config look like? Something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[supervisord]
nodaemon=true

[program:django]
command=/src/dev/run-django
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first block under &lt;code&gt;supervisord&lt;/code&gt; is for &lt;a href="http://supervisord.org/configuration.html#supervisord-section-settings"&gt;general configuration&lt;/a&gt; and the &lt;code&gt;nodaemon&lt;/code&gt; option tells supervisord to run in the foreground so that that Docker container doesn't lose track of it and stop prematurely.&lt;/p&gt;

&lt;p&gt;The second block indicates how we want to run the Django development server. The command entry points to a script and the other settings it supervisord to redirect all standard out &amp;amp; standard error from Django to the container's standard out &amp;amp; standard error so that we get to see it in the console. &lt;/p&gt;

&lt;p&gt;So what's in the &lt;code&gt;run-django&lt;/code&gt; script? That looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;cd&lt;/span&gt; /src/dancetimetable

&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DOCKER_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;netstat &lt;span class="nt"&gt;-nr&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'^0\.0\.0\.0'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;

python manage.py z runserver 0.0.0.0:8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This changes directory to where the Django &lt;code&gt;manage.py&lt;/code&gt; file is and runs the regular &lt;code&gt;runserver&lt;/code&gt; sub-command to get our development server running. &lt;/p&gt;

&lt;p&gt;The two extra details are, firstly, that we need to specify &lt;code&gt;0.0.0.0&lt;/code&gt; as the address rather than &lt;code&gt;localhost&lt;/code&gt; or &lt;code&gt;127.0.0.1&lt;/code&gt;. I do not know why. Secondly that find the address of our host machine from the perspective of the Docker container and make that available as DOCKER_HOST. We pick this up in our Django config files when specifying the HOST in the database settings. This is because I'm not running my Postgres database inside the Docker container. It is running on my local machine outside of the container and so Django needs to know how to connect to it. This is the only way I've found to do it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With all this in place, we can launch our Docker container and visit &lt;code&gt;https://localhost:8000&lt;/code&gt; to see our website. Now we have Django running inside a Docker container we're in a good place to add Nginx and then start introducing Phoenix.&lt;/p&gt;

&lt;p&gt;If you have any questions or advice, please comment below.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update 28-10-2017&lt;/em&gt;: I have edited the Dockerfile code block above to remove nginx elements that will be introduced and discussed in the next post.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>django</category>
      <category>phoenix</category>
    </item>
    <item>
      <title>Django to Phoenix - Part 0: Background</title>
      <dc:creator>Michael Jones</dc:creator>
      <pubDate>Tue, 24 Oct 2017 18:37:24 +0000</pubDate>
      <link>https://dev.to/michaeljones/django-to-phoenix-part-0-6ij</link>
      <guid>https://dev.to/michaeljones/django-to-phoenix-part-0-6ij</guid>
      <description>&lt;p&gt;Hey there, I wrote a website! I was pretty new to web development and I did my best. I wrote it in &lt;a href="https://www.djangoproject.com/"&gt;Django&lt;/a&gt; &amp;amp; &lt;a href="https://www.python.org/"&gt;Python&lt;/a&gt; as I was already familiar with Python, and Django is a friendly framework with a &lt;a href="https://djangopackages.org/"&gt;good ecosystem&lt;/a&gt;. It was a great experience. As a beginner, I was happy to have Django there to hold my hand along the way.&lt;/p&gt;

&lt;p&gt;However time has passed and I am getting into functional programming. Whilst &lt;a href="https://docs.python.org/2/howto/functional.html"&gt;functional techniques are possible in Python&lt;/a&gt;, it doesn't feel like a good fit. I've also been reading and listening to people talk about &lt;a href="http://phoenixframework.org/"&gt;Phoenix&lt;/a&gt;, &lt;a href="http://elixir-lang.org/"&gt;Elixir&lt;/a&gt; &amp;amp; the &lt;a href="https://www.erlang.org/"&gt;Erlang&lt;/a&gt; Virtual Machine and the combination sounds kind of great. &lt;/p&gt;

&lt;p&gt;So, I'm going to attempt to move at least some of my website, &lt;a href="https://www.tangotimetable.com/"&gt;Tango Timetable&lt;/a&gt;, from Django to Phoenix.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting Point
&lt;/h2&gt;

&lt;p&gt;The site is entirely in Django, with a couple of &lt;a href="https://en.wikipedia.org/wiki/Cron"&gt;cron jobs&lt;/a&gt; for weekly emails. It runs using &lt;a href="http://gunicorn.org/"&gt;Gunicorn&lt;/a&gt; behind &lt;a href="https://www.nginx.com/resources/wiki/"&gt;Nginx&lt;/a&gt; in a Docker container. I use Docker to simplify deployments as I was finding a 'git-pull &amp;amp; restart' approach to be too fragile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;I am comfortable with Python, experienced-but-no-expert in Django and a beginner at Phoenix &amp;amp; Elixir. I'm going to try to document my efforts here in future posts to share my findings but also to hopefully get guidance from others who might know better.&lt;/p&gt;

</description>
      <category>django</category>
      <category>phoenix</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
