Jekyll2020-05-19T13:12:41-07:00/Geoffrey LesselThings I wrote for you to readGeoffrey LesselConverting A Traditional Controller Driven App To Phoenix LiveView - Part 12019-09-25T00:00:00-07:002019-09-25T00:00:00-07:00/2019/converting-to-phoenix-liveview<div style="display: none">
This is part 1 of a 2 part post. In this one, we'll move from a
traditional Phoenix controller-driven application to one that is
live-updating with Phoenix LiveView. In part 2, we'll add some
interaction with the ability to comment.
</div>
<!--more-->
<div width="50%" style="float: right; padding-left: 1em; margin-left: 1em; font-size: 80%; color: #444; border-left: 2px solid #ddd;">
<p>Versions used</p>
<p>
<strong>Elixir</strong> <code>1.9.1-otp-22</code><br />
<strong>Phoenix</strong> <code>1.4.10</code><br />
<strong>Phoenix LiveView</strong> <code>0.2.0</code><br />
</p>
</div>
<p>I recently got back from ElixirConf 2019 and Phoenix LiveView was
undoubtedly the star of the conference. There were many talks on how
awesome it is, how to utilize it, and <a href="https://youtu.be/txk4WAlabvI">Chris McCord’s
keynote</a> was excellent. In truth, it is a very exciting
technology and has the potential to change the way real-time web
applications are built.</p>
<p>Shortly after Phoenix LiveView was first released, I played around
with it just a little bit:</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Have you played with <a href="https://twitter.com/elixirphoenix?ref_src=twsrc%5Etfw">@elixirphoenix</a>
<a href="https://twitter.com/hashtag/LiveView?src=hash&ref_src=twsrc%5Etfw">#LiveView</a>
yet? It’s *INSANE*. This is a quick test app re-computing and
re-rendering over 200 rows of data every 1/10 of a second. I wrote
no JS code. Bravo <a href="https://twitter.com/chris_mccord?ref_src=twsrc%5Etfw">@chris_mccord</a>
and team! <a href="https://twitter.com/hashtag/MyElixirStatus?src=hash&ref_src=twsrc%5Etfw">#MyElixirStatus</a>
<a href="https://t.co/E1oEkje8Ji">pic.twitter.com/E1oEkje8Ji</a></p>—
Geoffrey Lessel (@geolessel) <a href="https://twitter.com/geolessel/status/1108382138192134144?ref_src=twsrc%5Etfw">March
20, 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>However, I decided to dive a little bit deeper for a talk I recently
gave at <a href="https://www.meetup.com/fullstacktalks/events/264791606/">Full Stack Talks</a> in north San Diego County. This
post is a modified written version of the talk I gave that night. If
you’d rather watch the talk than read the text, you can <a href="https://www.youtube.com/watch?v=2Zaj_1htERc&list=PLydu3IE1lef2Po9GJWjL9zl1X4xAPphTM&index=10">view it
here</a>:</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/2Zaj_1htERc" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<h2 id="the-setup">The setup</h2>
<p><img src="/images/posts/2019/liveview-controller.png" width="50%" style="float: right; margin-left: 2em;" /></p>
<p>This is part 1 of a 2 part post. In this one, we’ll move from a
traditional Phoenix controller-driven application to one that is
live-updating. In part 2, we’ll add some interaction with the
ability to comment. If you watch the video above, you’ll see that I
went over both during the meetup in case you want to see what’s
coming.</p>
<p>Our task is to convert a traditional controller-driven Phoenix app to
one that uses Phoenix LiveView. The application we are working on is a
price tracker for 500 stocks traded on the Nasdaq exchange. In the
background, there is a process that fetches data from whatever pricing
source the application is connected to. Currently, our users have to
refresh their browser to get see this new information. We’d like to
update the application to use Phoenix LiveView to push the new
information to the user when it is received.</p>
<h3 id="a-few-notes-on-the-price-updater">A few notes on the price updater</h3>
<p>I mentioned above that there is a process that fetches data from a
source. For this sample application, that is abstracted away. In
reality, a GenServer is running on a timer. When the GenServer is
initialized, it fetches basic company information from a database and
binds them to a <code class="highlighter-rouge">companies</code> variable that it keeps in state. It also
initializes a <code class="highlighter-rouge">price</code> attribute for the company. Every two seconds, it
iterates through all 500 <code class="highlighter-rouge">companies</code> and generates a new price for the
company. Two-thirds of the time, the price will increase by a random
amount between 0 and 0.2 percent of the current price; one-third of
the time it will decrease by a similar amount.</p>
<p>Furthermore, this GenServer acts as a PubSub. It allows processes to
register themselves. Once a process is registered, it will receive an
<code class="highlighter-rouge">:update</code> message whenever prices are updated.</p>
<p>The code that handles the <code class="highlighter-rouge">:update_prices</code> message on each timer
message is below.</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># lib/fullstack/fake_db.ex</span>
<span class="k">def</span> <span class="n">handle_info</span><span class="p">(</span><span class="ss">:update_prices</span><span class="p">,</span> <span class="n">state</span><span class="p">)</span> <span class="k">do</span>
<span class="n">companies</span> <span class="o">=</span>
<span class="n">state</span><span class="o">.</span><span class="n">companies</span>
<span class="o">|></span> <span class="no">Flow</span><span class="o">.</span><span class="n">from_enumerable</span><span class="p">()</span>
<span class="o">|></span> <span class="no">Flow</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&</span><span class="no">Fullstack</span><span class="o">.</span><span class="n">update_latest_price_for_company</span><span class="o">/</span><span class="m">1</span><span class="p">)</span>
<span class="o">|></span> <span class="no">Enum</span><span class="o">.</span><span class="n">to_list</span><span class="p">()</span>
<span class="n">state</span><span class="o">.</span><span class="n">listeners</span>
<span class="o">|></span> <span class="no">Enum</span><span class="o">.</span><span class="n">each</span><span class="p">(</span><span class="k">fn</span> <span class="n">pid</span> <span class="o">-></span>
<span class="ss">:ok</span> <span class="o">=</span> <span class="no">Process</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">pid</span><span class="p">,</span> <span class="ss">:update</span><span class="p">,</span> <span class="p">[])</span>
<span class="k">end</span><span class="p">)</span>
<span class="p">{</span><span class="ss">:noreply</span><span class="p">,</span> <span class="p">%{</span><span class="n">state</span> <span class="o">|</span> <span class="ss">companies:</span> <span class="n">companies</span><span class="p">}}</span>
<span class="k">end</span>
</code></pre></div></div>
<h3 id="router-controller-and-view-template">Router, Controller, and View Template</h3>
<p>The router, controller, and view template are pretty simple and are
typical for what you’d see in a simple application like this.</p>
<h4 id="router">Router</h4>
<p>A <code class="highlighter-rouge">GET</code> request to the root of our application (<code class="highlighter-rouge">/</code>) will end up being
routed to <code class="highlighter-rouge">FullstackWeb.PageController.index/2</code>.</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># lib/fullstack_web/router.ex</span>
<span class="c1"># ... typical router setup stuff ...</span>
<span class="n">scope</span> <span class="sd">"</span><span class="s2">/"</span><span class="p">,</span> <span class="no">FullstackWeb</span> <span class="k">do</span>
<span class="n">pipe_through</span> <span class="ss">:browser</span>
<span class="n">get</span> <span class="sd">"</span><span class="s2">/"</span><span class="p">,</span> <span class="no">PageController</span><span class="p">,</span> <span class="ss">:index</span>
<span class="k">end</span>
</code></pre></div></div>
<h4 id="controller">Controller</h4>
<p>Once the request hits the controller, it is again pretty typical. Our
function simply requests all the companies in the database (including
with their current price information) and passes that list as
a <code class="highlighter-rouge">companies</code> assign to the <code class="highlighter-rouge">index.html</code> template.</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># lib/fullstack_web/controllers/page_controller.ex</span>
<span class="k">defmodule</span> <span class="no">FullstackWeb</span><span class="o">.</span><span class="no">PageController</span> <span class="k">do</span>
<span class="kn">use</span> <span class="no">FullstackWeb</span><span class="p">,</span> <span class="ss">:controller</span>
<span class="k">def</span> <span class="n">index</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="n">_params</span><span class="p">)</span> <span class="k">do</span>
<span class="n">companies</span> <span class="o">=</span> <span class="no">Fullstack</span><span class="o">.</span><span class="no">FakeDB</span><span class="o">.</span><span class="n">get_companies</span><span class="p">()</span>
<span class="n">render</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="sd">"</span><span class="s2">index.html"</span><span class="p">,</span> <span class="ss">companies:</span> <span class="n">companies</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<h4 id="view-template">View Template</h4>
<p>Finally, the user’s request hits gets to the view template which
returns the HTML that is sent to the user’s browser. The important
part of the code is below, in which we render a row of data for each
company in the <code class="highlighter-rouge">@companies</code> assign.</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># lib/fullstack_web/templates/index.html.eex</span>
<span class="c1"># ... table setup code including thead ...</span>
<span class="o"><%=</span> <span class="n">for</span> <span class="n">company</span> <span class="o"><-</span> <span class="nv">@companies</span> <span class="k">do</span> <span class="p">%</span><span class="o">></span>
<span class="o"><</span><span class="n">tr</span><span class="o">></span>
<span class="o"><</span><span class="n">td</span><span class="err">></span><span class="o"><%=</span> <span class="n">company</span><span class="o">.</span><span class="n">symbol</span> <span class="p">%</span><span class="err">></span><span class="o"></</span><span class="n">td</span><span class="o">></span>
<span class="o"><</span><span class="n">td</span><span class="err">></span><span class="o"><%=</span> <span class="n">company</span><span class="o">.</span><span class="n">name</span> <span class="p">%</span><span class="err">></span><span class="o"></</span><span class="n">td</span><span class="o">></span>
<span class="o"><</span><span class="n">td</span> <span class="n">class</span><span class="o">=</span><span class="sd">"</span><span class="s2">right"</span><span class="o">></span>
<span class="o"><%=</span> <span class="ss">:erlang</span><span class="o">.</span><span class="n">float_to_binary</span><span class="p">(</span><span class="n">company</span><span class="o">.</span><span class="n">latest_price</span><span class="p">,</span> <span class="p">[</span><span class="ss">decimals:</span> <span class="m">2</span><span class="p">])</span> <span class="p">%</span><span class="o">></span>
<span class="o"></</span><span class="n">td</span><span class="o">></span>
<span class="o"></</span><span class="n">tr</span><span class="o">></span>
<span class="o"><</span><span class="p">%</span> <span class="k">end</span> <span class="p">%</span><span class="o">></span>
</code></pre></div></div>
<h2 id="say-goodbye-to-the-controller">Say goodbye to the controller</h2>
<p>In order to keep up with other traders, we need the new information
pushed to us whenever it changes. Who has time to keep manually
refreshing the browser?</p>
<p>The first step in converting our conventional controller-driven
application is to get rid of the controller. We can render the same
information—even statically—with LiveView.</p>
<h3 id="add-the-dependency">Add the dependency</h3>
<p>First, we need to make sure that <code class="highlighter-rouge">phoenix_live_view</code> is included in
our list of dependencies. It is not included with the main Phoenix
dependency so we need to specify that we need it in our
application. Add <code class="highlighter-rouge">{:phoenix_live_view, "~> 0.2.0"}</code> to your mix.exs
file.</p>
<h3 id="add-four-lines-of-javascript">Add four lines of javascript</h3>
<p>The only javascript we will be touching in our application is the four
lines straight out of the setup guide for LiveView. These lines simply
set up the socket connections required for LiveView to do its magic.</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// in assets/app.js</span>
<span class="k">import</span> <span class="p">{</span><span class="nx">Socket</span><span class="p">}</span> <span class="k">from</span> <span class="s2">"phoenix"</span>
<span class="k">import</span> <span class="nx">LiveSocket</span> <span class="k">from</span> <span class="s2">"phoenix_live_view"</span>
<span class="kd">let</span> <span class="nx">liveSocket</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">LiveSocket</span><span class="p">(</span><span class="s2">"/live"</span><span class="p">,</span> <span class="nx">Socket</span><span class="p">)</span>
<span class="nx">liveSocket</span><span class="p">.</span><span class="nx">connect</span><span class="p">()</span>
</code></pre></div></div>
<h3 id="set-up-the-live-socket-in-the-endpoint">Set up the live socket in the Endpoint</h3>
<p>Our Endpoint needs to know that we expect LiveView socket
connections. Add this line.</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># lib/fullstack_web/endpoint.ex</span>
<span class="n">socket</span> <span class="sd">"</span><span class="s2">live"</span><span class="p">,</span> <span class="no">Phoenix</span><span class="o">.</span><span class="no">LiveView</span><span class="o">.</span><span class="no">Socket</span>
</code></pre></div></div>
<h3 id="add-some-salt">Add some salt</h3>
<p>In order to securely sign the network traffic over the LiveView socket,
we need to set a <code class="highlighter-rouge">signing_salt</code> for LiveView in the Endpoint’s config.</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># in config/config.exs</span>
<span class="n">config</span> <span class="ss">:fullstack</span><span class="p">,</span> <span class="no">FullstackWeb</span><span class="o">.</span><span class="no">Endpoint</span><span class="p">,</span>
<span class="ss">url:</span> <span class="p">[</span><span class="ss">host:</span> <span class="sd">"</span><span class="s2">localhost"</span><span class="p">],</span>
<span class="ss">secret_key_base:</span> <span class="sd">"</span><span class="s2">something-secret"</span><span class="p">,</span>
<span class="ss">render_errors:</span> <span class="p">[</span><span class="ss">view:</span> <span class="no">FullstackWeb</span><span class="o">.</span><span class="no">ErrorView</span><span class="p">,</span> <span class="ss">accepts:</span> <span class="sx">~w(html json)</span><span class="p">],</span>
<span class="ss">pubsub:</span> <span class="p">[</span><span class="ss">name:</span> <span class="no">Fullstack</span><span class="o">.</span><span class="no">PubSub</span><span class="p">,</span> <span class="ss">adapter:</span> <span class="no">Phoenix</span><span class="o">.</span><span class="no">PubSub</span><span class="o">.</span><span class="no">PG2</span><span class="p">],</span>
<span class="ss">live_view:</span> <span class="p">[</span><span class="ss">signing_salt:</span> <span class="sd">"</span><span class="s2">mmmm-salty"</span><span class="p">]</span> <span class="c1"># <-- Add this!</span>
</code></pre></div></div>
<h3 id="rename-the-template">Rename the template</h3>
<p>The template file that renders the table of companies is named
index.html.eex. However, LiveView expects any templates that require
live updating to have the .leex extension. All we need to do is rename
lib/fullstack_web/templates/page/index.html.eex to
lib/fullstack_web/templates/page/index.html.leex.</p>
<h3 id="update-the-router">Update the router</h3>
<p>Next, we need to update the router to route requests for the root of
our application to the LiveView version of our site.</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">defmodule</span> <span class="no">FullstackWeb</span><span class="o">.</span><span class="no">Router</span> <span class="k">do</span>
<span class="kn">use</span> <span class="no">FullstackWeb</span><span class="p">,</span> <span class="ss">:router</span>
<span class="kn">import</span> <span class="no">Phoenix</span><span class="o">.</span><span class="no">LiveView</span><span class="o">.</span><span class="no">Router</span> <span class="c1"># <-- add this</span>
<span class="c1"># ... other setup stuff ...</span>
<span class="n">scope</span> <span class="sd">"</span><span class="s2">/"</span><span class="p">,</span> <span class="no">FullstackWeb</span> <span class="k">do</span>
<span class="n">pipe_through</span> <span class="ss">:browser</span>
<span class="n">live</span> <span class="sd">"</span><span class="s2">/"</span><span class="p">,</span> <span class="no">PageLive</span> <span class="c1"># <-- change this</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<h3 id="define-fullstackwebpagelive">Define <code class="highlighter-rouge">FullstackWeb.PageLive</code></h3>
<p>We’ve routed all root requests to <code class="highlighter-rouge">FullstackWeb.PageLive</code> but we have
yet to define that module. Here’s where the magic starts to happen and
we start to get into the real implementation of LiveView.</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># lib/fullstack_web/live/page_live.ex</span>
<span class="k">defmodule</span> <span class="no">FullstackWeb</span><span class="o">.</span><span class="no">PageLive</span> <span class="k">do</span>
<span class="kn">use</span> <span class="no">Phoenix</span><span class="o">.</span><span class="no">LiveView</span>
<span class="n">alias</span> <span class="no">Fullstack</span><span class="o">.</span><span class="no">FakeDB</span>
<span class="k">def</span> <span class="n">mount</span><span class="p">(</span><span class="n">_session</span><span class="p">,</span> <span class="n">socket</span><span class="p">)</span> <span class="k">do</span>
<span class="p">{</span><span class="ss">:ok</span><span class="p">,</span>
<span class="n">assign</span><span class="p">(</span><span class="n">socket</span><span class="p">,</span>
<span class="ss">companies:</span> <span class="no">FakeDB</span><span class="o">.</span><span class="n">get_companies</span><span class="p">()</span>
<span class="p">)}</span>
<span class="k">end</span>
<span class="k">def</span> <span class="n">render</span><span class="p">(</span><span class="n">assigns</span><span class="p">)</span> <span class="k">do</span>
<span class="no">FullstackWeb</span><span class="o">.</span><span class="no">PageView</span><span class="o">.</span><span class="n">render</span><span class="p">(</span><span class="sd">"</span><span class="s2">index.html"</span><span class="p">,</span> <span class="n">assigns</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<p>Let’s break this down bit by bit.</p>
<h4 id="use-phoenixliveview"><code class="highlighter-rouge">use Phoenix.LiveView</code></h4>
<p>This will bring in all the functions necessary to set up a LiveView.
It expects us to define two functions: <code class="highlighter-rouge">mount/2</code> and <code class="highlighter-rouge">render/1</code>.</p>
<h4 id="mount2"><code class="highlighter-rouge">mount/2</code></h4>
<p><code class="highlighter-rouge">mount/2</code> is called when a new connection is established and is passed
the session information and the socket. We can use this to set up the
initial assigns that our template needs. This only runs once per
connection. As a return value, it expects a tuple containing the <code class="highlighter-rouge">:ok</code>
Atom and a socket.</p>
<p>Our implementation takes the provided socket and binds the list of
companies in our fake database to <code class="highlighter-rouge">companies</code> in the socket’s
<code class="highlighter-rouge">assigns</code> with the <code class="highlighter-rouge">assign/2</code> function.</p>
<h4 id="render1"><code class="highlighter-rouge">render/1</code></h4>
<p><code class="highlighter-rouge">render/1</code> is tasked with pushing the result of its function to the
client. It is passed <code class="highlighter-rouge">socket.assigns</code> as its one argument.</p>
<p>Our implementation simply passes the responsibility of rendering to
<code class="highlighter-rouge">FullstackWeb.PageView</code>, making sure to pass on the <code class="highlighter-rouge">assigns</code> it
receives.</p>
<h3 id="restart-the-server-and-refresh-the-browser">Restart the server and refresh the browser</h3>
<p><img src="/images/posts/2019/liveview-controller.png" width="50%" style="float: right; margin-left: 2em;" /></p>
<p>If you’ve done everything correctly, all we need to do now is restart
the Phoenix server (since we’ve modified <code class="highlighter-rouge">config/config.exs</code>) and
refresh the browser. If you see the same HTML rendered as before (with
different prices), then our refactor was successful!</p>
<h2 id="enable-live-updating">Enable Live Updating</h2>
<p>We now have requests being routed to <code class="highlighter-rouge">FullstackWeb.PageLive</code> instead of
<code class="highlighter-rouge">FullstackWeb.PageController</code>. Believe it or not, that was the hard part!</p>
<h3 id="register-the-connection-to-receive-updates">Register the connection to receive updates</h3>
<p>I mentioned earlier in this post that there is a GenServer that
updates the prices of the companies every two seconds. It has a
<code class="highlighter-rouge">register/1</code> function that we can use to register browser connection
processes in order to receive an <code class="highlighter-rouge">:update</code> message whenever new data
is available. We need to register every time a browser connects, but only
one time. Where should we do this registration?</p>
<p>If you answered <code class="highlighter-rouge">PageLive.mount/2</code>, you get a gold star! Since <code class="highlighter-rouge">mount/2</code>
is called once per process on initial mount, we can use this function to
register the current process with our fake database.</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># lib/fullstack_web/live/page_live.ex</span>
<span class="k">def</span> <span class="n">mount</span><span class="p">(</span><span class="n">_session</span><span class="p">,</span> <span class="n">socket</span><span class="p">)</span> <span class="k">do</span>
<span class="k">if</span> <span class="n">connected?</span><span class="p">(</span><span class="n">socket</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="no">FakeDB</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">self</span><span class="p">())</span> <span class="c1"># <-- add this</span>
<span class="p">{</span><span class="ss">:ok</span><span class="p">,</span>
<span class="n">assign</span><span class="p">(</span><span class="n">socket</span><span class="p">,</span>
<span class="ss">companies:</span> <span class="no">FakeDB</span><span class="o">.</span><span class="n">get_companies</span><span class="p">()</span>
<span class="p">)}</span>
<span class="k">end</span>
</code></pre></div></div>
<h3 id="handle-the-update-messages">Handle the <code class="highlighter-rouge">:update</code> messages</h3>
<p>Since we are now registered, <code class="highlighter-rouge">PageLive</code> will now receive an <code class="highlighter-rouge">:update</code>
message every two seconds. <code class="highlighter-rouge">PageLive</code> is currently not set up to
handle that message so we need to define a message handler function.</p>
<p>Whenever <code class="highlighter-rouge">FakeDB</code> tells us that there is an update available, we will
simply pull in the new updated list of companies and assign that to
the current socket. <code class="highlighter-rouge">render/1</code> will then receive that information and
push the changes to the browser. Finally, we will indicate that we will
no send a reply to the message sender.</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># lib/fullstack_web/live/page_live.ex</span>
<span class="k">def</span> <span class="n">handle_info</span><span class="p">(</span><span class="ss">:update</span><span class="p">,</span> <span class="n">socket</span><span class="p">)</span> <span class="k">do</span>
<span class="p">{</span><span class="ss">:noreply</span><span class="p">,</span> <span class="n">assign</span><span class="p">(</span><span class="n">socket</span><span class="p">,</span> <span class="ss">companies:</span> <span class="no">FakeDB</span><span class="o">.</span><span class="n">get_companies</span><span class="p">())}</span>
<span class="k">end</span>
</code></pre></div></div>
<h3 id="enjoy-your-live-updates">Enjoy your live updates!</h3>
<p><img src="/images/posts/2019/liveview-pagelive.gif" width="50%" style="float: right; margin-left: 2em;" /></p>
<p>That’s it! Your browser’s connection to Phoenix is now a live one! Any time
the fake database sends out an update, <code class="highlighter-rouge">PageLive</code> will push the new list
of companies to the browser through the socket connection.</p>
<h2 id="coming-in-part-2">Coming In Part 2</h2>
<p>In part 2, we’ll add a live chat and push the updating frequency waaay up.</p>
<p>If you have any questions or comments, hit me up on Twitter at
<a href="https://www.twitter.com/geolessel">@geolessel</a> or in the Elixir Slack community at @geo.</p>Geoffrey LesselThis is part 1 of a 2 part post. In this one, we'll move from a traditional Phoenix controller-driven application to one that is live-updating with Phoenix LiveView. In part 2, we'll add some interaction with the ability to comment.Phoenix In Action Has Been Released (And Other Updates)2019-05-29T00:00:00-07:002019-05-29T00:00:00-07:00/2019/phoenix-in-action-released<p>It has been about 18 months since I announced on this blog that I was
working on writing <em>Phoenix in Action</em> for Manning Publications. I’m
very excited to announce that it is finished!</p>
<h2 id="phoenix-in-action">Phoenix in Action</h2>
<p><a href="https://www.manning.com/books/phoenix-in-action?a_aid=geolessel&a_bid=80a2cec0"><img src="/images/phoenix-in-action.jpg" width="300" style="float: right; margin-left: 2em;" /></a></p>
<p>All of my writing energy was going towards finishing and releasing
<em>Phoenix in Action</em>, so I apologize for the lack of updates to this
site. However, it was a great journey from start to finish and
although it was harder than I thought it was going to be (and I
thought it was going to be hard), I am proud of how it ended up. I’ve
received some great feedback over the year and a half since it was
released in MEAP (pre-release) format and I look forward to hearing
what <em>you</em> think about it.</p>
<p>If you’ve not picked up a copy already, would you do so now? I’d
really appreciate the support. If you do buy a copy and enjoy it,
please leave a review at your favorite online retailer and tell others
about the book. You can buy it <a href="https://www.manning.com/books/phoenix-in-action?a_aid=geolessel&a_bid=80a2cec0">directly from Manning</a>.</p>
<h3 id="my-experience-writing-for-manning">My experience writing for Manning</h3>
<p>I won’t say a whole lot about my experience writing for Manning except
to say there were ups and downs but I was overall very pleased with
the process. While I did encouter a few frustrations during the
writing, Manning was nothing but accomodating and always wanted to do
the right thing by me. I also firmly believe that the editing process
I went through made the book so much better than I could have made it
on my own.</p>
<p>If you want to know more, feel free to contact me on <a href="https://www.twitter.com/geolessel">twitter at
@geolessel</a> or just read this great (and very in-depth)
writeup by Barry Pollard titled <a href="https://www.tunetheweb.com/blog/writing-a-technical-book-for-manning/">“Writing a Technical Book for
Manning”</a>. There is very little I found in that post to
disagree with or contradict. It very closely mirrored my own
experience. I’m just grateful he wrote all that and I can just link to
it.</p>
<h2 id="so-what-about-the-site">So what about the site?</h2>
<p>Manning approached me initially about writing <em>Phoenix in Action</em>
presumably because of the writing on this blog (and perhaps a
conference talk). I’d like to keep up the writing process and go back
to shorter-form writing and tutorials after tackling such a large
project.</p>
<h3 id="updating-old-posts">Updating old posts</h3>
<p>Initially, I have plans to go back to old posts and update any code
required to make them still relevant. I’m <em>sure</em> there are some things
that have broken in the 3 years since they were first published, but I
believe the contents are still important and worth making work again.</p>
<h3 id="adding-new-posts">Adding new posts</h3>
<p>I also have some ideas for new posts coming up as well. While writing
the book, I shipped a small number of pet projects that could make
great posts or tutorials. Here’s some highlights:</p>
<ul>
<li><a href="https://www.chatbroom.com">chatbroom.com</a>: a database-less real-time chat site that
uses Phoenix Presence, Channels, and React. Since it has no
database, it has no persistence (a feature!). If you aren’t there
when the post was sent, you won’t ever see the post in the chat
history. Also, you can’t get it back if you refresh the page or
navigate away and back again.</li>
<li><a href="http://www.cookingwithlink.com">cookingwithlink.com</a>: a simple site to view/search
cooking recipes for the Nintendo Switch game <em>The Legend of Zelda:
Breath of the Wild</em>. This could cover building simple APIs with
Phoenix.</li>
<li><a href="https://www.requestecho.com">requestecho.com</a>: kind of like requestbin.com, but
like <a href="https://www.chatbroom.com">ChatBroom</a>, it has no database and no
persistence. Use it to inspect web requests.</li>
<li>
<p>A <a href="https://github.com/boydm/scenic">Scenic UI framework</a> experiment in which I created an
external steering wheel display for the racing game F1 2018. Note
that I’ll be doing a talk about this at <a href="https://elixirconf.com/2019/speakers/54">ElixirConf 2019</a>! A preview:</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/SmTdZqlpt_I" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope;
picture-in-picture" allowfullscreen=""></iframe>
</li>
<li>
<p>A little project in which I create a MIDI sequencer for hardware
synthesizers. This isn’t all that far along, but here’s a preview:</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/Sok9dp-ooVM" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope;
picture-in-picture" allowfullscreen=""></iframe>
<p>This will likely <em>not</em> be an open-source project, but I could
potentially do a post about what I’ve learned during the process
with MIDI and GenServers trying to send millisecond-precise events
to external hardware.</p>
</li>
<li>
<p>I’ve done some testing with Phoenix LiveView as well and it is
🔥🔥🔥. I might do a post about how to set up a simple test
application that makes use of LiveView. A preview:</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Have you played with <a href="https://twitter.com/elixirphoenix?ref_src=twsrc%5Etfw">@elixirphoenix</a>
<a href="https://twitter.com/hashtag/LiveView?src=hash&ref_src=twsrc%5Etfw">#LiveView</a>
yet? It’s *INSANE*. This is a quick test app re-computing and
re-rendering over 200 rows of data every 1/10 of a second. I wrote
no JS code. Bravo <a href="https://twitter.com/chris_mccord?ref_src=twsrc%5Etfw">@chris_mccord</a>
and team! <a href="https://twitter.com/hashtag/MyElixirStatus?src=hash&ref_src=twsrc%5Etfw">#MyElixirStatus</a>
<a href="https://t.co/E1oEkje8Ji">pic.twitter.com/E1oEkje8Ji</a></p>—
Geoffrey Lessel (@geolessel) <a href="https://twitter.com/geolessel/status/1108382138192134144?ref_src=twsrc%5Etfw">March
20, 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
</li>
<li>
<p>A small Nerves application that watches a coffee pot’s current
draw. Once it finishes brewing, it notifies a slack channel.</p>
</li>
<li>Other, one-off stuff.</li>
</ul>
<p>Whew, that’s a list. Of course, I might end up doing posts about
<em>none</em> of those things. But if you’d like to see anything in
particular, please let me know.</p>
<h2 id="thanks-for-sticking-around">Thanks for sticking around</h2>
<p>Thanks for sticking around and for your continued visits to the
blog. I really do hope you find it a good resource as you learn Elixir
and Phoenix. As always, you can reach me on <a href="https://www.twitter.com/geolessel">twitter at
@geolessel</a> and <strong>@geo</strong> in the Elixir slack group.</p>Geoffrey LesselIt has been about 18 months since I announced on this blog that I was working on writing Phoenix in Action for Manning Publications. I’m very excited to announce that it is finished!I’ve Been Writing a Book—Phoenix in Action!2017-11-27T00:00:00-08:002017-11-27T00:00:00-08:00/2017/phoenix-in-action<p>If you’ve been visiting this site with any sort of regularity, you’ll notice that
it’s been quite a long time since my last blog post. Some of you might not care, and
others of you might be wondering what is going on. This is an update of sorts.</p>
<h2 id="an-update">An Update</h2>
<p>Since I last blogged back in April 2017, two major things have happened in my Elixir
world:</p>
<ol>
<li>I spoke at ElixirConf 2017! You can watch the video of the talk and see my notes
on my <a href="/talks"><strong>Talks I’ve Given</strong> page</a>.</li>
<li>I have been using up my creative writing energy writing a book for Manning Publications
— <em><a href="https://www.manning.com/books/phoenix-in-action?a_aid=geolessel&a_bid=80a2cec0">Phoenix in Action</a></em>.</li>
</ol>
<h3 id="phoenix-in-action-now-available"><em><a href="https://www.manning.com/books/phoenix-in-action?a_aid=geolessel&a_bid=80a2cec0">Phoenix in Action</a></em> now available</h3>
<p>While I’ve greatly missed blogging on smaller topics such as Ecto and various Phoenix or
Elixir projects, I have enjoyed the process of writing <em>Phoenix in Action</em> so far. I have
learned a lot about the writing process and I hope that once I start writing on this
blog again in the near future, you will see the benefits.</p>
<p><a href="https://www.manning.com/books/phoenix-in-action?a_aid=geolessel&a_bid=80a2cec0"><img src="/images/phoenix-in-action-meap.png" width="300" style="float: right; margin-left: 2em;" /></a></p>
<p>Head over to Manning’s
<em><a href="https://www.manning.com/books/phoenix-in-action?a_aid=geolessel&a_bid=80a2cec0">Phoenix in Action</a></em> page now to <a href="https://www.manning.com/books/phoenix-in-action?a_aid=geolessel&a_bid=80a2cec0">purchase the book</a>.
It is currently in the MEAP program which is Manning’s Early Access Program. What does that mean?
Basically, I’m still writing the book. Why would you want to purchase a book that isn’t finished?
Well, <em>you can help give me feedback during the process</em>. As a chapter is finished, you will be
notified that a new PDF is available for you to download which is the updated book.</p>
<p>Throughout the writing process, I would sincerely hope that you will provide me with feedback.
Are things making sense? Am I moving too fast in one area or neglecting another? While I certainly
have my own plans laid out for the remaining chapters, they aren’t yet set in stone (since the
chapters have yet to be written!).</p>
<h3 id="the-contents">The Contents</h3>
<p>I’m still writing the book, of course, but I do have an idea of where we are going. During the
course of the book, we’ll be building a real-time auction site like ebay step-by-step. Each chapter
will use the knowledge you gained from the preceding chapters to build up to more complex topics. By
the end, we’ll have a functioning auction site that has real-time interactions via channels and even
provides an API (just in case you want to build a mobile app or something). If you don’t
know Elixir at all, there is even a chapter introducing the main pieces of the Elixir language and
includes resources on where to dig deeper if you require it.</p>
<h2 id="thanks">Thanks!</h2>
<p>Thanks for visiting my site and I sincerely hope that you have learned something from my blog
posts. If you have, please also consider purchasing <em><a href="https://www.manning.com/books/phoenix-in-action?a_aid=geolessel&a_bid=80a2cec0">Phoenix in Action</a></em>
as well.</p>Geoffrey LesselIf you’ve been visiting this site with any sort of regularity, you’ll notice that it’s been quite a long time since my last blog post. Some of you might not care, and others of you might be wondering what is going on. This is an update of sorts.Introducing ReactPhoenix - Render React.js components in Phoenix views2017-04-20T00:00:00-07:002017-04-20T00:00:00-07:00/2017/introducing-react-phoenix<blockquote>
<p>Check out the package at <a href="https://github.com/geolessel/react-phoenix">https://github.com/geolessel/react-phoenix</a></p>
</blockquote>
<p>While I love Elixir and Phoenix, my current daily, pay-the-bills
language at <a href="https://planning.center">Planning Center</a> is Ruby and Rails. We create a lot
of <a href="https://facebook.github.io/react/">React.js</a> components as well and I really like working with
React. So it is an obvious transition for me to want to use React
components in my Phoenix applications as well.</p>
<p>Every time I started a new Phoenix application, it took me quite a while to
remember how to get everything set up in Phoenix in order to display a React
component. Beyond that, most of the tutorials I found that dealt with React and
Brunch (vs Webpack) only rendered a single component. I’m more interested in
React sprinkles in my app and the global rendering on every page load wasn’t
working for me. I saw a hole in the current offerings of packages dealing with
Phoenix and React and I decided to fill it.</p>
<h2 id="reactphoenix">ReactPhoenix</h2>
<p>I created a <a href="https://github.com/geolessel/react-phoenix">package named react_phoenix</a> to make it so much easier to get React
components into your Phoenix views. It is heavily inspired by
the <a href="https://github.com/reactjs/react-rails">react-rails</a> gem that I use every day. I wanted something with as
little installation pains as possible and that got out of my way when all I
wanted to do was render a component in a view. Here’s how it works.</p>
<h3 id="prerequisites">Prerequisites</h3>
<p>To begin with, your Phoenix app should already have React installed as a
dependency. Beyond that, I recommend that you have a couple of Babel presets
installed and active as well in order for brunch to know how to compile those
javascript component files.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> npm install react babel-preset-react babel-preset-env --save
</code></pre></div></div>
<p>Activate the presets in <code class="highlighter-rouge">babel</code> by adding this to your <code class="highlighter-rouge">brunch-config.js</code> file.</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">exports</span><span class="p">.</span><span class="nx">config</span> <span class="o">=</span> <span class="p">{</span>
<span class="c1">// ...</span>
<span class="na">plugins</span><span class="p">:</span> <span class="p">{</span>
<span class="na">babel</span><span class="p">:</span> <span class="p">{</span>
<span class="na">presets</span><span class="p">:</span> <span class="p">[</span><span class="s2">"env"</span><span class="p">,</span> <span class="s2">"react"</span><span class="p">],</span>
<span class="na">ignore</span><span class="p">:</span> <span class="p">[</span><span class="sr">/web</span><span class="se">\/</span><span class="sr">static</span><span class="se">\/</span><span class="sr">vendor/</span><span class="p">]</span>
<span class="p">}</span>
<span class="p">},</span>
<span class="c1">// ...</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="installation">Installation</h3>
<p>I tried to make the <a href="https://github.com/geolessel/react-phoenix">README</a> for the package detailed as I could in this
part, but I’ll mirror it here. I should state that as of this writing (and the
official public unveiling), the package is at version 0.3.0, which is what these
steps pertain to. It is unlikely this part will change much in the future, but
just in case, you may want to check that <a href="https://github.com/geolessel/react-phoenix">README</a> if things don’t seem
to be working. Also, we will be using Phoenix 1.2. I haven’t tested this in
Phoenix 1.3 <em>quite</em> yet.</p>
<h4 id="step-1">Step 1</h4>
<p>Add ReactPhoenix as a dependency in your <code class="highlighter-rouge">mix.exs</code> file.</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="n">deps</span> <span class="k">do</span>
<span class="p">[</span>
<span class="p">{</span><span class="ss">:react_phoenix</span><span class="p">,</span> <span class="sd">"</span><span class="s2">~> 0.3.0"</span><span class="p">}</span>
<span class="p">]</span>
<span class="k">end</span>
</code></pre></div></div>
<p>then run <code class="highlighter-rouge">mix deps.get</code>.</p>
<h4 id="step-2">Step 2</h4>
<p>Add the included javascript helper to our <code class="highlighter-rouge">package.json</code> file (in the
dependencies section).</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="s2">"dependencies"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="s2">"phoenix"</span><span class="p">:</span><span class="w"> </span><span class="s2">"file:deps/phoenix"</span><span class="p">,</span><span class="w">
</span><span class="s2">"phoenix_html"</span><span class="p">:</span><span class="w"> </span><span class="s2">"file:deps/phoenix_html"</span><span class="p">,</span><span class="w">
</span><span class="s2">"react-phoenix"</span><span class="p">:</span><span class="w"> </span><span class="s2">"file:deps/react_phoenix"</span><span class="w"> </span><span class="err"><--</span><span class="w"> </span><span class="err">ADD</span><span class="w"> </span><span class="err">THIS!</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>then run <code class="highlighter-rouge">npm install</code>.</p>
<h4 id="step-3">Step 3</h4>
<p>Import the javascript file into our <code class="highlighter-rouge">web/static/js/app.js</code> bundle.</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="s2">"react-phoenix"</span>
</code></pre></div></div>
<h4 id="step-4-optional">Step 4 (optional)</h4>
<p>Sometimes it’s nice to be able to call just the function name and not the full
name of the function that includes the module name. In order to do so, edit your
<code class="highlighter-rouge">web/web.ex</code> file and add an <code class="highlighter-rouge">import</code> for ReactPhoenix.</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="n">view</span> <span class="k">do</span>
<span class="kn">quote</span> <span class="k">do</span>
<span class="kn">use</span> <span class="no">Phoenix</span><span class="o">.</span><span class="no">View</span><span class="p">,</span> <span class="ss">root:</span> <span class="sd">"</span><span class="s2">web/templates"</span>
<span class="kn">import</span> <span class="no">Phoenix</span><span class="o">.</span><span class="no">Controller</span><span class="p">,</span> <span class="ss">only:</span> <span class="p">[</span><span class="ss">get_csrf_token:</span> <span class="m">0</span><span class="p">,</span> <span class="ss">get_flash:</span> <span class="m">2</span><span class="p">,</span> <span class="ss">view_module:</span> <span class="m">1</span><span class="p">]</span>
<span class="kn">use</span> <span class="no">Phoenix</span><span class="o">.</span><span class="no">HTML</span>
<span class="kn">import</span> <span class="no">MyPhoenixApp</span><span class="o">.</span><span class="no">Router</span><span class="o">.</span><span class="no">Helpers</span>
<span class="kn">import</span> <span class="no">MyPhoenixApp</span><span class="o">.</span><span class="no">ErrorHelpers</span>
<span class="kn">import</span> <span class="no">MyPhoenixApp</span><span class="o">.</span><span class="no">Gettext</span>
<span class="kn">import</span> <span class="no">ReactPhoenix</span> <span class="c1"># <-- ADD THIS!</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<h2 id="lets-render-some-react">Let’s render some React!</h2>
<p>Now that we have everything set up, let’s render some React
components. Create a simple component we can use to make sure things
are hooked up correctly. In <code class="highlighter-rouge">web/static/js/components/hello.jsx</code>:</p>
<h3 id="create-a-component">Create a component</h3>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">React</span> <span class="k">from</span> <span class="s2">"react"</span>
<span class="k">export</span> <span class="k">default</span> <span class="kd">class</span> <span class="nx">Hello</span> <span class="kd">extends</span> <span class="nx">React</span><span class="p">.</span><span class="nx">Component</span> <span class="p">{</span>
<span class="kd">constructor</span><span class="p">(</span><span class="nx">props</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="p">(</span><span class="nx">props</span><span class="p">)</span>
<span class="p">}</span>
<span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span>
<span class="o"><</span><span class="nx">h1</span><span class="o">></span><span class="nx">Hello</span> <span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">name</span><span class="p">}</span><span class="o"><</span><span class="sr">/h1</span><span class="err">>
</span> <span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="import-the-component"><code class="highlighter-rouge">import</code> the component</h3>
<p>Let our javascript environment know that this component exists
and we’d like to make it available in <code class="highlighter-rouge">web/static/js/app.js</code>.</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">Hello</span> <span class="k">from</span> <span class="s2">"./components/hello"</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">Components</span> <span class="o">=</span> <span class="p">{</span>
<span class="nx">Hello</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="render-the-component">Render the component</h3>
<p>Use ReactPhoenix to render this component in a Phoenix view:</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o"><%=</span> <span class="n">react_component</span><span class="p">(</span><span class="sd">"</span><span class="s2">Components.Hello"</span><span class="p">,</span> <span class="p">%{</span><span class="ss">name:</span> <span class="sd">"</span><span class="s2">React"</span><span class="p">})</span> <span class="p">%</span><span class="o">></span>
<span class="c1"># or, if you haven't added import ReactPhoenix in web/web.ex</span>
<span class="o"><%=</span> <span class="no">ReactPhoenix</span><span class="o">.</span><span class="n">react_component</span><span class="p">(</span><span class="sd">"</span><span class="s2">Components.Hello"</span><span class="p">,</span> <span class="p">%{</span><span class="ss">name:</span> <span class="sd">"</span><span class="s2">React"</span><span class="p">})</span> <span class="p">%</span><span class="o">></span>
</code></pre></div></div>
<p>Here we are telling ReactPhoenix that we’d like to render a component registered
as <code class="highlighter-rouge">Components.Hello</code> (which we did in <code class="highlighter-rouge">app.js</code>) and pass it props of <code class="highlighter-rouge">name: "React"</code>.</p>
<h3 id="view-the-results">View the results</h3>
<p>And that’s it! You should be able to reload your view and see your React
component lovingly rendered with the specified props.</p>
<h2 id="a-little-video-version">A little video version</h2>
<p>I took a quick video of me setting up a new Phoenix app and getting
React in it with ReactPhoenix. I go from <code class="highlighter-rouge">mix phoenix.new</code> to getting
a React component rendered in a view in 4 minutes (including
typos!).</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/icwjAbck8yk" frameborder="0" allowfullscreen=""></iframe>
<h2 id="the-future">The future</h2>
<p>I’d love to keep expanding this and making it better. I’ve already got it
working in a Phoenix app that does server-side React component rendering (using
a couple of other packages and the <code class="highlighter-rouge">target_id</code> option in
<code class="highlighter-rouge">ReactPhoenix.react_component/3</code>) but there are ways I can make this even
better. PLEASE reach out to me with any issues or suggestions you have to
make it better, easier, more robust, faster, whatever. I’d love to hear them.</p>
<p>You can check out the hex docs at
<a href="https://hexdocs.pm/react_phoenix/readme.html">https://hexdocs.pm/react_phoenix/readme.html</a>
and see the code on GitHub
at <a href="https://github.com/geolessel/react-phoenix">https://github.com/geolessel/react-phoenix</a>.</p>
<p>Get in touch with me on <a href="https://twitter.com/geolessel">twitter at @geolessel</a> and <strong>@geo</strong> in the
Elixir slack group. I also have a newsletter that I’d be grateful if you signed
up for below. I have some fun ideas brewing that I’d love to keep you informed
about in the future. Thanks!</p>Geoffrey LesselCheck out the package at https://github.com/geolessel/react-phoenixUsing Ecto.Multi To Group Database Operations2017-01-08T00:00:00-08:002017-01-08T00:00:00-08:00/2017/using-ecto-multi-to-group-database-operations<p>Have you ever had a situation in which you had multiple database calls that
relied on the success of the previous call? Normally it would be a pain to check
each database success/failure status and then continue on in the chain. This
can lead to many nested statements which can get nasty really fast. Beyond that,
if a deeply-nested operation fails, all previous operations have to be rolled back.</p>
<p>One of the cool things to come out of Ecto 2.0 is the module <code class="highlighter-rouge">Ecto.Multi</code>. It
is designed to be a way to group database calls into a single transaction so that
the group fails if one fails. In other words, it’s all or nothing. This takes care
of the problem we see above. There are no multiple levels of nesting and the API
is actually really nice to work with. Let’s take a look!</p>
<h2 id="the-situation">The Situation</h2>
<p>Let’s say for this example that you have an application running that tracks the
life of a door. This door may be guarding some multi-million dollar server room
or it could be your front door. Every person that has the clearance to open the
door has some sort of passkey that identifies them and allows them to unlock the door and
enter.</p>
<p>For the sake of a simple demonstration of <code class="highlighter-rouge">Ecto.Multi</code>, we’ll also say that this
app ins’t the best designed app you’ve ever seen and the way it utilizes the database
is pretty dreadful. I put this disclaimer here because this example is very contrived
and the calls we are making are strictly to demonstrate <code class="highlighter-rouge">Ecto.Multi</code> and <em>not</em> how
to successfully design and architect an app.</p>
<p>With that out of the way, let’s dig deeper.</p>
<h3 id="what-do-we-want-to-track">What do we want to track?</h3>
<p>Our application and database know about a few things:</p>
<ol>
<li>Doors</li>
<li>People</li>
<li>Entries (a Person entering a Door)</li>
<li>Logs (a very simple secondary log of Entries)</li>
</ol>
<p>For our simple demo app, whenever a Person attempts to unlock and enter a Door, we want to record the Entry,
update a cached count of how many entries a Person has made, and then make a Log
record about the Entry. If any one of those fails, we don’t want to save any of them.
If we can’t record the Log, we don’t want to record the Entry nor update the Person’s
entry count.</p>
<h2 id="the-old-way">The Old Way</h2>
<p>One version of what the pre-<code class="highlighter-rouge">Ecto.Multi</code> way of doing things might look like is
the following (simplified):</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">defmodule</span> <span class="no">Guard</span> <span class="k">do</span>
<span class="k">def</span> <span class="n">enter_door</span><span class="p">(</span><span class="n">person</span><span class="p">,</span> <span class="n">door</span><span class="p">)</span> <span class="k">do</span>
<span class="k">case</span> <span class="no">Repo</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="no">Entry</span><span class="o">.</span><span class="n">changeset</span><span class="p">(%</span><span class="no">Entry</span><span class="p">{},</span> <span class="p">%{</span><span class="ss">door_id:</span> <span class="n">door</span><span class="o">.</span><span class="n">id</span><span class="p">,</span> <span class="ss">person_id:</span> <span class="n">person</span><span class="o">.</span><span class="n">id</span><span class="p">})</span> <span class="k">do</span>
<span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">entry</span><span class="p">}</span> <span class="o">-></span>
<span class="k">case</span> <span class="no">Repo</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="no">Person</span><span class="o">.</span><span class="n">increase_entry_count_changeset</span><span class="p">(</span><span class="n">person</span><span class="p">))</span> <span class="k">do</span>
<span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">person</span><span class="p">}</span> <span class="o">-></span>
<span class="k">case</span> <span class="no">Repo</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="no">Log</span><span class="o">.</span><span class="n">changeset</span><span class="p">(%</span><span class="no">Log</span><span class="p">{},</span> <span class="p">%{</span><span class="ss">text:</span> <span class="sd">"</span><span class="s2">entry"</span><span class="p">}))</span> <span class="k">do</span>
<span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">log</span><span class="p">}</span> <span class="o">-></span>
<span class="sd">"</span><span class="s2">Success on all three!"</span>
<span class="p">{</span><span class="ss">:error</span><span class="p">,</span> <span class="n">_changeset</span><span class="p">}</span> <span class="o">-></span>
<span class="c1"># rollback Person and Entry database operations</span>
<span class="sd">"</span><span class="s2">Failed to save Log"</span>
<span class="k">end</span>
<span class="p">{</span><span class="ss">:error</span><span class="p">,</span> <span class="n">_changeset</span><span class="p">}</span> <span class="o">-></span>
<span class="c1"># rollback Entry database operation</span>
<span class="sd">"</span><span class="s2">Failed to save Person"</span>
<span class="k">end</span>
<span class="p">{</span><span class="ss">:error</span><span class="p">,</span> <span class="n">_changeset</span><span class="p">}</span> <span class="o">-></span>
<span class="sd">"</span><span class="s2">Failed to save Entry"</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<p>Have you ever written code like this before, perhaps in a Phoenix controller? I have.
I admit that the example is contrived in order to increase the nesting, but I have
a similar nesting structure running in a production app right now (don’t judge).</p>
<h2 id="the-ectomulti-way">The <code class="highlighter-rouge">Ecto.Multi</code> Way</h2>
<p>This is where <code class="highlighter-rouge">Ecto.Multi</code> swoops in to save the day. Let’s take a look at what it
would look like and then we’ll break it down a bit.</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">defmodule</span> <span class="no">Guard</span> <span class="k">do</span>
<span class="k">def</span> <span class="n">enter_door</span><span class="p">(</span><span class="n">person</span><span class="p">,</span> <span class="n">door</span><span class="p">)</span> <span class="k">do</span>
<span class="k">case</span> <span class="n">entry_transaction</span><span class="p">(</span><span class="n">person</span><span class="p">,</span> <span class="n">door</span><span class="p">)</span> <span class="k">do</span>
<span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="p">%{</span><span class="ss">entry:</span> <span class="n">entry</span><span class="p">,</span> <span class="ss">log:</span> <span class="n">log</span><span class="p">,</span> <span class="ss">person:</span> <span class="n">person</span><span class="p">}}</span> <span class="o">-></span>
<span class="no">Logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sd">"</span><span class="s2">Success on all three!"</span><span class="p">)</span>
<span class="p">{</span><span class="ss">:error</span><span class="p">,</span> <span class="ss">:log</span><span class="p">,</span> <span class="n">_failed_value</span><span class="p">,</span> <span class="n">_changes_successful</span><span class="p">}</span> <span class="o">-></span>
<span class="no">Logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sd">"</span><span class="s2">Failed to save Log"</span><span class="p">)</span>
<span class="p">{</span><span class="ss">:error</span><span class="p">,</span> <span class="ss">:person</span><span class="p">,</span> <span class="n">_failed_value</span><span class="p">,</span> <span class="n">_changes_successful</span><span class="p">}</span> <span class="o">-></span>
<span class="no">Logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sd">"</span><span class="s2">Failed to save Person"</span><span class="p">)</span>
<span class="p">{</span><span class="ss">:error</span><span class="p">,</span> <span class="ss">:entry</span><span class="p">,</span> <span class="n">_failed_value</span><span class="p">,</span> <span class="n">_changes_successful</span><span class="p">}</span> <span class="o">-></span>
<span class="no">Logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sd">"</span><span class="s2">Failed to save Entry"</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">def</span> <span class="n">entry_transaction</span><span class="p">(</span><span class="n">person</span><span class="p">,</span> <span class="n">door</span><span class="p">)</span> <span class="k">do</span>
<span class="no">Multi</span><span class="o">.</span><span class="n">new</span>
<span class="o">|></span> <span class="no">Multi</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="ss">:entry</span><span class="p">,</span> <span class="no">Entry</span><span class="o">.</span><span class="n">changeset</span><span class="p">(%</span><span class="no">Entry</span><span class="p">{},</span> <span class="p">%{</span><span class="ss">door_id:</span> <span class="n">door</span><span class="o">.</span><span class="n">id</span><span class="p">,</span> <span class="ss">person_id:</span> <span class="n">person</span><span class="o">.</span><span class="n">id</span><span class="p">}})</span>
<span class="o">|></span> <span class="no">Multi</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="ss">:person</span><span class="p">,</span> <span class="no">Person</span><span class="o">.</span><span class="n">increase_entry_count_changeset</span><span class="p">(</span><span class="n">person</span><span class="p">))</span>
<span class="o">|></span> <span class="no">Multi</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="ss">:log</span><span class="p">,</span> <span class="no">Log</span><span class="o">.</span><span class="n">changeset</span><span class="p">(%</span><span class="no">Log</span><span class="p">{},</span> <span class="p">%{</span><span class="n">text</span><span class="p">,</span> <span class="sd">"</span><span class="s2">entry"</span><span class="p">}))</span>
<span class="o">|></span> <span class="no">Repo</span><span class="o">.</span><span class="n">transaction</span><span class="p">()</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<p>First, let’s tackle the <code class="highlighter-rouge">entry_transaction</code> function. The <code class="highlighter-rouge">Ecto.Multi</code> module has
functions available that correspond with <code class="highlighter-rouge">Ecto.Repo</code> functions like <code class="highlighter-rouge">insert</code>, <code class="highlighter-rouge">delete</code>,
and <code class="highlighter-rouge">update</code> (to name just a few). However, these accept a new second argument that
is identified in the <a href="https://hexdocs.pm/ecto/2.1.2/Ecto.Multi.html">docs</a> as <code class="highlighter-rouge">name</code>. The <code class="highlighter-rouge">name</code> argument lets you name a particular
portion of the grouped transactions in order to pattern match on it later. In my example
above, I’ve simply named them after the record that is being dealt with. We’ll come
back to this later when we check out our <code class="highlighter-rouge">enter_door</code> function. Note that these
have to be unique within the transaction grouping.</p>
<p>Each step of the transaction passes the <code class="highlighter-rouge">Multi.new</code> result down through the chain
until eventually, after building up our transaction a piece at a time, we tell our
<code class="highlighter-rouge">Repo</code> to actually make the calls.</p>
<p>If at any point in the steps a changeset has errors or the database call itself
fails for one reason or another, none of the database calls will be persisted.
The particular call that fails the transaction will be returned in a tuple with
the form of <code class="highlighter-rouge">{:error, name_of_call, failed_value, changes_that_succeeded}</code>. The
<code class="highlighter-rouge">failed_value</code> will contain the changeset errors (if using a changeset) or any
other error values the call returned. <code class="highlighter-rouge">changes_that_succeeded</code> will contain the
results of any previous operations that were successful. However, as the <a href="https://hexdocs.pm/ecto/2.1.2/Ecto.Multi.html">docs</a>
state,</p>
<blockquote>
<p>any successful operations would have been rolled back</p>
</blockquote>
<p>because the transaction itself failed.</p>
<p>This is now where we can take a look at the <code class="highlighter-rouge">enter_door</code> function. If the transaction
fails, we’ve already seen how it will return the <code class="highlighter-rouge">{:error, ...}</code> tuple for us
to deal with how we’d like. If it succeeds, it will return a tuple with
<code class="highlighter-rouge">{:ok, map}</code>. In <code class="highlighter-rouge">map</code>, we can access the success values of each of the individual
operations from the value of what we <code class="highlighter-rouge">name</code>d that particular operation. So in
our example the <code class="highlighter-rouge">:entry</code> key in <code class="highlighter-rouge">map</code> would correspond with the result of the operation:</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Multi</span><span class="o">.</span><span class="n">new</span>
<span class="o">|></span> <span class="no">Multi</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="ss">:entry</span><span class="p">,</span> <span class="no">Entry</span><span class="o">.</span><span class="n">changeset</span><span class="p">(%</span><span class="no">Entry</span><span class="p">{},</span> <span class="p">%{</span><span class="o">...</span><span class="p">}))</span>
<span class="c1"># ^^^^^^ where we named our operation</span>
<span class="o">...</span>
</code></pre></div></div>
<p>This gives us enormous power to ensure that database calls that belong together
as a whole unit live and die together as a unit.</p>
<h2 id="using-ectomultirun-to-execute-arbitrary-functions">Using <code class="highlighter-rouge">Ecto.Multi.run</code> To Execute Arbitrary Functions</h2>
<p>Another thing we can do with <code class="highlighter-rouge">Ecto.Multi</code> is utilize the <code class="highlighter-rouge">run/3</code> function to
execute arbitrary code within which we will have the result of any previously-run
and successful operations. Let’s take a look at an example.</p>
<p>Pretend we want to ensure that a Person doesn’t enter a Door more than 10 times.
Something like that would normally (and probably should still) be done inside
a changeset for <code class="highlighter-rouge">Person</code>, but in our case, let’s utilize <code class="highlighter-rouge">run/3</code> to check whether
our Person now has more than 10 entries:</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">defmodule</span> <span class="no">Guard</span> <span class="k">do</span>
<span class="n">alias</span> <span class="no">Ecto</span><span class="o">.</span><span class="no">Multi</span>
<span class="k">def</span> <span class="n">enter_door</span><span class="p">(</span><span class="n">person</span><span class="p">,</span> <span class="n">door</span><span class="p">)</span> <span class="k">do</span>
<span class="k">case</span> <span class="n">entry_transaction</span><span class="p">(</span><span class="n">person</span><span class="p">,</span> <span class="n">door</span><span class="p">)</span> <span class="k">do</span>
<span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="p">%{</span><span class="ss">entry:</span> <span class="n">entry</span><span class="p">,</span> <span class="ss">log:</span> <span class="n">log</span><span class="p">,</span> <span class="ss">person:</span> <span class="n">person</span><span class="p">}}</span> <span class="o">-></span>
<span class="no">Logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sd">"</span><span class="s2">Success on all four!"</span><span class="p">)</span>
<span class="p">{</span><span class="ss">:error</span><span class="p">,</span> <span class="ss">:allowed</span><span class="p">,</span> <span class="n">_failed_value</span><span class="p">,</span> <span class="n">_changes_successful</span><span class="p">}</span> <span class="o">-></span>
<span class="no">Logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sd">"</span><span class="s2">Failed to pass allowed check"</span><span class="p">)</span>
<span class="c1"># ... other pattern matched failures</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">def</span> <span class="n">entry_transaction</span><span class="p">(</span><span class="n">person</span><span class="p">,</span> <span class="n">door</span><span class="p">)</span> <span class="k">do</span>
<span class="no">Multi</span><span class="o">.</span><span class="n">new</span>
<span class="o">|></span> <span class="no">Multi</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="ss">:entry</span><span class="p">,</span> <span class="no">Entry</span><span class="o">.</span><span class="n">changeset</span><span class="p">(%</span><span class="no">Entry</span><span class="p">{},</span> <span class="p">%{</span><span class="ss">door_id:</span> <span class="n">door</span><span class="o">.</span><span class="n">id</span><span class="p">,</span> <span class="ss">person_id:</span> <span class="n">person</span><span class="o">.</span><span class="n">id</span><span class="p">}})</span>
<span class="o">|></span> <span class="no">Multi</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="ss">:person</span><span class="p">,</span> <span class="no">Person</span><span class="o">.</span><span class="n">increase_entry_count_changeset</span><span class="p">(</span><span class="n">person</span><span class="p">))</span>
<span class="o">|></span> <span class="no">Mutli</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="ss">:allowed</span><span class="p">,</span> <span class="k">fn</span><span class="p">(%{</span><span class="ss">person:</span> <span class="n">person</span><span class="p">})</span> <span class="o">-></span> <span class="c1"># NEW CODE</span>
<span class="k">if</span> <span class="n">person</span><span class="o">.</span><span class="n">entry_count</span> <span class="o">></span> <span class="m">10</span> <span class="k">do</span> <span class="c1"># </span>
<span class="p">{</span><span class="ss">:error</span><span class="p">,</span> <span class="sd">"</span><span class="s2">Person entered more than 10 times"</span><span class="p">}</span> <span class="c1"># </span>
<span class="k">else</span> <span class="c1"># </span>
<span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="sd">"</span><span class="s2">continue"</span><span class="p">}</span> <span class="c1"># </span>
<span class="k">end</span> <span class="c1"># </span>
<span class="k">end</span><span class="p">)</span> <span class="c1"># </span>
<span class="o">|></span> <span class="no">Multi</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="ss">:log</span><span class="p">,</span> <span class="no">Log</span><span class="o">.</span><span class="n">changeset</span><span class="p">(%</span><span class="no">Log</span><span class="p">{},</span> <span class="p">%{</span><span class="n">text</span><span class="p">,</span> <span class="sd">"</span><span class="s2">entry"</span><span class="p">}))</span>
<span class="o">|></span> <span class="no">Repo</span><span class="o">.</span><span class="n">transaction</span><span class="p">()</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<p><code class="highlighter-rouge">Ecto.Multi.run</code> expects to receive a tuple containing either <code class="highlighter-rouge">{:ok, message}</code> or
<code class="highlighter-rouge">{:error, message}</code> in order to determine whether to continue the transaction.</p>
<p>We can see above that we pattern match on the <code class="highlighter-rouge">Person</code> which has the <em>result</em> of
successfully running the <code class="highlighter-rouge">update</code> operation above it. So if before the operation
the Person had 10 entries, after the successful <code class="highlighter-rouge">update</code>, it would have 11 and
trigger the failure. The error message would be passed on into the pattern-matched
failure in the <code class="highlighter-rouge">case</code> statement.</p>
<h2 id="a-powerful-feature-with-an-easy-api">A Powerful Feature With An Easy API</h2>
<p>Overall, I’m very impressed with <code class="highlighter-rouge">Ecto.Multi</code> and what it can do. It allows you
to stop worrying about rolling back previous successful operations if another
dependent operation fails for some reason. Beyond that, being able to pattern
match on the errorred transaction is huge and allows you to take action
depending on what actually failed.</p>
<p>I know that I will be using this extensively in the future and will likely go
back to my old code and update it to use it as well.</p>
<p>As always, I’d love questions or any other feedback you have on this post.
I’m <a href="https://twitter.com/geolessel">@geolessel</a> on Twitter and <strong>@geo</strong> in the Elixir slack group.
Also, I’d be grateful if you signed up for my email list
below. I’m brainstorming some neat things that I’d like to release in
the near future and I’d love to let you be the first to know about
it. Thanks!</p>Geoffrey LesselHave you ever had a situation in which you had multiple database calls that relied on the success of the previous call? Normally it would be a pain to check each database success/failure status and then continue on in the chain. This can lead to many nested statements which can get nasty really fast. Beyond that, if a deeply-nested operation fails, all previous operations have to be rolled back.Live-coding FizzBuzz In Elixir Lightning Talk2016-11-07T00:00:00-08:002016-11-07T00:00:00-08:00/2016/livecoding-fizzbuzz-in-elixir-lightning-talk<p>Last week at a <a href="https://www.meetup.com/fullstacktalks/">local tech meetup</a>, I presented a quick 6-minute lightning talk on Elixir. In it, I quickly implemented the infamous <a href="https://imranontech.com/2007/01/24/using-fizzbuzz-to-find-developers-who-grok-coding/">FizzBuzz</a> programming challenge in Elixir. While doing so, I briefly demonstrated some of the things I like about Elixir such as:</p>
<ul>
<li>Pattern matching</li>
<li>Function overloading</li>
<li>Guard clauses</li>
<li>Multiple shortcuts for function definitions</li>
</ul>
<p>So please take 6-7 minutes and watch it then let me know what you think! Keep in mind it was a lightning talk format and it was for a crowd of people who are at an introductory level of Elixir exposure.</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/BYcFLvIecoc" frameborder="0" allowfullscreen=""></iframe>
<h2 id="code">Code</h2>
<p>Here’s the final code of my implementation in case you’re interested:</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">defmodule</span> <span class="no">FizzBuzz</span> <span class="k">do</span>
<span class="k">def</span> <span class="n">go</span><span class="p">(</span><span class="n">min</span><span class="p">,</span> <span class="n">max</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="no">Enum</span><span class="o">.</span><span class="n">each</span><span class="p">(</span><span class="n">min</span><span class="o">..</span><span class="n">max</span><span class="p">,</span> <span class="o">&</span><span class="n">go</span><span class="o">/</span><span class="m">1</span><span class="p">)</span>
<span class="k">def</span> <span class="n">go</span><span class="p">(</span><span class="n">num</span><span class="p">)</span> <span class="ow">when</span> <span class="p">(</span><span class="n">rem</span><span class="p">(</span><span class="n">num</span><span class="p">,</span> <span class="m">15</span><span class="p">)</span> <span class="o">==</span> <span class="m">0</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="sd">"</span><span class="s2">fizzbuzz"</span>
<span class="k">def</span> <span class="n">go</span><span class="p">(</span><span class="n">num</span><span class="p">)</span> <span class="ow">when</span> <span class="p">(</span><span class="n">rem</span><span class="p">(</span><span class="n">num</span><span class="p">,</span> <span class="m">3</span><span class="p">)</span> <span class="o">==</span> <span class="m">0</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="sd">"</span><span class="s2">fizz"</span>
<span class="k">def</span> <span class="n">go</span><span class="p">(</span><span class="n">num</span><span class="p">)</span> <span class="ow">when</span> <span class="p">(</span><span class="n">rem</span><span class="p">(</span><span class="n">num</span><span class="p">,</span> <span class="m">5</span><span class="p">)</span> <span class="o">==</span> <span class="m">0</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="sd">"</span><span class="s2">buzz"</span>
<span class="k">def</span> <span class="n">go</span><span class="p">(</span><span class="n">num</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="n">num</span>
<span class="k">end</span>
</code></pre></div></div>Geoffrey LesselLast week at a local tech meetup, I presented a quick 6-minute lightning talk on Elixir. In it, I quickly implemented the infamous FizzBuzz programming challenge in Elixir. While doing so, I briefly demonstrated some of the things I like about Elixir such as:Hosting A Phoenix App In A Subdirectory With Nginx2016-10-25T00:00:00-07:002016-10-25T00:00:00-07:00/2016/hosting-a-phoenix-app-in-a-subdirectory-with-nginx<p>When I <a href="http://geoffreylessel.com/2016/fighter-verses">deployed my Fighter Verses project</a> a few weeks ago, I
struggled deciding where and how to host it. Would it be hosted with
it’s own domain name? Would it be a subdomain of geoffreylessel.com?
There were a few reasons I finally decided on hosting it in a
subdirectory of this site (<a href="http://geoffreylessel.com/fighter-verses">geoffreylessel.com/fighter-verses</a>). I
won’t go into those specific reasons, but ease of hosting and setup is
not one of them.</p>
<h2 id="the-easy-path">The easy path</h2>
<p>I developed the small site in Phoenix. I had successfully deployed
Phoenix apps to production before but they have to this point been
hosted in their own domain names. I didn’t foresee hosting a Phoenix
app under a subdomain of a current site as a big issue, but it turned
out to be a lot more trial and error than I anticipated.</p>
<p>One of the reasons I started writing these technical posts was so that
I could remember what I had learned about the subjects that interested
me. To be completely honest, I still go back and re-read some of my
own posts to remind myself how I did something. I am very thankful
that other people have seemed to learn a few things with me along the
way.</p>
<p>With those things in mind, I’d like to go through the steps I needed
to take to make my Phoenix app work in a current subdirectory.</p>
<p>We’ll break this down into two major sections of configuration:</p>
<ol>
<li><a href="#nginx">Nginx</a></li>
<li><a href="#phoenix">Phoenix</a></li>
</ol>
<h2 id="nginx">Nginx</h2>
<h3 id="the-current-setup">The current setup</h3>
<p>This blog site itself is generated with <a href="http://jekyllrb.com/">jekyll</a> and I love
how easy it is to make it work. I just upload the html files that it
generates and <em>boom</em>, a static html site is live.</p>
<p>I have chosen to host the site with <a href="http://nginx.org/en/docs/">nginx</a> because I’m
somewhat familiar with the configuration of it and have some
experience with it. It has served me well so far.</p>
<p>A sample of my nignx configuration pre-Phoenix app is below:</p>
<div class="language-nginx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">server</span> <span class="p">{</span>
<span class="kn">server_name</span> <span class="s">geoffreylessel.com</span> <span class="s">www.geoffreylessel.com</span><span class="p">;</span>
<span class="kn">listen</span> <span class="mi">80</span> <span class="s">default_server</span><span class="p">;</span>
<span class="kn">listen</span> <span class="s">[::]:80</span> <span class="s">default_server</span><span class="p">;</span>
<span class="kn">root</span> <span class="n">/srv/www/geoffreylessel.com/htdocs</span><span class="p">;</span>
<span class="kn">index</span> <span class="s">index.html</span><span class="p">;</span>
<span class="kn">location</span> <span class="n">/</span> <span class="p">{</span>
<span class="c1"># First attempt to serve request as file, then
</span> <span class="c1"># as directory, then fall back to displaying a 404.
</span> <span class="kn">try_files</span> <span class="nv">$uri</span> <span class="nv">$uri</span><span class="n">/</span> <span class="p">=</span><span class="mi">404</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This is pretty straight-forward and if you’ve ever set up a simple
hosted site with nginx most things will look familiar. I won’t go into
detail about what each directive does as you can pretty easily find
those topics elsewhere on the web and does not fit into the focus of
this post.</p>
<h3 id="hosting-a-phoenix-app-with-nginx">Hosting a phoenix app with nginx</h3>
<p>In order to contrast the setup for hosting within a subdirectory and
hosting regularly, I’d like to walk through how to set up basic
hosting of a Phoenix app with Nginx. The basic gist is to set up a
proxy that passes requests through to Erlang’s Cowboy server, which is
what Phoenix uses as its web server.</p>
<p>Let’s set up an example config file as if my Fighter Verses landing
page were hosted on its own domain name. Since we have to give it a
name in our configs, let’s just call it fvlandingpagebygeo.com.</p>
<p>We’d start out the config file with setting an <a href="http://nginx.org/en/docs/http/ngx_http_upstream_module.html#upstream">upstream proxy</a>.</p>
<div class="language-nginx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">upstream</span> <span class="s">fvlandingpagebygeo_phoenix</span> <span class="p">{</span>
<span class="kn">server</span> <span class="nf">127.0.0.1</span><span class="p">:</span><span class="mi">8900</span>
<span class="err">}</span>
</code></pre></div></div>
<p>Next, let’s tell nginx to pass all the requests coming in to port 80
on our server that is asking for fvlandingpagebygeo.com on to our
proxy:</p>
<div class="language-nginx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">server</span> <span class="p">{</span>
<span class="kn">server_name</span> <span class="s">fvlandingpagebygeo.com</span> <span class="s">www.fvlandingpagebygeo.com</span><span class="p">;</span>
<span class="kn">listen</span> <span class="mi">80</span> <span class="s">default_server</span><span class="p">;</span>
<span class="kn">listen</span> <span class="s">[::]:80</span> <span class="s">default_server</span><span class="p">;</span>
<span class="kn">root</span> <span class="n">/srv/www/fvlandingpagebygeo.com/htdocs</span><span class="p">;</span>
<span class="kn">index</span> <span class="s">index.html</span><span class="p">;</span>
<span class="kn">location</span> <span class="n">/</span> <span class="p">{</span>
<span class="c1"># pass the requests on to our proxy
</span> <span class="kn">try_files</span> <span class="nv">$uri</span> <span class="s">@proxy</span><span class="p">;</span>
<span class="p">}</span>
<span class="kn">location</span> <span class="s">@proxy</span> <span class="p">{</span>
<span class="kn">include</span> <span class="s">proxy_params</span><span class="p">;</span>
<span class="kn">proxy_redirect</span> <span class="no">off</span><span class="p">;</span>
<span class="kn">proxy_pass</span> <span class="s">http://fvlandingpagebygeo_phoenix</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="now-how-about-that-subdirectory">Now how about that subdirectory?</h3>
<p>Above we have one config file that sets up static html hosting and
another that passes all requests on to a proxy that then routes
requests to our Phoenix app. We need to combine them in order to now
host that Phoenix app in a subdirectory of our static html site.</p>
<div class="language-nginx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">upstream</span> <span class="s">fvlandingpagebygeo_phoenix</span> <span class="p">{</span>
<span class="kn">server</span> <span class="nf">127.0.0.1</span><span class="p">:</span><span class="mi">8900</span>
<span class="err">}</span>
<span class="s">server</span> <span class="p">{</span>
<span class="kn">server_name</span> <span class="s">geoffreylessel.com</span> <span class="s">www.geoffreylessel.com</span><span class="p">;</span>
<span class="kn">listen</span> <span class="mi">80</span> <span class="s">default_server</span><span class="p">;</span>
<span class="kn">listen</span> <span class="s">[::]:80</span> <span class="s">default_server</span><span class="p">;</span>
<span class="kn">root</span> <span class="n">/srv/www/geoffreylessel.com/htdocs</span><span class="p">;</span>
<span class="kn">index</span> <span class="s">index.html</span><span class="p">;</span>
<span class="kn">location</span> <span class="n">/</span> <span class="p">{</span>
<span class="kn">try_files</span> <span class="nv">$uri</span> <span class="nv">$uri</span><span class="n">/</span> <span class="p">=</span><span class="mi">404</span><span class="p">;</span>
<span class="p">}</span>
<span class="kn">location</span> <span class="n">/fighter-verses</span> <span class="p">{</span>
<span class="c1"># pass the requests on to our proxy
</span> <span class="kn">try_files</span> <span class="nv">$uri</span> <span class="s">@proxy</span><span class="p">;</span>
<span class="p">}</span>
<span class="kn">location</span> <span class="s">@proxy</span> <span class="p">{</span>
<span class="kn">include</span> <span class="s">proxy_params</span><span class="p">;</span>
<span class="kn">proxy_redirect</span> <span class="no">off</span><span class="p">;</span>
<span class="kn">proxy_pass</span> <span class="s">http://fvlandingpagebygeo_phoenix</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Now our config tells nginx to route most locations to the static html
site but to treat any request coming in with <code class="highlighter-rouge">/fighter-verses</code> as
special. It will pass those requests directly on to our proxy for our
Phoenix app. From there, it will be up to our Phoenix app to handle
the routing (as long as everything stays within that <code class="highlighter-rouge">/fighter-verses</code>
subdirectory).</p>
<h2 id="phoenix">Phoenix</h2>
<h3 id="confession">Confession</h3>
<p>To be completely honest, it took me awhile to figure out the
exact mixture of configuration needed in order to get the Phoenix app
to behave correctly. In theory, the following things need to
happen in order for the app to be successfully hosted in a
subdirectory:</p>
<ul>
<li>any compiled assets need to be prefixed with the subdirectory (<code class="highlighter-rouge">/fighter-verses</code>)</li>
<li>any <em>reference</em> to those assets needs to be prefixed with the subdirectory</li>
<li>all links should be prefixed with the subdirectory</li>
</ul>
<p>It turns out that Phoenix has handlers built-in that take care of
each of these issues. Let’s take a look at what I had to change in
order to get the subdirectory hosting working.</p>
<h3 id="static-assets">Static assets</h3>
<p>In order for my static assets to be referenced from within the
subdirectory, I needed to make this change in <code class="highlighter-rouge">lib/fv/endpoint.ex</code>:</p>
<h4 id="libfvendpointex"><code class="highlighter-rouge">lib/fv/endpoint.ex</code></h4>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">plug</span> <span class="no">Plug</span><span class="o">.</span><span class="no">Static</span><span class="p">,</span>
<span class="ss">at:</span> <span class="sd">"</span><span class="s2">/fighter-verses"</span><span class="p">,</span> <span class="ss">from:</span> <span class="ss">:fighter</span><span class="o">-</span><span class="n">verses</span><span class="p">,</span> <span class="ss">gzip:</span> <span class="no">false</span>
</code></pre></div></div>
<p>Originally, the line included <code class="highlighter-rouge">at: "/"</code> but we changed that.
This tells the static plug that we are serving our static files
beginning in our subdirectory and <em>not</em> from the root of the domain.</p>
<p>Beyond that, we also need to configure out production environment to
know that we are hosting from a subdirectory as well:</p>
<h4 id="configprodexs"><code class="highlighter-rouge">config/prod.exs</code></h4>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">config</span> <span class="ss">:fighter_verses</span><span class="p">,</span> <span class="no">FighterVerses</span><span class="o">.</span><span class="no">Endpoint</span><span class="p">,</span>
<span class="ss">http:</span> <span class="p">[</span><span class="ss">port:</span> <span class="p">{</span><span class="ss">:system</span><span class="p">,</span> <span class="sd">"</span><span class="s2">PORT"</span><span class="p">}],</span>
<span class="ss">url:</span> <span class="p">[</span><span class="ss">host:</span> <span class="sd">"</span><span class="s2">geoffreylessel.com"</span><span class="p">,</span> <span class="ss">port:</span> <span class="m">80</span><span class="p">],</span>
<span class="ss">cache_static_manifest:</span> <span class="sd">"</span><span class="s2">priv/static/manifest.json"</span><span class="p">,</span>
<span class="ss">static_url:</span> <span class="p">[</span><span class="ss">path:</span> <span class="sd">"</span><span class="s2">/fighter-verses"</span><span class="p">],</span>
<span class="ss">server:</span> <span class="no">true</span><span class="p">,</span>
<span class="ss">version:</span> <span class="no">Mix</span><span class="o">.</span><span class="no">Project</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="ss">:version</span><span class="p">]</span>
</code></pre></div></div>
<p>Again, the important addition here from what is generated
automatically with <code class="highlighter-rouge">mix phoenix.new</code> is the <code class="highlighter-rouge">static_url</code> key. We tell
it that the path we want everything under is <code class="highlighter-rouge">"/fighter-verses"</code>.</p>
<h3 id="routing">Routing</h3>
<p>Finally, we are going to need to change the Router to let it know that
we are handling requests to our app from with the subdirectory. Here’s
the updated, relevant information in my <code class="highlighter-rouge">web/router.ex</code> file:</p>
<h4 id="webrouterex"><code class="highlighter-rouge">web/router.ex</code></h4>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">scope</span> <span class="sd">"</span><span class="s2">/fighter-verses/"</span><span class="p">,</span> <span class="no">FighterVerses</span> <span class="k">do</span>
<span class="n">pipe_through</span> <span class="ss">:browser</span> <span class="c1"># Use the default browser stack</span>
<span class="n">get</span> <span class="sd">"</span><span class="s2">/"</span><span class="p">,</span> <span class="no">PageController</span><span class="p">,</span> <span class="ss">:index</span>
<span class="n">get</span> <span class="sd">"</span><span class="s2">/next"</span><span class="p">,</span> <span class="no">PageController</span><span class="p">,</span> <span class="ss">:next</span>
<span class="n">get</span> <span class="sd">"</span><span class="s2">/prev"</span><span class="p">,</span> <span class="no">PageController</span><span class="p">,</span> <span class="ss">:prev</span>
<span class="n">get</span> <span class="sd">"</span><span class="s2">/reset"</span><span class="p">,</span> <span class="no">PageController</span><span class="p">,</span> <span class="ss">:reset</span>
<span class="k">end</span>
</code></pre></div></div>
<p>After changing these configuration settings, any time we use
<code class="highlighter-rouge">*_path/2</code> the URL generated already has the <code class="highlighter-rouge">/fighter-verses</code>
prefix. I had to go through and change all my hard-coded paths from
<code class="highlighter-rouge">/</code> to use the helper (which I should have used in the first place.</p>
<h2 id="conclusion">Conclusion</h2>
<p>It turns out that once you know the secret sauce of what to change,
it’s not that difficult to set it up in this manner. But getting to
that point, to me, was rather difficult. Here are actual <code class="highlighter-rouge">git commit</code>
messages as I was trying to get it working:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dcb4155 * port 80
17825e7 * remove port from prod url
fa87868 * remove url
261e3b2 * change from localhost for prod
d189d4e * Change static path
3ae4ea1 * Work on links
5287820 * fighterverses
7eaae1a * ?
0d8953d * ugh
463c536 * Remove static paths
691dc7b * remove root config
06e9483 * modify endpoint to server static from /fighter-verses
83dfe56 * back to static_url
189e51d * change endpoint
978df36 * Try adding a path to url
792b1d7 * Change static_url for prod
</code></pre></div></div>
<p><strong>Note that this is <em>not</em> a post on good commit messages.</strong></p>
<p>It was a bit of a trial-and-error session as everything worked
locally, but I had to deploy every change to see if it worked on the
live production server. Hopefully this post will keep you from having
to do the same.</p>
<p>I’m going to provide a list of resources I consulted as I was working
on getting this working. Thanks for reading and let me know if you
have any questions. And as always, I’d love to hear your feedback. You
can contact me on Twitter at
<a href="https://twitter.com/geolessel">@geolessel</a> or in the Elixir Slack
group at @geo.</p>
<p><strong>And finally, please sign up for my mailing list below. I have a few cool things rattling
around in my mind that, if they ever come into existance, I’d love to let you know about.</strong></p>
<h2 id="resources">Resources</h2>
<ul>
<li><a href="http://nginx.org/en/docs/">Nginx docs</a></li>
<li><a href="http://nginx.org/en/docs/http/ngx_http_upstream_module.html#upstream">Nginx upstream docs</a></li>
<li><a href="https://hexdocs.pm/plug/1.2.2/Plug.Static.html">Phoenix Static Assets guide</a></li>
<li><a href="http://www.phoenixframework.org/docs/static-assets">Plug.Static docs</a></li>
<li><a href="https://hexdocs.pm/phoenix/1.2.1/Phoenix.Endpoint.html">Phoenix.Endpoint docs</a></li>
</ul>Geoffrey LesselWhen I deployed my Fighter Verses project a few weeks ago, I struggled deciding where and how to host it. Would it be hosted with it’s own domain name? Would it be a subdomain of geoffreylessel.com? There were a few reasons I finally decided on hosting it in a subdirectory of this site (geoffreylessel.com/fighter-verses). I won’t go into those specific reasons, but ease of hosting and setup is not one of them.Announcing My Fighter Verses Landing Page2016-10-05T00:00:00-07:002016-10-05T00:00:00-07:00/2016/fighter-verses<p>While most of my free-time development focus has been on Elixir apps that don’t use the web,
I’ve been playing a lot with the Phoenix framework as well. I deployed
one Phoenix app to production in the spring of this year and it has
been running great since then. However, it is a site that is built for
my family’s use and is not public. I wanted to do another little
website that I could use on a regular basis (and others could use as well).</p>
<h2 id="the-idea">The idea</h2>
<p>One of the things I’d like to do more of is memorizing Bible
verses. There is a memorization schedule called <a href="http://fighterverses.com">Fighter Verses</a>
that I’ve utilized in the past. There are phone apps and downloads for
the schedule. I wanted to be reminded often of this week’s verse and I
wanted it to look relatively nice at the same time.</p>
<p>So I thought up this little web app that would present the user with
their current verse (based on browser cookies) and use Trianglify to
create a dynamic background for each verse reference. It will have no
database and is super small. But it is something I find useful that is
running on my server.</p>
<p>I have this as my home page in my browser and am also going to
redesign the site so that it fill up a whole mobile screen allowing
you to screenshot it and use it on your lock screen.</p>
<h2 id="the-execution">The execution</h2>
<div style="display: flex; align-items: top; justify-content: center;">
<div width="50%" style="padding:1em 1em 1em 0;">
<img src="/images/fv-desktop.jpg" />
</div>
<div width="50%" style="padding:1em 0 1em 1em;">
<img src="/images/fv-mobile.jpg" />
</div>
</div>
<p>If you’d like to take a look at it too, go to
<a href="http://geoffreylessel.com/fighter-verses">geoffreylessel.com/fighter-verses</a>. The first time you visit, a
cookie will be stored in which the site will remember what verse you
are on (they are split up into <code class="highlighter-rouge">year:week</code> with 5 years of 52 weeks each).
To navigate, slide out the drawer on the right side of the screen with
the hamburger menu. There is some About information in there as well
in case you are interested.</p>
<p>I designed it first for mobile screens and it kinda stretches out for
larger screens. I’m not a designer, so I’m sure some of the design
implementation could be improved but it is good enough for me right
now to release it to the world.</p>
<h2 id="the-issues">The issues</h2>
<p>There are a few things I’d like to improve:</p>
<ol>
<li>
<p>On larger screens, Trianglify takes a while to generate the
background and insert it into the html. I think there are likely
better ways to go about doing that.</p>
</li>
<li>
<p>There are a couple design nits I’d like to pick and get squared
away.</p>
</li>
<li>
<p>The navigation between verses could use improving. If you come to
the site and are personally on year 3 week 22, you’d have to hit
the next button a lot of times before that verse shows up. I’d like
a direct way to get there.</p>
</li>
</ol>
<h2 id="it-is-shipped">It is shipped</h2>
<p>Even though there are things I’d like to change, I’m happy with
shipping it as-is. I’m trying to ship things more often and keep
myself accountable with some friends at work who are also creators and
need accountability with shipping their work.</p>
<p>So <a href="http://geoffreylessel.com/fighter-verses">go take a look</a> and let me know what you think! I’d love your
feedback on anything about it. I’m <a href="https://geoffreylessel.com/geolessel">@geolessel</a> on Twitter
and <strong>@geo</strong> in the Elixir slack group. And if you’re interested in
how I got Phoenix being hosted in a subdirectory on my server or how I
deploy my production apps, sign up for my email list below and I’ll
let you know when I post something about those things in the future.</p>Geoffrey LesselWhile most of my free-time development focus has been on Elixir apps that don’t use the web, I’ve been playing a lot with the Phoenix framework as well. I deployed one Phoenix app to production in the spring of this year and it has been running great since then. However, it is a site that is built for my family’s use and is not public. I wanted to do another little website that I could use on a regular basis (and others could use as well).Connecting to Multiple Databases with Ecto2016-07-26T00:00:00-07:002016-07-26T00:00:00-07:00/2016/connecting-to-multiple-databases-with-ecto<p>Now that we’ve explored some simple Ecto setups, let’s get a little
bit exotic. Did you know that you can set up your application in such
a way that you can connect to multiple databases with Ecto? Ecto actually makes
it super simple to do so. Let’s take a look.</p>
<h2 id="setting-everything-up">Setting everything up</h2>
<p>I’m not going to go into much detail regarding the initial setup of
your Elixir application. I’ve covered that in detail in
<a href="http://geoffreylessel.com/2016/from-zero-to-ecto-in-10-minutes">other parts</a> of this site. Instead, let’s just jump into an
already set up project.</p>
<p>Let’s imagine that you have a legacy MySQL database that has User
information that we need in order to have our application work
properly. At the same time, we also would like to slowly migrate our
site over to PostgreSQL and we store our new Favorite records
there. Favorite what? Who knows. It doesn’t really matter in this
post.</p>
<p>But let’s reiterate what we are going to be doing here. We’ll be
connecting our application to <strong>two different databases</strong>. Not only
that, those two databases <strong>will not even be the same type of
database</strong>. Pretty cool, huh?</p>
<p>Like in a normal Elixir/Ecto setup, let’s take a look at our
<code class="highlighter-rouge">config/config.exs</code> file:</p>
<h4 id="configconfigexs">config/config.exs</h4>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">use</span> <span class="no">Mix</span><span class="o">.</span><span class="no">Config</span>
<span class="n">config</span> <span class="ss">:my_app</span><span class="p">,</span> <span class="no">MyApp</span><span class="o">.</span><span class="no">LegacyRepo</span><span class="p">,</span>
<span class="ss">adapter:</span> <span class="no">Ecto</span><span class="o">.</span><span class="no">Adapters</span><span class="o">.</span><span class="no">MySQL</span><span class="p">,</span>
<span class="ss">database:</span> <span class="sd">"</span><span class="s2">legacy_db"</span><span class="p">,</span>
<span class="ss">username:</span> <span class="sd">"</span><span class="s2">username"</span><span class="p">,</span>
<span class="ss">password:</span> <span class="sd">"</span><span class="s2">somethingsecret"</span><span class="p">,</span>
<span class="ss">hostname:</span> <span class="sd">"</span><span class="s2">legacy.mysite.com"</span>
<span class="n">config</span> <span class="ss">:my_app</span><span class="p">,</span> <span class="no">MyApp</span><span class="o">.</span><span class="no">NewRepo</span><span class="p">,</span>
<span class="ss">adapter:</span> <span class="no">Ecto</span><span class="o">.</span><span class="no">Adapters</span><span class="o">.</span><span class="no">Postgres</span><span class="p">,</span>
<span class="ss">username:</span> <span class="sd">"</span><span class="s2">username"</span><span class="p">,</span>
<span class="ss">password:</span> <span class="sd">"</span><span class="s2">somethingsecret"</span><span class="p">,</span>
<span class="ss">database:</span> <span class="sd">"</span><span class="s2">new_db"</span><span class="p">,</span>
<span class="ss">hostname:</span> <span class="sd">"</span><span class="s2">newhotness.mysite.com"</span>
<span class="n">config</span> <span class="ss">:my_app</span><span class="p">,</span> <span class="ss">ecto_repos:</span> <span class="p">[</span><span class="no">MyApp</span><span class="o">.</span><span class="no">LegacyRepo</span><span class="p">,</span> <span class="no">MyApp</span><span class="o">.</span><span class="no">NewRepo</span><span class="p">]</span>
</code></pre></div></div>
<p>We also need to ensure that both Repos are supervised.</p>
<h4 id="libmy_appex">lib/my_app.ex</h4>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">defmodule</span> <span class="no">ExistingDb</span> <span class="k">do</span>
<span class="kn">use</span> <span class="no">Application</span>
<span class="k">def</span> <span class="n">start</span><span class="p">(</span><span class="n">_type</span><span class="p">,</span> <span class="n">_args</span><span class="p">)</span> <span class="k">do</span>
<span class="kn">import</span> <span class="no">Supervisor</span><span class="o">.</span><span class="no">Spec</span><span class="p">,</span> <span class="ss">warn:</span> <span class="no">false</span>
<span class="n">children</span> <span class="o">=</span> <span class="p">[</span>
<span class="n">supervisor</span><span class="p">(</span><span class="no">MyApp</span><span class="o">.</span><span class="no">LegacyRepo</span><span class="p">,</span> <span class="p">[]),</span> <span class="c1"># <-- our addition</span>
<span class="n">supervisor</span><span class="p">(</span><span class="no">MyApp</span><span class="o">.</span><span class="no">NewRepo</span><span class="p">,</span> <span class="p">[])</span> <span class="c1"># <-- our addition</span>
<span class="p">]</span>
<span class="n">opts</span> <span class="o">=</span> <span class="p">[</span><span class="ss">strategy:</span> <span class="ss">:one_for_one</span><span class="p">,</span> <span class="ss">name:</span> <span class="no">MyApp</span><span class="o">.</span><span class="no">Supervisor</span><span class="p">]</span>
<span class="no">Supervisor</span><span class="o">.</span><span class="n">start_link</span><span class="p">(</span><span class="n">children</span><span class="p">,</span> <span class="n">opts</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<p>Finally, we need to also create the Repo modules. For the sake of
being concise, I’ll put them both in the same file:</p>
<h4 id="libreposex">lib/repos.ex</h4>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">defmodule</span> <span class="no">MyApp</span><span class="o">.</span><span class="no">LegacyRepo</span> <span class="k">do</span>
<span class="kn">use</span> <span class="no">Ecto</span><span class="o">.</span><span class="no">Repo</span><span class="p">,</span> <span class="ss">otp_app:</span> <span class="ss">:my_app</span>
<span class="k">end</span>
<span class="k">defmodule</span> <span class="no">MyApp</span><span class="o">.</span><span class="no">NewRepo</span> <span class="k">do</span>
<span class="kn">use</span> <span class="no">Ecto</span><span class="o">.</span><span class="no">Repo</span><span class="p">,</span> <span class="ss">otp_app:</span> <span class="ss">:my_app</span>
<span class="k">end</span>
</code></pre></div></div>
<p>Wow, that was easy. For completeness, let’s define a couple of really
simple schemas.</p>
<h4 id="libuserex">lib/user.ex</h4>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">defmodule</span> <span class="no">User</span> <span class="k">do</span>
<span class="kn">use</span> <span class="no">Ecto</span><span class="o">.</span><span class="no">Schema</span>
<span class="n">schema</span> <span class="sd">"</span><span class="s2">users"</span> <span class="k">do</span>
<span class="n">field</span> <span class="ss">:username</span><span class="p">,</span> <span class="ss">:string</span>
<span class="n">has_many</span> <span class="ss">:favorites</span><span class="p">,</span> <span class="no">Favorite</span>
<span class="n">timestamps</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<h4 id="libfavoriteex">lib/favorite.ex</h4>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">defmodule</span> <span class="no">Favorite</span> <span class="k">do</span>
<span class="kn">use</span> <span class="no">Ecto</span><span class="o">.</span><span class="no">Schema</span>
<span class="n">schema</span> <span class="sd">"</span><span class="s2">favorites"</span> <span class="k">do</span>
<span class="n">field</span> <span class="ss">:value</span><span class="p">,</span> <span class="ss">:string</span>
<span class="n">belongs_to</span> <span class="ss">:user</span><span class="p">,</span> <span class="no">User</span>
<span class="n">timestamps</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<h2 id="using-our-repos">Using Our Repos</h2>
<p>Now that we have our Repos and defined a couple sample schemas, let’s
test out how to use them. In order to use them correctly, we’ll need
to query each Repo separately depending on which record we’d like to retrieve.</p>
<p>We’re not going to get fancy here so let’s just open up iex with <code class="highlighter-rouge">iex -S mix</code>.</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># a little setup</span>
<span class="n">iex</span><span class="o">></span> <span class="kn">import</span> <span class="no">Ecto</span><span class="o">.</span><span class="no">Query</span>
<span class="c1"># let's grab a User from the legacy db</span>
<span class="n">iex</span><span class="o">></span> <span class="n">user</span> <span class="o">=</span> <span class="no">MyApp</span><span class="o">.</span><span class="no">LegacyRepo</span><span class="o">.</span><span class="n">get_by</span><span class="p">(</span><span class="no">User</span><span class="p">,</span> <span class="p">%{</span><span class="ss">username:</span> <span class="sd">"</span><span class="s2">Geo"</span><span class="p">})</span>
<span class="c1"># now that we have our user from the legacy database, let's find their</span>
<span class="c1"># favorites in the new database</span>
<span class="n">iex</span><span class="o">></span> <span class="n">favorites</span> <span class="o">=</span> <span class="no">MyApp</span><span class="o">.</span><span class="no">NewRepo</span><span class="o">.</span><span class="n">all</span> <span class="n">from</span> <span class="n">fav</span> <span class="ow">in</span> <span class="no">Favorite</span><span class="p">,</span>
<span class="ss">where:</span> <span class="n">fav</span><span class="o">.</span><span class="n">user_id</span> <span class="o">==</span> <span class="o">^</span><span class="n">user</span><span class="o">.</span><span class="n">id</span>
</code></pre></div></div>
<h2 id="not-a-lot-of-work">Not A Lot of Work</h2>
<p>One of the things that I’ve really been enjoying about Ecto is that
although it is explicit in the way Elixir is explicit, it doesn’t take
a lot of work to get it to do what you want it to do (most of the
time). In this case, it was almost trivial to connect to
two different databases <strong>of two different types</strong> within the same application.</p>
<p>There seems to be more and more people every day moving over to Elixir
and Phoenix as their main development tools. I think this
demonstration of connecting to both a legacy database and a fresh
database could be helpful in a lot of transitions.</p>
<p>If you have any questions, please let me know. I’m
<a href="https://twitter.com/geolessel">@geolessel</a> on Twitter and <strong>@geo</strong> in the Elixir slack
group. Also, I’d be grateful if you signed up for my email list
below. I’m brainstorming some neat things that I’d like to release in
the near future and I’d love to let you be the first to know about
it. Thanks!</p>Geoffrey LesselNow that we’ve explored some simple Ecto setups, let’s get a little bit exotic. Did you know that you can set up your application in such a way that you can connect to multiple databases with Ecto? Ecto actually makes it super simple to do so. Let’s take a look.Using Ecto With an Existing MySQL Database2016-06-29T00:00:00-07:002016-06-29T00:00:00-07:00/2016/using-ecto-with-an-existing-mysql-database<p>There was some great feedback from people regarding my post on getting
Ecto in your non-Phoenix application (<a href="http://geoffreylessel.com/2016/from-zero-to-ecto-in-10-minutes">From Zero to Ecto in 10 Minutes</a>).
One of the questions I got was about using Ecto with existing databases. While
using Ecto with a new database is a great way to start a new project, there are
plenty of existing projects that could stand to have a little Elixir love
thrown their way.</p>
<p>So let’s explore creating a new Elixir application that interacts with an existing
database. Furthermore, while most tutorials show you how to use Ecto with Postgres,
there are plenty of existing projects that use MySQL. Let’s make this <strong>more difficult</strong> and
set up the application to access a MySQL database. Not difficult enough you say? Fine.
Let’s also deal with an unusual primary key column name.</p>
<p>But really, all that isn’t hard at all with Elixir and Ecto. It only took me 6 minutes
to get my basic application up and running.</p>
<h2 id="set-up-your-project">Set up your project</h2>
<p>For this sample project, I’m going to continue with data from one of my
favorite sports: Formula 1 racing. There is a fantastic database online that contains
the entire history of the sport and we are going to use that. If you’d like to
follow along, I’m going to be working with the <a href="http://ergast.com/mrd/">Ergast Motor Racing Developer API</a>
database dump.</p>
<h3 id="mysql">MySQL</h3>
<p>After getting the dump, create a database (I’m calling mine <code class="highlighter-rouge">f1</code>) and
import that into your local MySQL database. I’m assuming, of course,
that you have a local MySQL server running and can import the SQL dump. If not, there
are plenty of tutorials out there to <a href="http://www.mysqltutorial.org/">get you started</a>.</p>
<p>Let’s take a quick peek at what the <code class="highlighter-rouge">races</code> table looks like:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mysql> use f1
mysql> describe races;
+-----------+--------------+------+-----+------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+------------+----------------+
| raceId | int(11) | NO | PRI | NULL | auto_increment |
| year | int(11) | NO | | 0 | |
| round | int(11) | NO | | 0 | |
| circuitId | int(11) | NO | | 0 | |
| name | varchar(255) | NO | | | |
| date | date | NO | | 0000-00-00 | |
| time | time | YES | | NULL | |
| url | varchar(255) | YES | UNI | NULL | |
+-----------+--------------+------+-----+------------+----------------+
8 rows in set (0.00 sec)
</code></pre></div></div>
<p>We can see here an output of the fields and types we are going to have to connect
to in our application – and notice our primary key is named <code class="highlighter-rouge">raceId</code>. We’ll
have to deal with that later.</p>
<h3 id="elixir">Elixir</h3>
<p>Now that we have that out of the way, we can finally get to some Elixir. The
first thing we’ll need to do, of course, is create a new application with
<code class="highlighter-rouge">ecto</code> and a supervisor. I’m going to name mine <strong>F1History</strong>.
I wrote a detailed post about how to quickly get
going in <a href="http://geoffreylessel.com/2016/from-zero-to-ecto-in-10-minutes">From Zero To Ecto In 10 Minutes</a>. If you need some
pointers on getting started, go there.</p>
<p><strong><em>HOWEVER, we need to make a couple changes from that tutorial</em></strong> since we will be using
MySQL instead of the default—Postgres.</p>
<p>In our <code class="highlighter-rouge">mix.exs</code> file, instead of indicating <code class="highlighter-rouge">:postgrex</code> as a dependency, we’ll
need to use <code class="highlighter-rouge">:mariaex</code>. <a href="https://github.com/xerions/mariaex">mariaex</a> is the Ecto recommended MySQL driver.</p>
<h4 id="mixexs">mix.exs</h4>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># ...</span>
<span class="k">def</span> <span class="n">application</span> <span class="k">do</span>
<span class="p">[</span><span class="ss">applications:</span> <span class="p">[</span><span class="ss">:logger</span><span class="p">,</span> <span class="ss">:ecto</span><span class="p">,</span> <span class="ss">:mariaex</span><span class="p">],</span>
<span class="ss">mod:</span> <span class="p">{</span><span class="no">F1History</span><span class="p">,</span> <span class="p">[]}]</span>
<span class="k">end</span>
<span class="k">defp</span> <span class="n">deps</span> <span class="k">do</span>
<span class="p">[</span>
<span class="p">{</span><span class="ss">:ecto</span><span class="p">,</span> <span class="sd">"</span><span class="s2">~> 2.0"</span><span class="p">},</span>
<span class="p">{</span><span class="ss">:mariaex</span><span class="p">,</span> <span class="sd">"</span><span class="s2">~> 0.7"</span><span class="p">}</span>
<span class="p">]</span>
<span class="k">end</span>
<span class="c1"># ...</span>
</code></pre></div></div>
<p>In the <code class="highlighter-rouge">config/config.exs</code> file, we’ll also need to tell Ecto that we’re going
to be talking to a MySQL database:</p>
<h4 id="configconfigexs">config/config.exs</h4>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># ...</span>
<span class="n">config</span> <span class="ss">:f1_history</span><span class="p">,</span> <span class="no">F1History</span><span class="o">.</span><span class="no">Repo</span><span class="p">,</span>
<span class="ss">adapter:</span> <span class="no">Ecto</span><span class="o">.</span><span class="no">Adapters</span><span class="o">.</span><span class="no">MySQL</span><span class="p">,</span>
<span class="ss">database:</span> <span class="sd">"</span><span class="s2">f1"</span><span class="p">,</span>
<span class="ss">username:</span> <span class="sd">"</span><span class="s2">root"</span><span class="p">,</span>
<span class="ss">password:</span> <span class="sd">"</span><span class="s2">"</span><span class="p">,</span>
<span class="ss">hostname:</span> <span class="sd">"</span><span class="s2">localhost"</span>
<span class="n">config</span> <span class="ss">:f1_history</span><span class="p">,</span> <span class="ss">ecto_repos:</span> <span class="p">[</span><span class="no">F1History</span><span class="o">.</span><span class="no">Repo</span><span class="p">]</span>
<span class="c1"># ...</span>
</code></pre></div></div>
<h2 id="defining-your-schema">Defining your schema</h2>
<p>Now that we have told Elixir and Ecto how to start and connect to the databse,
we can now define our schema. This is the file that we will use to define what
data we’d like to retrieve from the database and in what format. If you remember
from above, here is how our existing table is defined:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+-----------+--------------+------+-----+------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+------------+----------------+
| raceId | int(11) | NO | PRI | NULL | auto_increment |
| year | int(11) | NO | | 0 | |
| round | int(11) | NO | | 0 | |
| circuitId | int(11) | NO | | 0 | |
| name | varchar(255) | NO | | | |
| date | date | NO | | 0000-00-00 | |
| time | time | YES | | NULL | |
| url | varchar(255) | YES | UNI | NULL | |
+-----------+--------------+------+-----+------------+----------------+
</code></pre></div></div>
<p>So with that in mind, you will need to match field names and types. Here’s how I defined my file:</p>
<h4 id="libraceex">lib/race.ex</h4>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">defmodule</span> <span class="no">F1History</span><span class="o">.</span><span class="no">Race</span> <span class="k">do</span>
<span class="kn">use</span> <span class="no">Ecto</span><span class="o">.</span><span class="no">Schema</span>
<span class="nv">@primary_key</span> <span class="p">{</span><span class="ss">:raceId</span><span class="p">,</span> <span class="ss">:id</span><span class="p">,</span> <span class="ss">autogenerate:</span> <span class="no">true</span><span class="p">}</span>
<span class="n">schema</span> <span class="sd">"</span><span class="s2">races"</span> <span class="k">do</span>
<span class="n">field</span> <span class="ss">:year</span><span class="p">,</span> <span class="ss">:integer</span>
<span class="n">field</span> <span class="ss">:round</span><span class="p">,</span> <span class="ss">:integer</span>
<span class="n">field</span> <span class="ss">:circuitId</span><span class="p">,</span> <span class="ss">:integer</span>
<span class="n">field</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:string</span>
<span class="n">field</span> <span class="ss">:date</span><span class="p">,</span> <span class="no">Ecto</span><span class="o">.</span><span class="no">Date</span>
<span class="n">field</span> <span class="ss">:time</span><span class="p">,</span> <span class="no">Ecto</span><span class="o">.</span><span class="no">Time</span>
<span class="n">field</span> <span class="ss">:url</span><span class="p">,</span> <span class="ss">:string</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<p>It looks suspiciously like every other Ecto schema you’ve set up in the past, right?
There may be a couple new things in here that you haven’t seen before or aren’t real
familiar with, so I’d like to point them out.</p>
<h3 id="funky-primary-key">Funky primary key</h3>
<p>This table (and all of them in the Ergast database) have what I’d consider to be a
funky primary key column name. Instead of a normal <code class="highlighter-rouge">id</code>, we have <code class="highlighter-rouge">raceId</code>. If you
remember from your past work in Ecto or a previous post of mine, Ecto will automatically
assume that the primary key name is <code class="highlighter-rouge">id</code>. We can override that default with the
<code class="highlighter-rouge">@primary_key</code> attribute. Here’s what the Ecto documents say about <code class="highlighter-rouge">@primary_key</code>:</p>
<blockquote>
<p>configures the schema primary key. It expects a tuple {field_name, type, options} with
the primary key field name, type (typically :id or :binary_id, but can be any type)
and options. Defaults to {:id, :id, autogenerate: true}. When set to false, does not
define a primary key in the schema;</p>
</blockquote>
<p>So this is pretty straightforward (now that you know about it). If you’d like to read
more about the <code class="highlighter-rouge">@primary_key</code> attribute, you can in the <a href="https://hexdocs.pm/ecto/2.0.2/Ecto.Schema.html">Ecto.Schema</a>
docs.</p>
<h3 id="funky-field-types">Funky field types</h3>
<p>You’ll also notice that there are a couple of unusual field types: <code class="highlighter-rouge">Ecto.Date</code> and <code class="highlighter-rouge">Ecto.Time</code>.
Dates and times have been somewhat tricky things to deal with in Elixir but now that <a href="http://elixir-lang.org/blog/2016/06/21/elixir-v1-3-0-released/">Elixir
v1.3 has been released</a>, we have new Calendar data types. That’s beyond the scope
of this post, but know that Ecto defined it’s own special types for dealing with <code class="highlighter-rouge">Date</code>s and <code class="highlighter-rouge">Time</code>s
(as well as <code class="highlighter-rouge">DateTime</code>s and <code class="highlighter-rouge">UUID</code>s). Again, you can read about these in detail in the <a href="https://hexdocs.pm/ecto/2.0.2/Ecto.Schema.html">Ecto.Schema</a>
docs online.</p>
<h2 id="testing-it-out-in-iex">Testing it out in iex</h2>
<p>Now comes the point of truth. Did all this work? Let’s see if we can query our existing
database table in an <code class="highlighter-rouge">iex</code> session:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> iex -S mix
iex(1)> import Ecto.Query
nil
iex(2)> F1History.Repo.one from race in F1History.Race, limit: 1
%F1History.Race{__meta__: #Ecto.Schema.Metadata<:loaded, "races">,
circuitId: 1, date: #Ecto.Date<2009-03-29>, name: "Australian Grand Prix",
raceId: 1, round: 1, time: #Ecto.Time<06:00:00>,
url: "http://en.wikipedia.org/wiki/2009_Australian_Grand_Prix", year: 2009}
</code></pre></div></div>
<h2 id="thats-it">That’s it!</h2>
<p>And that’s all there is to it! You can now <a href="http://geoffreylessel.com/2016/f1-standings-with-ecto/">use Ecto’s powerful composable queries</a>
to get information from your database, and, of course, write to it and update it as well.
The powerful nature of Ecto comes through as connecting and wiring up Ecto to an existing
database is just as easy as using Ecto to create one from scratch. Don’t let old databases
stop you from connecting with Elixir any longer!</p>
<p>If you have any questions regarding this post, Elixir, Ecto, or anything else, please
don’t hesitate to contact me. I love hearing from readers and get some great ideas for new
posts (like this one!) based on your questions and comments. I’m <a href="https://twitter.com/geolessel">@geolessel</a>
on Twitter and @geo in the Elixir Slack group.</p>
<p>And finally, please sign up for my mailing list below. I have a few cool things rattling
around in my mind that, if they ever come into existance, I’d love to let you know about.</p>Geoffrey LesselThere was some great feedback from people regarding my post on getting Ecto in your non-Phoenix application (From Zero to Ecto in 10 Minutes). One of the questions I got was about using Ecto with existing databases. While using Ecto with a new database is a great way to start a new project, there are plenty of existing projects that could stand to have a little Elixir love thrown their way.