<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tjaco Oostdijk</title>
    <description>The latest articles on DEV Community by Tjaco Oostdijk (@drumusician).</description>
    <link>https://dev.to/drumusician</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F71321%2F7a64678a-246b-4941-b8d6-ea41920459de.jpeg</url>
      <title>DEV Community: Tjaco Oostdijk</title>
      <link>https://dev.to/drumusician</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/drumusician"/>
    <language>en</language>
    <item>
      <title>Extending Flutter Driver with custom commands</title>
      <dc:creator>Tjaco Oostdijk</dc:creator>
      <pubDate>Mon, 17 Feb 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/kabisasoftware/extending-flutter-driver-with-custom-commands-38pl</link>
      <guid>https://dev.to/kabisasoftware/extending-flutter-driver-with-custom-commands-38pl</guid>
      <description>&lt;p&gt;&lt;a href="https://flutter.dev/docs/cookbook/testing/integration/introduction"&gt;Flutter Driver&lt;/a&gt; is a library to write end-to-end integration tests for Flutter apps. It’s similar to Selenium WebDriver (for web apps), Espresso (for native Android apps) and Earl Grey (for native iOS apps). It works by instrumenting the Flutter app, deploying it on a real device or emulator and then ‘driving’ the application using a suite of Dart &lt;a href="https://pub.dev/packages/test"&gt;tests&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A typical, basic Flutter Driver test looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

import 'package:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart';

void main() {
  group('Counter App', () {
    final counterTextFinder = find.byValueKey('counter');
    final buttonFinder = find.byValueKey('increment');

    FlutterDriver driver;

    // Connect to the Flutter driver before running any tests.
    setUpAll(() async {
      driver = await FlutterDriver.connect();
    });

    // Close the connection to the driver after the tests have completed.
    tearDownAll(() async {
      driver?.close();
    });

    test('starts at 0', () async {
      expect(await driver.getText(counterTextFinder), "0");
    });

    test('increments the counter', () async {
      await driver.tap(buttonFinder);

      expect(await driver.getText(counterTextFinder), "1");
    });
  });
}

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

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;setUpAll&lt;/code&gt; hook a connection between the test and the running application is setup via the Flutter Driver API. This works because the application is instrumented with a Flutter Driver extension; basically an API injected into your app that can receive requests from our tests to “drive” the application.&lt;/p&gt;

&lt;p&gt;The instrumentation of the Flutter app works by wrapping your app’s &lt;code&gt;main&lt;/code&gt; function like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7

import 'package:flutter_driver/driver_extension.dart';
import 'package:my_app/main.dart' as app;

void main() {
  enableFlutterDriverExtension(); // &amp;lt;-- ENABLE INSTRUMENTATION
  app.main();
}

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

&lt;/div&gt;



&lt;p&gt;Flutter Driver supports a handful of API’s to communicate with the running app. For example &lt;code&gt;getText&lt;/code&gt;, &lt;code&gt;tap&lt;/code&gt;, &lt;code&gt;waitFor&lt;/code&gt; etc. For me, coming from &lt;a href="http://nightwatchjs.org/"&gt;Nightwatch.js&lt;/a&gt;, the number of things that can be done to drive the application is quite limited.&lt;/p&gt;

&lt;p&gt;Fortunately it’s possible to extend Flutter Driver to support custom commands. These commands allow you to communicate between your tests and the application and are also the foundation for all of Flutter Driver’s own API’s like &lt;code&gt;getText&lt;/code&gt;, &lt;code&gt;tap&lt;/code&gt; etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Extending Flutter Driver
&lt;/h3&gt;

&lt;p&gt;To extend Flutter Driver with a custom command we need to provide a &lt;a href="https://api.flutter.dev/flutter/flutter_driver_extension/DataHandler.html"&gt;&lt;code&gt;DataHandler&lt;/code&gt;&lt;/a&gt;. As the docs say:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2

Optionally you can pass a [DataHandler] callback. It will be called if the
test calls [FlutterDriver.requestData].

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

&lt;/div&gt;



&lt;p&gt;Flutter Driver will pass whatever is sent from test with &lt;code&gt;driver.requestData(...)&lt;/code&gt; to the DataHandler. DataHandler only supports sending and receiving Strings, so you might want to encode your messages using JSON.&lt;/p&gt;

&lt;p&gt;To demonstrate this, let’s implement a handler to navigate back to the root route of our app. This way we can ensure that every test starts from the root page of our application.&lt;/p&gt;

&lt;p&gt;The first step is to provide a &lt;code&gt;DataHandler&lt;/code&gt; to Flutter Driver:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3

enableFlutterDriverExtension(handler: (payload) async {
  print(payload);
});

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

&lt;/div&gt;



&lt;p&gt;The handler will receive a String payload and can optionally return a String response.&lt;/p&gt;

&lt;p&gt;For the sake of simplicity let’s use a String as payload for now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5

enableFlutterDriverExtension(handler: (payload) async {
  if(payload == "navigate_to_root") {
    // do something smart here
  }
});

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

&lt;/div&gt;



&lt;p&gt;From here, we need to implement something that will allow us to navigate to the root of our app. I’m not sure if the following is necessarily the best way to do this (if you know a better way please let me know!), but it works and is relatively straightforward.&lt;/p&gt;

&lt;p&gt;We’ll use a &lt;a href="https://api.flutter.dev/flutter/widgets/NavigatorObserver-class.html"&gt;NavigationObserver&lt;/a&gt; to get a hold of the &lt;a href="https://api.flutter.dev/flutter/widgets/NavigatorState-class.html"&gt;NavigatorState&lt;/a&gt;, which we can use to push and pop routes. We need to be able to pass in a NavigationObserver from our test entry point, so we can access it when we receive a command to navigate to root.&lt;/p&gt;

&lt;p&gt;Change the &lt;code&gt;main&lt;/code&gt; function of your app as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

void main() {
  _main(null);
}

void mainTest(NavigatorObserver navigatorObserver) {
  _main(navigatorObserver);
}

void _main(NavigatorObserver navigatorObserver) {
  runApp(MyApp(
    navigatorObserver: navigatorObserver,
  ));
}

class MyApp extends StatelessWidget {
  final NavigatorObserver navigatorObserver;

  const MyApp({Key key, this.navigatorObserver}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // all other MaterialApp initialisation here
      navigatorObservers: navigatorObserver == null ? [] : [navigatorObserver],
    );
  }
}

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

&lt;/div&gt;



&lt;p&gt;This allows us to hook up a NavigationObserver from our test wrapper like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

import 'package:flutter/material.dart';
import 'package:flutter_driver/driver_extension.dart';
import 'package:my_app/main.dart' as app;

void main() {
  final navigationObserver = NavigatorObserver();

  enableFlutterDriverExtension(handler: (payload) async {
    if (payload == "navigate_to_root") {
      navigationObserver.navigator.popUntil(ModalRoute.withName('/'));
    }

    return null;
  });

  app.mainTest(navigationObserver);
}

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

&lt;/div&gt;



&lt;p&gt;Now from our tests we can send our custom command, for example in a &lt;code&gt;setUp&lt;/code&gt; hook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

void main() {
  FlutterDriver driver;

  setUpAll(() async {
    driver = await FlutterDriver.connect();
  });

  tearDownAll(() async {
    driver?.close();
  });

  setUp(() async {
    await driver.requestData("navigate_to_root");
  });   

  /* Actual tests here */
}

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

&lt;/div&gt;



&lt;p&gt;This will make sure that before every test, the app navigates back to the root route no matter where we navigated to in our tests.&lt;/p&gt;

&lt;p&gt;Of course, this is just an example of how you can implement communication between your Driver tests and your app. If you’re going to send more complex commands that require arguments you might want to send JSON data, but I’ll leave that as an exercise to you, dear reader ;-)&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Using Phoenix Presence in LiveView |&gt; A Simple Example</title>
      <dc:creator>Tjaco Oostdijk</dc:creator>
      <pubDate>Tue, 11 Feb 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/kabisasoftware/using-phoenix-presence-in-liveview-a-simple-example-c75</link>
      <guid>https://dev.to/kabisasoftware/using-phoenix-presence-in-liveview-a-simple-example-c75</guid>
      <description>&lt;p&gt;Normally I'm not a big fan of things that seem like magic, but in the case of Phoenix Presence I actually love it. I love the way you can very simply add Phoenix Presence to your Phoenix app and easily track user's presence with it. Amazing! I will definitely dig into the internals at some point in the future, but for now I wanted to show a very simple example of using Phoenix Presence inside LiveView!&lt;/p&gt;

&lt;p&gt;Let's see how that would work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who's reading?
&lt;/h2&gt;

&lt;p&gt;We are going to build a very simple but powerful feature for the blog you're currently reading. We'll add an indicator at the top of the page that shows how many people are currently reading this page. So where do we want this indicator? Preferably this would reside on the same page as a blog post so that it would indicate for every individual blog post how many users are currently reading that article. To do this, we'll create a very small LiveView component inserted in the markdown of our post. Just like we did in my &lt;a href="https://realworldphoenix.com/blog/2020-01-28/rendering_markdown"&gt;last post&lt;/a&gt; when I rendered scrambled text. At a later stage I will probably want to make it more generic, but for now let's focus on getting this to work for this blog post.&lt;/p&gt;

&lt;p&gt;If you head over to my &lt;a href="https://realworldphoenix.com/blog/2020-02-11/simple_phoenix_presence"&gt;interactive blog&lt;/a&gt;, you can see this counter in action!&lt;/p&gt;

&lt;p&gt;This is how we embed the LiveView Component into out markdown post:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="no"&gt;Phoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;LiveView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Helpers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;live_render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;@conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;RealWorldPhoenixWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Live&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;ReaderCount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;session:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;slug:&lt;/span&gt; &lt;span class="s2"&gt;"simple_phoenix_presence"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  LiveView or LiveView Component?
&lt;/h2&gt;

&lt;p&gt;LiveView is maturing quickly and currently also has a concept of Components. Components basically are small building blocks that either are stateful or stateless. I am definitely planning on doing a writeup on using LiveView components, but for our usecase we simply need the basic LiveView.&lt;/p&gt;

&lt;p&gt;Let's create a basic LiveView boilerplate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;RealWorldPhoenixWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Live&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;ReaderCount&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Phoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;LiveView&lt;/span&gt;

  &lt;span class="nv"&gt;@moduledoc&lt;/span&gt; &lt;span class="sd"&gt;"""
    A small LiveView that shows the number of readers of a post using Phoenix Presence
  """&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;assigns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="sx"&gt;~L""&lt;/span&gt;&lt;span class="s2"&gt;"
      Readers: &amp;lt;\%= @reader_count %&amp;gt;
    """&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:reader_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Ok, so now we need to hook up Presence. Let's see how that would work. First we'll head over to the &lt;a href="https://hexdocs.pm/phoenix/Phoenix.Presence.html"&gt;docs&lt;/a&gt; and see how it is setup. The first thing I notice is this: &lt;em&gt;Provides Presence tracking to processes and channels.&lt;/em&gt;. We are not really using Channels, but our LiveView is a process so I guess that should work, right?&lt;/p&gt;

&lt;h2&gt;
  
  
  Presence Setup
&lt;/h2&gt;

&lt;p&gt;Let's first setup Presence using the guide provides by the Phoenix Team:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Setup a Presence Module in our app:
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;RealWorldPhoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Presence&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Phoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Presence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;otp_app:&lt;/span&gt; &lt;span class="ss"&gt;:real_world_phoenix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="ss"&gt;pubsub_server:&lt;/span&gt; &lt;span class="no"&gt;RealWorldPhoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PubSub&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  2.  Add this module to our supervision tree:
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/real_world_phoenix/application.ex&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;RealWorldPhoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Application&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@moduledoc&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Application&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;children&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="no"&gt;RealWorldPhoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="no"&gt;RealWorldPhoenixWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="no"&gt;RealWorldPhoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Presence&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;= Add This!&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;strategy:&lt;/span&gt; &lt;span class="ss"&gt;:one_for_one&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="no"&gt;RealWorldPhoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Supervisor&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="no"&gt;Supervisor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;config_change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;changed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_new&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;removed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;RealWorldPhoenixWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Endpoint&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config_change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;changed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;removed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="ss"&gt;:ok&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  I don't have any Channels... ?
&lt;/h2&gt;

&lt;p&gt;So the next and last step in the guide shows an example of adding Presence tracking to a channel implementation. But we are not using channels! So how do we go about and add this to our LiveView?&lt;/p&gt;

&lt;p&gt;Presence uses a PubSub (Publish-Subscribe) mechanism to perform it's magic. So what we should do in our LiveView is make sure we subscribe to the topic of the current page. Once we do that we can add a callback that will track any presence_diff where we'll update the counter if someone leaves or joins the current topic.&lt;/p&gt;

&lt;p&gt;In our &lt;code&gt;mount/2&lt;/code&gt; function we'll subscribe to the page topic. Basically using a topic such as &lt;code&gt;blog:simple_phoenix_presence&lt;/code&gt;. So in essence a general category with a post slug after the colon. Here are the updates to our LiveView module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;RealWorldPhoenixWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Live&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;ReaderCount&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(%{&lt;/span&gt;&lt;span class="ss"&gt;slug:&lt;/span&gt; &lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# before subscribing, let's get the current_reader_count&lt;/span&gt;
    &lt;span class="n"&gt;topic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"blog:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;initial_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Presence&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;map_size&lt;/span&gt;

    &lt;span class="c1"&gt;# Subscribe to the topic&lt;/span&gt;
    &lt;span class="no"&gt;RealWorldPhoenixWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Endpoint&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Track changes to the topic&lt;/span&gt;
    &lt;span class="no"&gt;Presence&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;track&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;%{}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:reader_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;initial_count&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Listening to topic events to track changes
&lt;/h2&gt;

&lt;p&gt;Now the only thing left for us to do is implement a callback in our live_view to catch any diffs happening on the topic we are tracking. The following function will do the trick:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;handle_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;event:&lt;/span&gt; &lt;span class="s2"&gt;"presence_diff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;payload:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;joins:&lt;/span&gt; &lt;span class="n"&gt;joins&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;leaves:&lt;/span&gt; &lt;span class="n"&gt;leaves&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
        &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;assigns:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;reader_count:&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;reader_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;map_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;joins&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;map_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;leaves&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:noreply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:reader_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reader_count&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We pattern match on the "presence_diff" event and also use pattern matching to get the current values for joins, leaves and count respectively. When someone subscribes to the topic, there will be a change in the &lt;code&gt;joins&lt;/code&gt; and when they leave a similar change in &lt;code&gt;leaves&lt;/code&gt;. The &lt;code&gt;reader_count&lt;/code&gt; is something we update ourselves in the socket whenever someone joins or leaves.&lt;/p&gt;

&lt;p&gt;That is really all we need to do to get realtime updates of the number of readers of our page. That is quite easy and really cool to be able to see that data. Currently we don't ask user for information once they enter our blog, but once we add login or even just their name, we would be able to show that data as part of the presence_diff as well. But, let's leave that for another time.&lt;/p&gt;

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

&lt;p&gt;I hope that I showed you how easy it is to add Phoenix Presence to a LiveView component. I was pleasantly surprised as to how little effort this actually takes.&lt;/p&gt;

&lt;p&gt;Hope you learned something new!&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>phoenix</category>
    </item>
    <item>
      <title>Using Phoenix Presence in LiveView |&gt; A Simple Example</title>
      <dc:creator>Tjaco Oostdijk</dc:creator>
      <pubDate>Tue, 11 Feb 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/kabisasoftware/using-phoenix-presence-in-liveview-a-simple-example-2hc4</link>
      <guid>https://dev.to/kabisasoftware/using-phoenix-presence-in-liveview-a-simple-example-2hc4</guid>
      <description>&lt;p&gt;Normally I’m not a big fan of things that seem like magic, but in the case of Phoenix Presence I actually love it. I love the way you can very simply add Phoenix Presence to your Phoenix app and easily track user’s presence with it. Amazing! I will definitely dig into the internals at some point in the future, but for now I wanted to show a very simple example of using Phoenix Presence inside LiveView!&lt;/p&gt;

&lt;p&gt;Let’s see how that would work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who’s reading?
&lt;/h2&gt;

&lt;p&gt;We are going to build a very simple but powerful feature for the blog you’re currently reading. We’ll add an indicator at the top of the page that shows how many people are currently reading this page. So where do we want this indicator? Preferably this would reside on the same page as a blog post so that it would indicate for every individual blog post how many users are currently reading that article. To do this, we’ll create a very small LiveView component inserted in the markdown of our post. Just like we did in my &lt;a href="https://realworldphoenix.com/blog/2020-01-28/rendering_markdown"&gt;last post&lt;/a&gt; when I rendered scrambled text. At a later stage I will probably want to make it more generic, but for now let’s focus on getting this to work for this blog post.&lt;/p&gt;

&lt;p&gt;If you head over to my &lt;a href="https://realworldphoenix.com/blog/2020-02-11/simple_phoenix_presence"&gt;interactive blog&lt;/a&gt;, you can see this counter in action!&lt;/p&gt;

&lt;p&gt;This is how we embed the LiveView Component into out markdown post:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1

&amp;lt;%= Phoenix.LiveView.Helpers.live_render(@conn, RealWorldPhoenixWeb.Live.ReaderCount, session: %{slug: "simple_phoenix_presence"}) %&amp;gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  LiveView or LiveView Component?
&lt;/h2&gt;

&lt;p&gt;LiveView is maturing quickly and currently also has a concept of Components. Components basically are small building blocks that either are stateful or stateless. I am definitely planning on doing a writeup on using LiveView components, but for our usecase we simply need the basic LiveView.&lt;/p&gt;

&lt;p&gt;Let’s create a basic LiveView boilerplate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

defmodule RealWorldPhoenixWeb.Live.ReaderCount do
  use Phoenix.LiveView

  @moduledoc """
    A small LiveView that shows the number of readers of a post using Phoenix Presence
  """

  def render(assigns) do
    ~L"""
      Readers: &amp;lt;\%= @reader_count %&amp;gt;
    """
  end

  def mount(_session, socket) do
    {:ok, assign(socket, :reader_count, 0)}
  end
end

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

&lt;/div&gt;



&lt;p&gt;Ok, so now we need to hook up Presence. Let’s see how that would work. First we’ll head over to the &lt;a href="https://hexdocs.pm/phoenix/Phoenix.Presence.html"&gt;docs&lt;/a&gt; and see how it is setup. The first thing I notice is this: &lt;em&gt;Provides Presence tracking to processes and channels.&lt;/em&gt;. We are not really using Channels, but our LiveView is a process so I guess that should work, right?&lt;/p&gt;

&lt;h2&gt;
  
  
  Presence Setup
&lt;/h2&gt;

&lt;p&gt;Let’s first setup Presence using the guide provides by the Phoenix Team:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Setup a Presence Module in our app:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4

defmodule RealWorldPhoenix.Presence do
  use Phoenix.Presence, otp_app: :real_world_phoenix,
                        pubsub_server: RealWorldPhoenix.PubSub
end

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Add this module to our supervision tree:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# lib/real_world_phoenix/application.ex

defmodule RealWorldPhoenix.Application do
  @moduledoc false
  use Application

  def start(_type, _args) do
    children = [
      RealWorldPhoenix.Repo,
      RealWorldPhoenixWeb.Endpoint,
      RealWorldPhoenix.Presence # &amp;lt;= Add This!
    ]

    opts = [strategy: :one_for_one, name: RealWorldPhoenix.Supervisor]
    Supervisor.start_link(children, opts)
  end

  def config_change(changed, _new, removed) do
    RealWorldPhoenixWeb.Endpoint.config_change(changed, removed)
    :ok
  end
end

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  I don’t have any Channels… ?
&lt;/h2&gt;

&lt;p&gt;So the next and last step in the guide shows an example of adding Presence tracking to a channel implementation. But we are not using channels! So how do we go about and add this to our LiveView?&lt;/p&gt;

&lt;p&gt;Presence uses a PubSub (Publish-Subscribe) mechanism to perform it’s magic. So what we should do in our LiveView is make sure we subscribe to the topic of the current page. Once we do that we can add a callback that will track any presence_diff where we’ll update the counter if someone leaves or joins the current topic.&lt;/p&gt;

&lt;p&gt;In our &lt;code&gt;mount/2&lt;/code&gt; function we’ll subscribe to the page topic. Basically using a topic such as &lt;code&gt;blog:simple_phoenix_presence&lt;/code&gt;. So in essence a general category with a post slug after the colon. Here are the updates to our LiveView module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

defmodule RealWorldPhoenixWeb.Live.ReaderCount do
  ...

  def mount(%{slug: slug}, socket) do
    # before subscribing, let's get the current_reader_count
    topic = "blog:#{slug}"
    initial_count = Presence.list(topic) |&amp;gt; map_size

    # Subscribe to the topic
    RealWorldPhoenixWeb.Endpoint.subscribe(topic)

    # Track changes to the topic
    Presence.track(
      self(),
      topic,
      socket.id,
      %{}
    )

    {:ok, assign(socket, :reader_count, initial_count)}
  end
  # ...
end

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Listening to topic events to track changes
&lt;/h2&gt;

&lt;p&gt;Now the only thing left for us to do is implement a callback in our live_view to catch any diffs happening on the topic we are tracking. The following function will do the trick:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10

...
  def handle_info(
        %{event: "presence_diff", payload: %{joins: joins, leaves: leaves}},
        %{assigns: %{reader_count: count}} = socket
      ) do
    reader_count = count + map_size(joins) - map_size(leaves)

    {:noreply, assign(socket, :reader_count, reader_count)}
  end
...

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

&lt;/div&gt;



&lt;p&gt;We pattern match on the “presence_diff” event and also use pattern matching to get the current values for joins, leaves and count respectively. When someone subscribes to the topic, there will be a change in the &lt;code&gt;joins&lt;/code&gt; and when they leave a similar change in &lt;code&gt;leaves&lt;/code&gt;. The &lt;code&gt;reader_count&lt;/code&gt; is something we update ourselves in the socket whenever someone joins or leaves.&lt;/p&gt;

&lt;p&gt;That is really all we need to do to get realtime updates of the number of readers of our page. That is quite easy and really cool to be able to see that data. Currently we don’t ask user for information once they enter our blog, but once we add login or even just their name, we would be able to show that data as part of the presence_diff as well. But, let’s leave that for another time.&lt;/p&gt;

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

&lt;p&gt;I hope that I showed you how easy it is to add Phoenix Presence to a LiveView component. I was pleasantly surprised as to how little effort this actually takes.&lt;/p&gt;

&lt;p&gt;Hope you learned something new!&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Rendering Markdown in Elixir</title>
      <dc:creator>Tjaco Oostdijk</dc:creator>
      <pubDate>Wed, 29 Jan 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/kabisasoftware/rendering-markdown-in-elixir-3m28</link>
      <guid>https://dev.to/kabisasoftware/rendering-markdown-in-elixir-3m28</guid>
      <description>&lt;p&gt;Recently, I acquired the domain &lt;code&gt;realworldphoenix.com&lt;/code&gt; in order to continue my blogging adventure on a separate blog dedicated to writing exclusive content on Phoenix and Elixir (and some related subjects occassionally). I could of course take an off the shelf blogging platform or just use some kind of static site generator. But as I am writing about using Phoenix in the Real World, I figured why not just create a Phoenix application to host my blog. Then I can also do some nice things like make it a bit interactive or even let people sign-up and comment without using a third-party tool for that. We'll see.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing content for a blog.
&lt;/h2&gt;

&lt;p&gt;As most of you would probably agree, writing long-form content using html is a bit of a pain. So most people use markdown to write blogposts and I'm no exception. So let's find out how we can write markdown, put it under version control (just because that is nice to have) and then make sure it gets rendered as html in our Phoenix app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Earmark
&lt;/h2&gt;

&lt;p&gt;The go-to way to convert markdown to html in Elixir land is the great library written by Dave Thomas, &lt;a href="https://github.com/pragdave/earmark"&gt;Earmark&lt;/a&gt;. It is used by &lt;a href="https://github.com/elixir-lang/ex_doc"&gt;ex_doc&lt;/a&gt; and is a nice and well maintained library. We could of course convert our markdown in our phoenix controller directly using Earmark and output that in our view, but we can do even better. We can make sure that our Phoenix app can actually render markdown files stored in our template directly to html, just like it render's &lt;code&gt;eex&lt;/code&gt; to html.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phoenix Markdown Library
&lt;/h2&gt;

&lt;p&gt;In fact there is even a library that handles this for us: &lt;a href="https://github.com/boydm/phoenix_markdown"&gt;Phoenix Markdown&lt;/a&gt;. Now I can just use this and be done with it (and as you are reading this... I am actually already using it ;) ), but my curious mind wants to know more! I'd like to know how this mechanism works so I have a better understanding of how rendering works in Phoenix. So, without further ado, let's crack this baby open and take a look inside. Ooohhh what is that....?&lt;/p&gt;

&lt;h2&gt;
  
  
  It's a Template Engine!
&lt;/h2&gt;

&lt;p&gt;Ofcourse, Phoenix has this all figured out. The way that &lt;code&gt;eex&lt;/code&gt; and &lt;code&gt;exs&lt;/code&gt; are implemented is as Phoenix Template Engines. It is fairly straightforward to create your own Template Engine by implementing the &lt;code&gt;Phoenix.Template.Engine&lt;/code&gt; behaviour. This is exactly what &lt;a href="https://github.com/boydm"&gt;Boyd Multerer&lt;/a&gt; has created in his library &lt;a href="https://github.com/boydm/phoenix_markdown"&gt;phoenix_markdown&lt;/a&gt;. Does that name sound familiar? Oh, that's because boyd was also the creator of the awesome &lt;a href="https://github.com/boydm/scenic"&gt;Scenic Library&lt;/a&gt;. Great stuff!&lt;/p&gt;

&lt;h2&gt;
  
  
  How do we create a Template Engine?
&lt;/h2&gt;

&lt;p&gt;So how does one go about and create a template engine? Well, the best way to find out is to see if we can create one for ourselves, right? So let's create a template engine that scrambles all the text that gets rendered by the engine. We'll use the &lt;code&gt;Cambridge University Scrambled Text&lt;/code&gt; concept. So scramble all letters in words but keep the first and last letters intact. Fun fact, this so-called research was actually never done at Cambridge University! Somehow this internet meme got morphed into being a Cambridge research subject, but was actually never the case. Here's a &lt;a href="https://www.mrc-cbu.cam.ac.uk/people/matt.davis/cmabridge/"&gt;nice article&lt;/a&gt; from a Cambridge Professor about this.&lt;br&gt;
This however doesn't hold us back from using the concept to build our awesome scramble engine! So let's get cakrcnig!&lt;/p&gt;
&lt;h2&gt;
  
  
  Our scramble Engine
&lt;/h2&gt;

&lt;p&gt;If you want to follow along you can create a fresh phoenix project and put the files in there to test this out yourself.&lt;br&gt;
We'll define our engine in &lt;code&gt;lib/engine/scrambled.ex&lt;/code&gt;. The &lt;code&gt;Phoenix.Template.Engine&lt;/code&gt; behaviour requires us to implement &lt;code&gt;compile/2&lt;/code&gt;. In there we can add our magic formula. :)&lt;/p&gt;

&lt;p&gt;This is what I came up with for our use case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;RealWorldPhoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Engines&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Scrambled&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@moduledoc&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;

  &lt;span class="nv"&gt;@behaviour&lt;/span&gt; &lt;span class="no"&gt;Phoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Template&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Engine&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read!&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;scramble_words&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Earmark&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_html!&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;EEx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compile_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;engine:&lt;/span&gt; &lt;span class="no"&gt;Phoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;HTML&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;file:&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;line:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;scramble_words&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;trim:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;shuffle_words_in_sentence&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;shuffle_words_in_sentence&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sentence&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;sentence&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;shuffle&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;shuffle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;word&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;scrambled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="n"&gt;word&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;trim:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shuffle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;scrambled&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

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



&lt;p&gt;And to use this in our Phoenix app, we need to configure the file extension we want to invoke scrambling for. You can see this is the same as I did in my app to configure &lt;code&gt;phoenix_markdown&lt;/code&gt;. At compile time Phoenix will match any templates with the &lt;code&gt;.scrambled&lt;/code&gt; extension and will run that source file through our Engine by calling our &lt;code&gt;compile/2&lt;/code&gt; function defined in our Engine module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/config.exs&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:phoenix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:template_engines&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;md:&lt;/span&gt; &lt;span class="no"&gt;PhoenixMarkdown&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;scrambled:&lt;/span&gt; &lt;span class="no"&gt;RealWorldPhoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Engines&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Scrambled&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So I am expecting this to scramble content written in markdown. I'm just considering paragraphs of text, just to not complicate things unnecessarily for this use case. That is why I am basically splitting sentences by splitting on the newline character and then processing each sentence and the containing words before joining them together again with newlines. Seems pretty straightforward. Let's see if this works!&lt;/p&gt;

&lt;h2&gt;
  
  
  Smartypants... 🤔
&lt;/h2&gt;

&lt;p&gt;Phoenix Markdown has an option to render server_tags, which basically means that you can invoke Elixir inside your markdown page using the standard tags you use in &lt;code&gt;eex&lt;/code&gt; templates to invoke some Elixir code and evaluate it. I wanted to use this to render the following bit of text to illustrate my &lt;code&gt;scrambled&lt;/code&gt; engine, but the first pass led to this error while compiling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;==&lt;/span&gt; Compilation error &lt;span class="k"&gt;in &lt;/span&gt;file lib/real_world_phoenix_web/views/post_view.ex &lt;span class="o"&gt;==&lt;/span&gt;
&lt;span class="k"&gt;**&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;SyntaxError&lt;span class="o"&gt;)&lt;/span&gt; lib/real_world_phoenix_web/templates/post/blogs/2020-01-28/rendering_markdown.html.md:60: unexpected token: &lt;span class="s2"&gt;"“"&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;column 39, code point U+201C&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;(&lt;/span&gt;eex&lt;span class="o"&gt;)&lt;/span&gt; lib/eex/compiler.ex:45: EEx.Compiler.generate_buffer/4
    &lt;span class="o"&gt;(&lt;/span&gt;phoenix&lt;span class="o"&gt;)&lt;/span&gt; lib/phoenix/template.ex:361: Phoenix.Template.compile/3
    &lt;span class="o"&gt;(&lt;/span&gt;phoenix&lt;span class="o"&gt;)&lt;/span&gt; lib/phoenix/template.ex:167: anonymous fn/4 &lt;span class="k"&gt;in &lt;/span&gt;Phoenix.Template.&lt;span class="s2"&gt;"MACRO-__before_compile__"&lt;/span&gt;/2
    &lt;span class="o"&gt;(&lt;/span&gt;elixir&lt;span class="o"&gt;)&lt;/span&gt; lib/enum.ex:1948: Enum.&lt;span class="s2"&gt;"-reduce/3-lists^foldl/2-0-"&lt;/span&gt;/3
    &lt;span class="o"&gt;(&lt;/span&gt;phoenix&lt;span class="o"&gt;)&lt;/span&gt; expanding macro: Phoenix.Template.__before_compile__/1
    lib/real_world_phoenix_web/views/post_view.ex:1: RealWorldPhoenixWeb.PostView &lt;span class="o"&gt;(&lt;/span&gt;module&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;(&lt;/span&gt;elixir&lt;span class="o"&gt;)&lt;/span&gt; lib/kernel/parallel_compiler.ex:229: anonymous fn/4 &lt;span class="k"&gt;in &lt;/span&gt;Kernel.ParallelCompiler.spawn_workers/7
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;What? 😕🤷&lt;/p&gt;

&lt;p&gt;Ok, so what is happening here. It is seeing an unexpected token and it seems to be a left handed double quote. After banging my head against the wall for about a day, I decided to &lt;code&gt;rtfm&lt;/code&gt; again and luckily Boyd has us covered. See &lt;a href="https://github.com/boydm/phoenix_markdown#unexpected-token-in-server-tags"&gt;here&lt;/a&gt;, and I'll quote it below:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;By default Earmark replaces some characters with prettier UTF-8 versions. For example, single and double quotes are replaced with left- and right-handed versions. This may break any server tag which contains a prettified character since EEx cannot interpret them as intended. To fix this, disable smartypants processing.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So it is actually a simple fix in our config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:phoenix_markdown&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:earmark&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;
  &lt;span class="ss"&gt;smartypants:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Interactive blog...
&lt;/h2&gt;

&lt;p&gt;The example below is rendered using LiveView. You can see this if you head over to my &lt;a href="http://realworldphoenix.com/blog/2020-01-28/rendering_markdown#scramble"&gt;interactive blog&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="no"&gt;Phoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;LiveView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Helpers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;live_render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;@conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;RealWorldPhoenixWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Live&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Scrambled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And here is the LiveView component responsible for rendering the scrambled text:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;RealWorldPhoenixWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Live&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Scrambled&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Phoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;LiveView&lt;/span&gt;
  &lt;span class="nv"&gt;@moduledoc&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;assigns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="sx"&gt;~L""&lt;/span&gt;&lt;span class="s2"&gt;"
      &amp;lt;h2 id="&lt;/span&gt;&lt;span class="n"&gt;scramble&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;Let's Scramble!
        &amp;lt;button phx-click="&lt;/span&gt;&lt;span class="n"&gt;scramble&lt;/span&gt;&lt;span class="s2"&gt;" class="&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;
          &amp;lt;%= if @scrambled, do: "&lt;/span&gt;&lt;span class="n"&gt;un&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;scramble&lt;/span&gt;&lt;span class="s2"&gt;", else: "&lt;/span&gt;&lt;span class="n"&gt;scramble&lt;/span&gt;&lt;span class="s2"&gt;" %&amp;gt;
        &amp;lt;/button&amp;gt;
      &amp;lt;/h2&amp;gt;
      &amp;lt;%= get_scrambled_content(@scrambled) %&amp;gt;
    """&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:scrambled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;handle_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"scramble"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:noreply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:scrambled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;!socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assigns&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scrambled&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;get_scrambled_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Phoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;View&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;RealWorldPhoenixWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PostView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"blogs/2020-01-28/_scrambled.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;get_scrambled_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Phoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;View&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;RealWorldPhoenixWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PostView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"blogs/2020-01-28/_notscrambled.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Hold on buddy! This can't work!
&lt;/h2&gt;

&lt;p&gt;The above LiveView renders scrambled text and when the button is pressed it unscrambles it.&lt;/p&gt;

&lt;p&gt;Ah I was wondering if you would notice. You smartypants out there probably were already wondering how in the world I get this &lt;code&gt;scrambling&lt;/code&gt; functionality working. No, I'm not fooling you, it is running though our actual template engine. But indeed Phoenix pre-compiles the templates. That is also one of the reasons it is so fast.&lt;/p&gt;

&lt;p&gt;So it is not running through our template when you press that button. I confess. I have two versions of the same file it switches between, one with the &lt;code&gt;.scrambled&lt;/code&gt; extension and one without that are both precompiled by Phoenix when the app gets compiled. You will notice that the scrambling is also the same if you switch back and forth. That is because, once compiled, it does not recompile necessarily.&lt;/p&gt;

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

&lt;p&gt;Although my Scrambled Engine might not be so useful, I think it does illustrate how easy it can be to create a Template Engine that does something small and works pretty quickly out of the box.&lt;/p&gt;

&lt;p&gt;Be sure to check out the Core Example &lt;a href="https://hexdocs.pm/eex/EEx.html"&gt;EEx Engine&lt;/a&gt; if you want to dive in some more. And be sure to read the awesome docs about &lt;a href="https://hexdocs.pm/phoenix/Phoenix.Template.html#content"&gt;templating in Phoenix&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;I hope this was helpful and that you learned something new.&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>markdown</category>
    </item>
    <item>
      <title>Rendering Markdown in Elixir</title>
      <dc:creator>Tjaco Oostdijk</dc:creator>
      <pubDate>Wed, 29 Jan 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/kabisasoftware/rendering-markdown-in-elixir-2p42</link>
      <guid>https://dev.to/kabisasoftware/rendering-markdown-in-elixir-2p42</guid>
      <description>&lt;p&gt;Recently, I acquired the domain &lt;code&gt;realworldphoenix.com&lt;/code&gt; in order to continue my blogging adventure on a separate blog dedicated to writing exclusive content on Phoenix and Elixir (and some related subjects occassionally). I could of course take an off the shelf blogging platform or just use some kind of static site generator. But as I am writing about using Phoenix in the Real World, I figured why not just create a Phoenix application to host my blog. Then I can also do some nice things like make it a bit interactive or even let people sign-up and comment without using a third-party tool for that. We’ll see.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing content for a blog.
&lt;/h2&gt;

&lt;p&gt;As most of you would probably agree, writing long-form content using html is a bit of a pain. So most people use markdown to write blogposts and I’m no exception. So let’s find out how we can write markdown, put it under version control (just because that is nice to have) and then make sure it gets rendered as html in our Phoenix app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Earmark
&lt;/h2&gt;

&lt;p&gt;The go-to way to convert markdown to html in Elixir land is the great library written by Dave Thomas, &lt;a href="https://github.com/pragdave/earmark"&gt;Earmark&lt;/a&gt;. It is used by &lt;a href="https://github.com/elixir-lang/ex_doc"&gt;ex_doc&lt;/a&gt; and is a nice and well maintained library. We could of course convert our markdown in our phoenix controller directly using Earmark and output that in our view, but we can do even better. We can make sure that our Phoenix app can actually render markdown files stored in our template directly to html, just like it render’s &lt;code&gt;eex&lt;/code&gt; to html.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phoenix Markdown Library
&lt;/h2&gt;

&lt;p&gt;In fact there is even a library that handles this for us: &lt;a href="https://github.com/boydm/phoenix_markdown"&gt;Phoenix Markdown&lt;/a&gt;. Now I can just use this and be done with it (and as you are reading this… I am actually already using it ;) ), but my curious mind wants to know more! I’d like to know how this mechanism works so I have a better understanding of how rendering works in Phoenix. So, without further ado, let’s crack this baby open and take a look inside. Ooohhh what is that….?&lt;/p&gt;

&lt;h2&gt;
  
  
  It’s a Template Engine!
&lt;/h2&gt;

&lt;p&gt;Ofcourse, Phoenix has this all figured out. The way that &lt;code&gt;eex&lt;/code&gt; and &lt;code&gt;exs&lt;/code&gt; are implemented is as Phoenix Template Engines. It is fairly straightforward to create your own Template Engine by implementing the &lt;code&gt;Phoenix.Template.Engine&lt;/code&gt; behaviour. This is exactly what &lt;a href="https://github.com/boydm"&gt;Boyd Multerer&lt;/a&gt; has created in his library &lt;a href="https://github.com/boydm/phoenix_markdown"&gt;phoenix_markdown&lt;/a&gt;. Does that name sound familiar? Oh, that’s because boyd was also the creator of the awesome &lt;a href="https://github.com/boydm/scenic"&gt;Scenic Library&lt;/a&gt;. Great stuff!&lt;/p&gt;

&lt;h2&gt;
  
  
  How do we create a Template Engine?
&lt;/h2&gt;

&lt;p&gt;So how does one go about and create a template engine? Well, the best way to find out is to see if we can create one for ourselves, right? So let’s create a template engine that scrambles all the text that gets rendered by the engine. We’ll use the &lt;code&gt;Cambridge University Scrambled Text&lt;/code&gt; concept. So scramble all letters in words but keep the first and last letters intact. Fun fact, this so-called research was actually never done at Cambridge University! Somehow this internet meme got morphed into being a Cambridge research subject, but was actually never the case. Here’s a &lt;a href="https://www.mrc-cbu.cam.ac.uk/people/matt.davis/cmabridge/"&gt;nice article&lt;/a&gt; from a Cambridge Professor about this.This however doesn’t hold us back from using the concept to build our awesome scramble engine! So let’s get cakrcnig!&lt;/p&gt;

&lt;h2&gt;
  
  
  Our scramble Engine
&lt;/h2&gt;

&lt;p&gt;If you want to follow along you can create a fresh phoenix project and put the files in there to test this out yourself.We’ll define our engine in &lt;code&gt;lib/engine/scrambled.ex&lt;/code&gt;. The &lt;code&gt;Phoenix.Template.Engine&lt;/code&gt; behaviour requires us to implement &lt;code&gt;compile/2&lt;/code&gt;. In there we can add our magic formula. :)&lt;/p&gt;

&lt;p&gt;This is what I came up with for our use case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

defmodule RealWorldPhoenix.Engines.Scrambled do
  @moduledoc false

  @behaviour Phoenix.Template.Engine

  def compile(path, _name) do
    path
    |&amp;gt; File.read!()
    |&amp;gt; scramble_words()
    |&amp;gt; Earmark.as_html!()
    |&amp;gt; EEx.compile_string(engine: Phoenix.HTML.Engine, file: path, line: 1)
  end

  defp scramble_words(content) do
    content
    |&amp;gt; String.split("\n", trim: true)
    |&amp;gt; Enum.map(&amp;amp;shuffle_words_in_sentence/1)
    |&amp;gt; Enum.join("\n")
  end

  defp shuffle_words_in_sentence(sentence) do
    sentence
    |&amp;gt; String.split(" ")
    |&amp;gt; Enum.map(&amp;amp;shuffle/1)
    |&amp;gt; Enum.join(" ")
  end

  defp shuffle(word) do
    if String.length(word) &amp;lt; 4 do
      word
    else
      scrambled =
        word
        |&amp;gt; String.split("", trim: true)
        |&amp;gt; Enum.slice(1..-2)
        |&amp;gt; Enum.shuffle()
        |&amp;gt; Enum.join("")

      String.first(word) &amp;lt;&amp;gt; scrambled &amp;lt;&amp;gt; String.last(word)
    end
  end
end

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

&lt;/div&gt;



&lt;p&gt;And to use this in our Phoenix app, we need to configure the file extension we want to invoke scrambling for. You can see this is the same as I did in my app to configure &lt;code&gt;phoenix_markdown&lt;/code&gt;. At compile time Phoenix will match any templates with the &lt;code&gt;.scrambled&lt;/code&gt; extension and will run that source file through our Engine by calling our &lt;code&gt;compile/2&lt;/code&gt; function defined in our Engine module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5

# config/config.exs

config :phoenix, :template_engines,
  md: PhoenixMarkdown.Engine,
  scrambled: RealWorldPhoenix.Engines.Scrambled

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

&lt;/div&gt;



&lt;p&gt;So I am expecting this to scramble content written in markdown. I’m just considering paragraphs of text, just to not complicate things unnecessarily for this use case. That is why I am basically splitting sentences by splitting on the newline character and then processing each sentence and the containing words before joining them together again with newlines. Seems pretty straightforward. Let’s see if this works!&lt;/p&gt;

&lt;h2&gt;
  
  
  Smartypants… 🤔
&lt;/h2&gt;

&lt;p&gt;Phoenix Markdown has an option to render server_tags, which basically means that you can invoke Elixir inside your markdown page using the standard tags you use in &lt;code&gt;eex&lt;/code&gt; templates to invoke some Elixir code and evaluate it. I wanted to use this to render the following bit of text to illustrate my &lt;code&gt;scrambled&lt;/code&gt; engine, but the first pass led to this error while compiling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9

== Compilation error in file lib/real_world_phoenix_web/views/post_view.ex ==
** (SyntaxError) lib/real_world_phoenix_web/templates/post/blogs/2020-01-28/rendering_markdown.html.md:60: unexpected token: "“" (column 39, code point U+201C)
    (eex) lib/eex/compiler.ex:45: EEx.Compiler.generate_buffer/4
    (phoenix) lib/phoenix/template.ex:361: Phoenix.Template.compile/3
    (phoenix) lib/phoenix/template.ex:167: anonymous fn/4 in Phoenix.Template."MACRO- __before_compile__"/2
    (elixir) lib/enum.ex:1948: Enum."-reduce/3-lists^foldl/2-0-"/3
    (phoenix) expanding macro: Phoenix.Template. __before_compile__ /1
    lib/real_world_phoenix_web/views/post_view.ex:1: RealWorldPhoenixWeb.PostView (module)
    (elixir) lib/kernel/parallel_compiler.ex:229: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/7

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

&lt;/div&gt;



&lt;p&gt;What? 😕🤷&lt;/p&gt;

&lt;p&gt;Ok, so what is happening here. It is seeing an unexpected token and it seems to be a left handed double quote. After banging my head against the wall for about a day, I decided to &lt;code&gt;rtfm&lt;/code&gt; again and luckily Boyd has us covered. See &lt;a href="https://github.com/boydm/phoenix_markdown#unexpected-token-in-server-tags"&gt;here&lt;/a&gt;, and I’ll quote it below:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;By default Earmark replaces some characters with prettier UTF-8 versions. For example, single and double quotes are replaced with left- and right-handed versions. This may break any server tag which contains a prettified character since EEx cannot interpret them as intended. To fix this, disable smartypants processing.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So it is actually a simple fix in our config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3

config :phoenix_markdown, :earmark, %{
  smartypants: false
}

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Interactive blog…
&lt;/h2&gt;

&lt;p&gt;The example below is rendered using LiveView. You can see this if you head over to my &lt;a href="http://realworldphoenix.com/blog/2020-01-28/rendering_markdown#scramble"&gt;interactive blog&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1

&amp;lt;%= Phoenix.LiveView.Helpers.live_render(@conn, RealWorldPhoenixWeb.Live.Scrambled) %&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;And here is the LiveView component responsible for rendering the scrambled text:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

defmodule RealWorldPhoenixWeb.Live.Scrambled do
  use Phoenix.LiveView
  @moduledoc false

  def render(assigns) do
    ~L"""
      &amp;lt;h2 id="scramble"&amp;gt;Let's Scramble!
        &amp;lt;button phx-click="scramble" class="button"&amp;gt;
          &amp;lt;%= if @scrambled, do: "un-scramble", else: "scramble" %&amp;gt;
        &amp;lt;/button&amp;gt;
      &amp;lt;/h2&amp;gt;
      &amp;lt;%= get_scrambled_content(@scrambled) %&amp;gt;
    """
  end

  def mount(_session, socket) do
    {:ok, assign(socket, :scrambled, true)}
  end

  def handle_event("scramble", _value, socket) do
    {:noreply, assign(socket, :scrambled, !socket.assigns.scrambled)}
  end

  defp get_scrambled_content(true) do
    Phoenix.View.render(RealWorldPhoenixWeb.PostView, "blogs/2020-01-28/_scrambled.html", [])
  end

  defp get_scrambled_content(false) do
    Phoenix.View.render(RealWorldPhoenixWeb.PostView, "blogs/2020-01-28/_notscrambled.html", [])
  end
end

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Hold on buddy! This can’t work!
&lt;/h2&gt;

&lt;p&gt;The above LiveView renders scrambled text and when the button is pressed it unscrambles it.&lt;/p&gt;

&lt;p&gt;Ah I was wondering if you would notice. You smartypants out there probably were already wondering how in the world I get this &lt;code&gt;scrambling&lt;/code&gt; functionality working. No, I’m not fooling you, it is running though our actual template engine. But indeed Phoenix pre-compiles the templates. That is also one of the reasons it is so fast.&lt;/p&gt;

&lt;p&gt;So it is not running through our template when you press that button. I confess. I have two versions of the same file it switches between, one with the &lt;code&gt;.scrambled&lt;/code&gt; extension and one without that are both precompiled by Phoenix when the app gets compiled. You will notice that the scrambling is also the same if you switch back and forth. That is because, once compiled, it does not recompile necessarily.&lt;/p&gt;

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

&lt;p&gt;Although my Scrambled Engine might not be so useful, I think it does illustrate how easy it can be to create a Template Engine that does something small and works pretty quickly out of the box.&lt;/p&gt;

&lt;p&gt;Be sure to check out the Core Example &lt;a href="https://hexdocs.pm/eex/EEx.html"&gt;EEx Engine&lt;/a&gt; if you want to dive in some more. And be sure to read the awesome docs about &lt;a href="https://hexdocs.pm/phoenix/Phoenix.Template.html#content"&gt;templating in Phoenix&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;I hope this was helpful and that you learned something new.&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Real World Phoenix |&gt; Let's D4Y |&gt; __using__ K8S</title>
      <dc:creator>Tjaco Oostdijk</dc:creator>
      <pubDate>Tue, 21 Jan 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/kabisasoftware/real-world-phoenix-let-s-d4y-using-k8s-22hf</link>
      <guid>https://dev.to/kabisasoftware/real-world-phoenix-let-s-d4y-using-k8s-22hf</guid>
      <description>&lt;p&gt;As promised in my &lt;a href="https://www.theguild.nl/real-world-phoenix-lets-d4y/"&gt;last post&lt;/a&gt;, here is step 3 in my deployment adventure. The onewhere I use Kubernetes!&lt;/p&gt;

&lt;p&gt;In the last post we explored a very straightforward and simpleway to deploy a Phoenix application using the built-in Elixir releases on render.com.&lt;/p&gt;

&lt;p&gt;Today I want to discuss a much more complicated setup, using Kubernetes. I know formost applications you will surely not need a setup using Kubernetes, but I’vebeen having an urge to try it out for a while now and wanted to see if it isreally that complicated as I seem to be hearing through the grapevine. Whoknows, maybe it turns out it’s actually not that hard at all. Also I really like the fact that I can have a cluster to spin up nodes (ie.applications) of all the experiments / ideas / apps that I’m currently playingwith, so I can actually expose those into the Real World with a reproducable andeasy deployment workflow. Let’s see if we can make that dream come true!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Kubernetes Cluster
&lt;/h2&gt;

&lt;p&gt;If you are not aware of what Kubernetes is, head over to &lt;a href="https://kubernetes.io/docs/tutorials/kubernetes-basics/"&gt;their website&lt;/a&gt;, as they have great documentation to get started on the basics.&lt;/p&gt;

&lt;p&gt;To use Kubernetes for our deployment the first thing we’ll need is a Kubernetescluster. This is the part where you see most tutorials and guides grab for alocal setup with minikube. While I like that it’s possible to do this, it stillseems a bit too far from real life for me, so I wanted to have a bit more of aproduction-like environment, like… for instance… a production environment :)&lt;/p&gt;

&lt;p&gt;The beauty is that these days there are more and more managed Kubernetesservices that do all the hard work like setting up the cluster, managingupgrades etc etc. I’ll be using the service DigitalOcean is offering. Mainly because Ialready have a lot of content running at DigitalOcean, so it is known territoryfor me and they make it really easy to get started with Kubernetes.If you haven’t used DigitalOcean before, you can use this &lt;a href="https://m.do.co/c/78020d21b236"&gt;referrallink&lt;/a&gt; to create an account and get $100 creditto spend in the first 60 days. So more than enough to follow along with this tutorial.&lt;/p&gt;

&lt;h2&gt;
  
  
  Terraform + Helm
&lt;/h2&gt;

&lt;p&gt;We are going to need to use 2 more tools to get this setup up and running. &lt;a href="https://www.terraform.io/"&gt;Terraform&lt;/a&gt; and &lt;a href="https://helm.sh/"&gt;Helm&lt;/a&gt;. We’ll use terraform to setup most of the infrastructure. In this way I don’t have to remember all the tweaks and install steps I took to get everything up and running. I can just automate the whole setup declaratively and put the whole thing under version control. This is often referred to these days as an infrastructure-as-code setup.I wanted to use terraform for everything, but ran into some trouble setting up a few of the tools needed, so reverted to Helm (the Kubernetes package manager) toinstall Traefik and our Gitlab Runner.&lt;/p&gt;

&lt;p&gt;Our Terraform setup will include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Setting up a DigitalOcean Kubernetes Cluster with 2 worker nodes&lt;/li&gt;
&lt;li&gt;Connecting a gitlab project to the kubernetes cluster&lt;/li&gt;
&lt;li&gt;Creating a kubernetes_secret to be able to pull images from our private gitlabregistry.&lt;/li&gt;
&lt;li&gt;Setup a managed database cluster service @ digitalocean&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And we’ll use Helm to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install Traefik as an ingress controller&lt;/li&gt;
&lt;li&gt;Create a Gitlab Runner in our cluster&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So let’s get crackin’!&lt;/p&gt;

&lt;h3&gt;
  
  
  Install doctl to talk to your DigitalOcean account
&lt;/h3&gt;

&lt;p&gt;If we want to automate any of this stuff we’ll have to be able to talk toDigitalOcean programmatically, so installing doctl is step one. Please refer tothis guide to get that setup: &lt;a href="https://blog.digitalocean.com/introducing-doctl/"&gt;doctl up and runningguide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After you have &lt;code&gt;doctl&lt;/code&gt; installed, make sure you create a &lt;a href="https://www.digitalocean.com/docs/api/create-personal-access-token/"&gt;personal access token&lt;/a&gt; in DigitalOcean and add that in an environment variable and also initialize youraccount.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3

export DIGITALOCEAN_TOKEN=[token]
doctl auth init -t $DIGITALOCEAN_TOKEN
doctl account get

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install Terraform
&lt;/h3&gt;

&lt;p&gt;Of course there is a handy install guide for terraform also. Actually just anexecutable, as it is written in go, so they have a handy packaged binary you can use.See: &lt;a href="https://www.terraform.io/downloads.html"&gt;terraform install&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup our project
&lt;/h3&gt;

&lt;p&gt;If you want to follow along with this guide, create a folder that will hold yourterraform configs and create a &lt;code&gt;main.tf&lt;/code&gt; file that will hold the terraform declarations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3

mkdir kube-terra
cd kube-terra
touch main.tf

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setup the DigitalOcean Kubernetes cluster
&lt;/h3&gt;

&lt;p&gt;Terraform works in a declaritive manner, which means that we state what we wantthe world to be like and terraform figures out how to get to that state. Interraform you describe an item you want to exist as a resource. A Kubernetescluster is a form of a resource that is provided by one of the many providersthat exist in terraform. So let’s create a DigitalOcean kubernetes cluster:&lt;/p&gt;

&lt;p&gt;Creating a resource is always in the format: &lt;code&gt;resource [kind] [reference] {}&lt;/code&gt;We can use the reference we provide later on to refer back to this resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

resource "digitalocean_tag" "kubernetes-cl01" {
  name = "kubernetes-cl01"
}

data "digitalocean_kubernetes_versions" "versions" {}

resource "digitalocean_kubernetes_cluster" "cl01" {
  name = "cl01"
  region = "ams3"
  version = data.digitalocean_kubernetes_versions.versions.latest_version

  node_pool {
    name = "default"
    size = "s-1vcpu-2gb"
    node_count = 2
    tags = ["${digitalocean_tag.kubernetes-cl01.id}"]
  }
}

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

&lt;/div&gt;



&lt;p&gt;Now you can get your cluster setup by issuing the following commands in the folder youcreated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2

tarraform init
terraform apply

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

&lt;/div&gt;



&lt;p&gt;This will take about 5 minutes and then you’ll have a fresh kubernetes clusterup and running. Now that was easy!&lt;/p&gt;

&lt;h3&gt;
  
  
  Install Traefik
&lt;/h3&gt;

&lt;p&gt;Kubernetes needs an ingress controller to route traffic from the outside worldto the services that are running inside the cluster as these services are notexposed to the outside world, which is a good thing! Nginx is often used, but I went with Traefik, a very nice alternative that has a lot of nice functinality out of the box, like automatic ssl certificates using Let’s Encryptand auto discovery of services running in the cluster. This setup also takes advantage of a DigitalOcean load balancer, which will automatically be created by the serviceType set below to &lt;code&gt;LoadBalancer&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Create a file called &lt;code&gt;traefik-values.yml&lt;/code&gt; in the root of your kubernetes config directory and add the following content. If you manage dns settings with DigitalOcean, you can comment out those parts as well. This will provide automatic ssl certificate generation. Very cool! If not you can just leave it like this. It’ll work, but without ssl enabled. A benefit of using the dns-challenge as opposed to the more standard acme-challenge is that you can also get a valid certificate if your production system is behind a firewall.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

image: traefik
dashboard:
  enabled: true
  domain: domain.example.com # put a (sub)domain here where you want to access the traefik dashboard
serviceType: LoadBalancer
rbac:
  enabled: true
# ssl:
# enabled: true # Enables SSL
# enforced: true # Redirects HTTP to HTTPS
# acme:
# enabled: true # Enables Let's Encrypt certificates
# staging: false # Use Lets Encrypt staging area for this example. For production purposes set this to false
# email: info@drumusician.com # Email address that Let's Encrypt uses to notify about certificate expiry etc.
# challengeType: "dns-01"
# dnsProvider:
# name: digitalocean # This is why you need your domain to be under Digital Ocean control
# digitalocean:
# DO_AUTH_TOKEN: $DIGITALOCEAN_TOKEN
# domains:
# enabled: true
# domainsList:
# - main: domain.example.com # domain that belongs to this certificate

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

&lt;/div&gt;



&lt;p&gt;Now we can go ahead and install traefik in our cluster:&lt;/p&gt;

&lt;p&gt;Install helm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1

brew install kubernetes-helm

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

&lt;/div&gt;



&lt;p&gt;Pull in our fresh cluster configuration locally (otherwise helm willfail):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1

doctl kubernetes cluster kubeconfig save cl01

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

&lt;/div&gt;



&lt;p&gt;And install traefik:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4

# get the helm/stable charts
helm repo add stable https://kubernetes-charts.storage.googleapis.com/
# install Traefik
helm install traefik --values traefik-values.yml stable/traefik

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

&lt;/div&gt;



&lt;p&gt;After Traefik is installed we have enough running to be able to deploy our applicationto the cluster. We are going to use gitlab-ci to deploy so we’ll need toinstall a gitlab-runner in our cluster first so we can use that in our project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a kubernetes secret to access our registry
&lt;/h3&gt;

&lt;p&gt;If we install the Gitlab runner in the next step, we’ll need a kubernetes secretto be able to pull from our private registry in gitlab.Go to &lt;a href="https://gitlab.com/profile/personal%5C_access%5C_tokens"&gt;https://gitlab.com/profile/personal\_access\_tokens&lt;/a&gt; and create an accesstoken to access the gitlab registry. Scopes: api, read_registry.&lt;/p&gt;

&lt;p&gt;Then create a file at &lt;code&gt;~/.docker/docker-registry.json&lt;/code&gt; with the credentials forthe access token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7

{
  "registry.gitlab.com": {
    "username": "", # access-token-name
    "password": "", # token
    "email": "" # email of the gitlab account
  }
}

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

&lt;/div&gt;



&lt;p&gt;And then in our main.tf we can create the kubernetes secret.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11

resource "kubernetes_secret" "docker_pull_secret" {
  metadata {
    name = "gitlab.com"
  }

  data = {
    ".dockercfg" = file("~/.docker/docker-registry.json")
  }

  type = "kubernetes.io/dockercfg"
}

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

&lt;/div&gt;



&lt;p&gt;With that in place, we’ll have to initialize the new provider and then we can add the secret:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2

terraform init
terraform apply

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install Gitlab Runner
&lt;/h3&gt;

&lt;p&gt;To be able to deploy into our cluster from Gitlab CI we will need our runner to run inside of our cluster and have enough access rights to actually spin-up pods and services in our cluster. So we’ll install that using helm as well. Gitlab has an install script from their interface, but that doesn’t provide you with customisation options that we’ll need for our use case.&lt;/p&gt;

&lt;p&gt;Create the following file in your kubernetes dir: &lt;code&gt;gitlab-runner-values.yml&lt;/code&gt; andadd this content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8

gitlabUrl: https://gitlab.com/
runnerRegistrationToken: "" # copy this from your gitlab project settings: 
rbac:
  create: true
  clusterWideAccess: true
runners:
  privileged: true
imagePullSecrets: ["gitlab.com"]

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

&lt;/div&gt;



&lt;p&gt;Now let’s install the runner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1

helm install --namespace default gitlab-runner -f gitlab-runner-values.yml gitlab/gitlab-runner

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Connect a gitlab project to the cluster
&lt;/h3&gt;

&lt;p&gt;This is something you can do through the Gitlab interface, but I like automatingit here as well. We need to provide the Gitlab project in terraform like this.If you don’t have a project yet, you should create it on &lt;code&gt;gitlab.com&lt;/code&gt; and add theID here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3

data "gitlab_project" "project-x" {
  id = [your-project-id]
}

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

&lt;/div&gt;



&lt;p&gt;Then we can use this reference to create the settings in our project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12

resource "gitlab_project_cluster" "gitlab-kubernetes" {
  project = data.gitlab_project.project-x.id
  name = "my-awesome-cluster"
  domain = "[mydomain.com]"
  enabled = true
  kubernetes_api_url = digitalocean_kubernetes_cluster.cl01.endpoint
  kubernetes_token = digitalocean_kubernetes_cluster.cl01.kube_config[0].token
  kubernetes_ca_cert = base64decode(digitalocean_kubernetes_cluster.cl01.kube_config[0].cluster_ca_certificate)
  kubernetes_namespace = ""
  kubernetes_authorization_type = "rbac"
  environment_scope = "*"
}

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

&lt;/div&gt;



&lt;p&gt;And lastly we are not going the skip the database, as that is too easy to do.You could potentially setup a database persistent volume in kubernetes and havedb_pods spinup, but I think that it is much easier to have thedatabase as a separate service outside of kubernetes. The DigitalOcean manageddatabase service is a great option. You pay a little extra, but it takes care ofbackups etc. It is just one of those things that you don’t want to worry about,right?&lt;/p&gt;

&lt;p&gt;Of course, we can easily add the creation of a database cluster to our terraformsetup. Note that I am using only 1 node for the cluster in this example. For actual failsafe production usage, you probably want to have at least 2 nodes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8

resource "digitalocean_database_cluster" "postgres-db" {
  name = "postgres-db-cluster"
  engine = "pg"
  version = "11"
  size = "db-s-1vcpu-1gb"
  region = "ams3"
  node_count = 1
}

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

&lt;/div&gt;



&lt;p&gt;And while we are at it, let’s create the database and firewall settings as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13

resource "digitalocean_database_db" "test-prod" {
  cluster_id = digitalocean_database_cluster.postgres-db.id
  name = "test_prod"
}

resource "digitalocean_database_firewall" "db-fw" {
  cluster_id = digitalocean_database_cluster.postgres-db.id

  rule {
    type = "k8s"
    value = digitalocean_kubernetes_cluster.cl01.id
  }
}

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

&lt;/div&gt;



&lt;p&gt;This concludes our cluster setup. To get our app deployed we’ll have to add somesetup to our project. We’ll need to dockerize our project, add kubectl deployconfig files and add a gitlab-ci.yml that will trigger the gitlab-ci pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  A project to deploy
&lt;/h2&gt;

&lt;p&gt;Now we have our kubernetes cluster up-and-running it is time to see how we woulddeploy our app to the cluster.&lt;/p&gt;

&lt;p&gt;I have a git tag prepared for you to use from my real_world_phoenix project. Youcan clone that checkout to use to deploy to the cluster. It has the necessaryfiles to trigger deployment which I’ll explain next.&lt;/p&gt;

&lt;p&gt;Go ahead and clone the project from the tag I created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1

git clone https://gitlab.com/drumusician/real_world_phoenix.git --branch kubernetes-deploy

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Gitlab pipeline
&lt;/h3&gt;

&lt;p&gt;To get our app deployed to kubernetes we’ll need to package it up in a containerand we’ll use Gitlab CI system for all of this. That means we’llprepare our app and package it using a 2-step dockerfile. The advantage of the2-step dockerfile method is that we can use a larger docker image to package ourapp that has all the packages for building and packaging our release and use a much slimmer container to just run our release. The release will be self-contained, so there is not a lot needed for it to run.&lt;/p&gt;

&lt;p&gt;The steps we’ll need in our CI file are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;init # some compiling and pushing of artifacts&lt;/li&gt;
&lt;li&gt;build and push # build our container and push to our registry&lt;/li&gt;
&lt;li&gt;deploy # deploy to our kubernetes cluster&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For pushing we’ll use the gitlab container registry that is available in everygitlab project. The deployment will be a collection of kubernetes yaml filesthat we can version control in our project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dockerfile
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

# ---- Build Stage ----
FROM elixir:alpine AS app_builder

# Set environment variables for building the application
ENV MIX_ENV=prod \
    TEST=1 \
    LANG=C.UTF-8

RUN apk add --update git nodejs npm &amp;amp;&amp;amp; \
    rm -rf /var/cache/apk/*

# Install hex and rebar
RUN mix local.hex --force &amp;amp;&amp;amp; \
    mix local.rebar --force

# Create the application build directory
RUN mkdir /app
WORKDIR /app

# Copy over all the necessary application files and directories
COPY config ./config
COPY lib ./lib
COPY priv ./priv
COPY assets ./assets
COPY mix.exs .
COPY mix.lock .

# Fetch the application dependencies and build the application
RUN mix deps.get
RUN mix deps.compile
RUN npm run deploy --prefix ./assets
RUN mix phx.digest
RUN mix release

# ---- Application Stage ----
FROM alpine AS app

ENV LANG=C.UTF-8

# Install openssl
RUN apk add --update openssl ncurses-libs postgresql-client &amp;amp;&amp;amp; \
    rm -rf /var/cache/apk/*

# Copy over the build artifact from the previous step and create a non root user
RUN adduser -D -h /home/app app
WORKDIR /home/app
COPY --from=app_builder /app/_build .
RUN chown -R app: ./prod
USER app

COPY entrypoint.sh .

# Run the Phoenix app
CMD ["./entrypoint.sh"]

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Entrypoint
&lt;/h3&gt;

&lt;p&gt;The entrypoint.sh is basically just a bash script that verifies the db is upand running before starting and running any pending migrations before startup aswell.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13

#!/bin/sh
# Docker entrypoint script.

# Wait until Postgres is ready
# while ! pg_isready -q -h $DB_HOST -p 5432 -U $DB_USER
# do
# echo "$(date) - waiting for database to start"
# sleep 2
# done

./prod/rel/real_world_phoenix/bin/real_world_phoenix eval RealWorldPhoenix.Release.migrate

./prod/rel/real_world_phoenix/bin/real_world_phoenix start

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Gitlab CI Yaml file
&lt;/h3&gt;

&lt;p&gt;To trigger the gitlab-ci pipeline we’ll need to add a &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; file inthe root of our project.&lt;/p&gt;

&lt;p&gt;This is the content needed to initialize, build, push and deploy using the CIpipeline. We start off creating a few variables we’ll need and then define thestages. I have added some comments in the yaml file below that should explainthe steps in detail.&lt;/p&gt;

&lt;p&gt;Note the use of &lt;a href="https://github.com/GoogleContainerTools/kaniko"&gt;Kaniko&lt;/a&gt; to build the container and push it to our registry. Kaniko doesn’t depend on a Docker daemon and executes each command within a Dockerfile completely in userspace. This enables building container images in environments that can’t easily or securely run a Docker daemon, such as a our Kubernetes cluster. Very nice!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

variables:
  REGISTRY: registry.gitlab.com
  CONTAINER_RELEASE_IMAGE: $REGISTRY/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME:$CI_COMMIT_SHORT_SHA

stages:
  - init
  - build
  - deploy

# the dot syntax makes this a hidden step that we can include in other places further down.
.elixir_default: &amp;amp;elixir_default
  image: elixir:1.9
  before_script:
    - mix local.hex --force
    - mix local.rebar --force

.javascript_default: &amp;amp;javascript_default
  image: node:alpine
  before_script:
    - cd assets

# Compile our elixir artifacts
elixir_compile:
  &amp;lt;&amp;lt;: *elixir_default
  stage: init
  script:
    - mix deps.get --only test
    - mix compile
    - mix compile --warnings-as-errors
  artifacts:
    paths:
      - mix.lock
      - _build
      - deps

# compile javascript artifacts
javascript_deps:
  &amp;lt;&amp;lt;: *javascript_default
  stage: init
  script:
    - npm install --progress=false
  artifacts:
    paths:
      - assets/node_modules

# build our container image
build:
  stage: build
  image:
    name: gcr.io/kaniko-project/executor:debug
    entrypoint: [""]
  script:
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" &amp;gt; /kaniko/.docker/config.json
    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CONTAINER_RELEASE_IMAGE

# and deploy our kubernetes cluster
deploy:
  stage: deploy
  image:
    name: lwolf/kubectl_deployer:latest
  script:
    - cat deploy.yml | envsubst | kubectl apply -f -
    - cat service.yml | envsubst | kubectl apply -f -
    - cat ingress.yml | envsubst | kubectl apply -f -
  only:
    refs:
      - master

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Yaml deployment files: service, ingress and deploy
&lt;/h3&gt;

&lt;p&gt;The last thing we need is the kubernetes yaml files that are used in the cipipeline:&lt;/p&gt;

&lt;h4&gt;
  
  
  service.yml
&lt;/h4&gt;

&lt;p&gt;The service pod running our application. This is basically our app running in a docker container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11

apiVersion: v1
kind: Service
metadata:
  name: real-world-phoenix-service
spec:
  ports:
  - name: web
    port: 8001
    protocol: TCP
  selector:
    app: real_world_phoenix

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

&lt;/div&gt;



&lt;h4&gt;
  
  
  ingress.yml
&lt;/h4&gt;

&lt;p&gt;The mapping of our domain name to our service&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: real-world-phoenix-ingress
spec:
  rules:
  - host: $DOMAIN
    http:
      paths:
      - path: "/"
        backend:
          serviceName: real-world-phoenix-service
          servicePort: web

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

&lt;/div&gt;



&lt;h4&gt;
  
  
  deploy.yml
&lt;/h4&gt;

&lt;p&gt;Our deployment configuration. This provides Kubernetes the info about which container image to use and how many replicas of our app we want to have running.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

apiVersion: apps/v1
kind: Deployment
metadata:
  name: real-world-phoenix
  labels:
    app: real_world_phoenix
spec:
  revisionHistoryLimit: 5
  replicas: 1
  selector:
    matchLabels:
      app: real_world_phoenix
  template:
    metadata:
      labels:
        app: real_world_phoenix
    spec:
      imagePullSecrets:
        - name: "gitlab.com"
      containers:
      - name: real-world-phoenix
        image: registry.gitlab.com/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME:$CI_COMMIT_SHORT_SHA
        imagePullPolicy: Always
        name: real-world-phoenix-deployment
        resources: {}
        ports:
          - containerPort: 8001
        env:
          - name: SECRET_KEY_BASE
            value: $SECRET_KEY_BASE
          - name: DB_USER
            value: $DB_USER
          - name: DB_PASSWORD
            value: $DB_PASS
          - name: DB_NAME
            value: "test_prod"
          - name: DB_HOST
            value: $DB_CLUSTER
          - name: DB_PORT
            value: "25060"
          - name: APP_PORT
            value: "8001"
          - name: APP_HOSTNAME
            value: $DOMAIN

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

&lt;/div&gt;



&lt;p&gt;The above yaml file has references to a number of environment variables. The nice thing is that we can add all of these secrets inside of our Gitlab project and the deployment will use these values. In your project go to &lt;code&gt;Settings -&amp;gt; CI/CD -&amp;gt; Variables&lt;/code&gt;.Now if we have all of this in our project all we have to do is commit and push it to our gitlab repo. Then Gitlab CI will do it’s magic and our application will be up and running after the pipeline jobs succeed. Nice!&lt;/p&gt;

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

&lt;p&gt;While it is not necessarily the easiest setup, I really like the fact that once I have this setup I can fairly easily put some configuration in any of my gitlab projects and CI will take care of the rest, even the ssl certificate is handled for me so I don’t have to think about renewing them and all that stuff.&lt;/p&gt;

&lt;p&gt;The reduction of manual work by using terraform is also a really nice benefit here. Terraform is a powerful tool, so use with care! If there is any interest in exploring terraform more, please leave a comment and I’ll plan in some more in-depth post exploring the world of terraform.&lt;/p&gt;

&lt;p&gt;I hope this post was easy to follow and I hope that for those who followed along everything worked as expected? If not, do let me know, because I want to make sure everything here works as expected.&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Real World Phoenix |&gt; Let's D4Y |&gt; using K8S</title>
      <dc:creator>Tjaco Oostdijk</dc:creator>
      <pubDate>Tue, 21 Jan 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/kabisasoftware/real-world-phoenix-let-s-d4y-using-k8s-8i</link>
      <guid>https://dev.to/kabisasoftware/real-world-phoenix-let-s-d4y-using-k8s-8i</guid>
      <description>&lt;p&gt;As promised in my &lt;a href="https://www.theguild.nl/real-world-phoenix-lets-d4y/"&gt;last post&lt;/a&gt;, here is step 3 in my deployment adventure. The one&lt;br&gt;
where I use Kubernetes!&lt;/p&gt;

&lt;p&gt;In the last post we explored a very straightforward and simple&lt;br&gt;
way to deploy a Phoenix application using the built-in Elixir releases on render.com. &lt;/p&gt;

&lt;p&gt;Today I want to discuss a much more complicated setup, using Kubernetes. I know for&lt;br&gt;
most applications you will surely not need a setup using Kubernetes, but I've&lt;br&gt;
been having an urge to try it out for a while now and wanted to see if it is&lt;br&gt;
really that complicated as I seem to be hearing through the grapevine. Who&lt;br&gt;
knows, maybe it turns out it's actually not that hard at all. &lt;br&gt;
Also I really like the fact that I can have a cluster to spin up nodes (ie.&lt;br&gt;
applications) of all the experiments / ideas / apps that I'm currently playing&lt;br&gt;
with, so I can actually expose those into the Real World with a reproducable and&lt;br&gt;
easy deployment workflow. Let's see if we can make that dream come true! &lt;/p&gt;
&lt;h2&gt;
  
  
  The Kubernetes Cluster
&lt;/h2&gt;

&lt;p&gt;If you are not aware of what Kubernetes is, head over to &lt;a href="https://kubernetes.io/docs/tutorials/kubernetes-basics/"&gt;their website&lt;/a&gt;, as they have great documentation to get started on the basics.&lt;/p&gt;

&lt;p&gt;To use Kubernetes for our deployment the first thing we'll need is a Kubernetes&lt;br&gt;
cluster. This is the part where you see most tutorials and guides grab for a&lt;br&gt;
local setup with minikube. While I like that it's possible to do this, it still&lt;br&gt;
seems a bit too far from real life for me, so I wanted to have a bit more of a&lt;br&gt;
production-like environment, like... for instance... a production environment :) &lt;/p&gt;

&lt;p&gt;The beauty is that these days there are more and more managed Kubernetes&lt;br&gt;
services that do all the hard work like setting up the cluster, managing&lt;br&gt;
upgrades etc etc. I'll be using the service DigitalOcean is offering. Mainly because I&lt;br&gt;
already have a lot of content running at DigitalOcean, so it is known territory&lt;br&gt;
for me and they make it really easy to get started with Kubernetes.&lt;br&gt;
If you haven't used DigitalOcean before, you can use this &lt;a href="https://m.do.co/c/78020d21b236"&gt;referral&lt;br&gt;
link&lt;/a&gt; to create an account and get $100 credit&lt;br&gt;
to spend in the first 60 days. So more than enough to follow along with this tutorial.&lt;/p&gt;
&lt;h2&gt;
  
  
  Terraform + Helm
&lt;/h2&gt;

&lt;p&gt;We are going to need to use 2 more tools to get this setup up and running. &lt;a href="https://www.terraform.io/"&gt;Terraform&lt;/a&gt; and &lt;a href="https://helm.sh/"&gt;Helm&lt;/a&gt;. We'll use terraform to setup most of the infrastructure. In this way I don't have to remember all the tweaks and install steps I took to get everything up and running. I can just automate the whole setup declaratively and put the whole thing under version control. &lt;br&gt;
This is often referred to these days as an infrastructure-as-code setup.&lt;br&gt;
I wanted to use terraform for everything, but ran into some trouble setting up a &lt;br&gt;
few of the tools needed, so reverted to Helm (the Kubernetes package manager) to&lt;br&gt;
install Traefik and our Gitlab Runner. &lt;/p&gt;

&lt;p&gt;Our Terraform setup will include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Setting up a DigitalOcean Kubernetes Cluster with 2 worker nodes&lt;/li&gt;
&lt;li&gt;Connecting a gitlab project to the kubernetes cluster&lt;/li&gt;
&lt;li&gt;Creating a kubernetes_secret to be able to pull images from our private gitlab
registry.&lt;/li&gt;
&lt;li&gt;Setup a managed database cluster service @ digitalocean&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And we'll use Helm to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install Traefik as an ingress controller&lt;/li&gt;
&lt;li&gt;Create a Gitlab Runner in our cluster &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So let's get crackin'!&lt;/p&gt;
&lt;h3&gt;
  
  
  Install doctl to talk to your DigitalOcean account
&lt;/h3&gt;

&lt;p&gt;If we want to automate any of this stuff we'll have to be able to talk to&lt;br&gt;
DigitalOcean programmatically, so installing doctl is step one. Please refer to&lt;br&gt;
this guide to get that setup: &lt;a href="https://blog.digitalocean.com/introducing-doctl/"&gt;doctl up and running&lt;br&gt;
guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After you have &lt;code&gt;doctl&lt;/code&gt; installed, make sure you create a &lt;a href="https://www.digitalocean.com/docs/api/create-personal-access-token/"&gt;personal access token&lt;/a&gt; in DigitalOcean and add that in an environment variable and also initialize your&lt;br&gt;
account.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DIGITALOCEAN_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=[&lt;/span&gt;token]
doctl auth init &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nv"&gt;$DIGITALOCEAN_TOKEN&lt;/span&gt;
doctl account get
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Install Terraform
&lt;/h3&gt;

&lt;p&gt;Of course there is a handy install guide for terraform also. Actually just an&lt;br&gt;
executable, as it is written in go, so they have a handy packaged binary you can use.&lt;br&gt;
See: &lt;a href="https://www.terraform.io/downloads.html"&gt;terraform install&lt;/a&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Setup our project
&lt;/h3&gt;

&lt;p&gt;If you want to follow along with this guide, create a folder that will hold your&lt;br&gt;
terraform configs and create a &lt;code&gt;main.tf&lt;/code&gt; file that will hold the terraform declarations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;kube-terra
&lt;span class="nb"&gt;cd &lt;/span&gt;kube-terra
&lt;span class="nb"&gt;touch &lt;/span&gt;main.tf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Setup the DigitalOcean Kubernetes cluster
&lt;/h3&gt;

&lt;p&gt;Terraform works in a declaritive manner, which means that we state what we want&lt;br&gt;
the world to be like and terraform figures out how to get to that state. In&lt;br&gt;
terraform you describe an item you want to exist as a resource. A Kubernetes&lt;br&gt;
cluster is a form of a resource that is provided by one of the many providers&lt;br&gt;
that exist in terraform. So let's create a DigitalOcean kubernetes cluster:&lt;/p&gt;

&lt;p&gt;Creating a resource is always in the format: &lt;code&gt;resource [kind] [reference] {}&lt;/code&gt;&lt;br&gt;
We can use the reference we provide later on to refer back to this resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"digitalocean_tag"&lt;/span&gt; &lt;span class="s2"&gt;"kubernetes-cl01"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"kubernetes-cl01"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"digitalocean_kubernetes_versions"&lt;/span&gt; &lt;span class="s2"&gt;"versions"&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"digitalocean_kubernetes_cluster"&lt;/span&gt; &lt;span class="s2"&gt;"cl01"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"cl01"&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ams3"&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;digitalocean_kubernetes_versions&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;versions&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;latest_version&lt;/span&gt;

  &lt;span class="nx"&gt;node_pool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt;
    &lt;span class="nx"&gt;size&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"s-1vcpu-2gb"&lt;/span&gt;
    &lt;span class="nx"&gt;node_count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="nx"&gt;tags&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"${digitalocean_tag.kubernetes-cl01.id}"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now you can get your cluster setup by issuing the following commands in the folder you&lt;br&gt;
created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;tarraform init
terraform apply
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will take about 5 minutes and then you'll have a fresh kubernetes cluster&lt;br&gt;
up and running. Now that was easy!&lt;/p&gt;
&lt;h3&gt;
  
  
  Install Traefik
&lt;/h3&gt;

&lt;p&gt;Kubernetes needs an ingress controller to route traffic from the outside world&lt;br&gt;
to the services that are running inside the cluster as these services are not&lt;br&gt;
exposed to the outside world, which is a good thing! Nginx is often used, but I went with Traefik, a very nice alternative that has a lot of nice functinality out of the box, like automatic ssl certificates using Let's Encrypt&lt;br&gt;
and auto discovery of services running in the cluster. This setup also takes advantage of a DigitalOcean load balancer, which will automatically be created by the serviceType set below to &lt;code&gt;LoadBalancer&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Create a file called &lt;code&gt;traefik-values.yml&lt;/code&gt; in the root of your kubernetes config directory and add the following content. If you manage dns settings with DigitalOcean, you can comment out those parts as well. This will provide automatic ssl certificate generation. Very cool! If not you can just leave it like this. It'll work, but without ssl enabled. A benefit of using the dns-challenge as opposed to the more standard acme-challenge is that you can also get a valid certificate if your production system is behind a firewall.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;
&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik&lt;/span&gt;
&lt;span class="na"&gt;dashboard&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;domain.example.com&lt;/span&gt;                     &lt;span class="c1"&gt;# put a (sub)domain here where you want to access the traefik dashboard&lt;/span&gt;
&lt;span class="na"&gt;serviceType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LoadBalancer&lt;/span&gt;
&lt;span class="na"&gt;rbac&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;span class="c1"&gt;# ssl:&lt;/span&gt;
&lt;span class="c1"&gt;#   enabled: true                                # Enables SSL&lt;/span&gt;
&lt;span class="c1"&gt;#   enforced: true                               # Redirects HTTP to HTTPS&lt;/span&gt;
&lt;span class="c1"&gt;# acme:&lt;/span&gt;
&lt;span class="c1"&gt;#   enabled: true                                # Enables Let's Encrypt certificates&lt;/span&gt;
&lt;span class="c1"&gt;#   staging: false                               # Use Lets Encrypt staging area for this example. For production purposes set this to false&lt;/span&gt;
&lt;span class="c1"&gt;#   email: info@drumusician.com                  # Email address that Let's Encrypt uses to notify about certificate expiry etc.&lt;/span&gt;
&lt;span class="c1"&gt;#   challengeType: "dns-01"&lt;/span&gt;
&lt;span class="c1"&gt;#   dnsProvider:&lt;/span&gt;
&lt;span class="c1"&gt;#     name:  digitalocean                        # This is why you need your domain to be under Digital Ocean control&lt;/span&gt;
&lt;span class="c1"&gt;#     digitalocean:&lt;/span&gt;
&lt;span class="c1"&gt;#       DO_AUTH_TOKEN: $DIGITALOCEAN_TOKEN&lt;/span&gt;
&lt;span class="c1"&gt;#   domains:&lt;/span&gt;
&lt;span class="c1"&gt;#     enabled: true&lt;/span&gt;
&lt;span class="c1"&gt;#     domainsList:&lt;/span&gt;
&lt;span class="c1"&gt;#       - main: domain.example.com               # domain that belongs to this certificate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we can go ahead and install traefik in our cluster:&lt;/p&gt;

&lt;p&gt;Install helm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;kubernetes-helm
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Pull in our fresh cluster configuration locally (otherwise helm will&lt;br&gt;
fail):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;doctl kubernetes cluster kubeconfig save cl01
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And install traefik:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# get the helm/stable charts&lt;/span&gt;
helm repo add stable https://kubernetes-charts.storage.googleapis.com/
&lt;span class="c"&gt;# install Traefik&lt;/span&gt;
helm &lt;span class="nb"&gt;install &lt;/span&gt;traefik &lt;span class="nt"&gt;--values&lt;/span&gt; traefik-values.yml stable/traefik
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After Traefik is installed we have enough running to be able to deploy our application&lt;br&gt;
to the cluster. We are going to use gitlab-ci to deploy so we'll need to&lt;br&gt;
install a gitlab-runner in our cluster first so we can use that in our project.&lt;/p&gt;
&lt;h3&gt;
  
  
  Creating a kubernetes secret to access our registry
&lt;/h3&gt;

&lt;p&gt;If we install the Gitlab runner in the next step, we'll need a kubernetes secret&lt;br&gt;
to be able to pull from our private registry in gitlab.&lt;br&gt;
Go to &lt;a href="https://gitlab.com/profile/personal_access_tokens"&gt;https://gitlab.com/profile/personal_access_tokens&lt;/a&gt; and create an access&lt;br&gt;
token to access the gitlab registry. Scopes: api, read_registry.&lt;/p&gt;

&lt;p&gt;Then create a file at &lt;code&gt;~/.docker/docker-registry.json&lt;/code&gt; with the credentials for&lt;br&gt;
the access token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"registry.gitlab.com"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;access-token-name&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;token&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;email&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;gitlab&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;account&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And then in our main.tf we can create the kubernetes secret.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"kubernetes_secret"&lt;/span&gt; &lt;span class="s2"&gt;"docker_pull_secret"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"gitlab.com"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;".dockercfg"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"~/.docker/docker-registry.json"&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"kubernetes.io/dockercfg"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With that in place, we'll have to initialize the new provider and then we can add the secret:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform init
terraform apply
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Install Gitlab Runner
&lt;/h3&gt;

&lt;p&gt;To be able to deploy into our cluster from Gitlab CI we will need our runner to run inside of our cluster and have enough access rights to actually spin-up pods and services in our cluster. So we'll install that using helm as well. Gitlab has an install script from their interface, but that doesn't provide you with customisation options that we'll need for our use case.&lt;/p&gt;

&lt;p&gt;Create the following file in your kubernetes dir: &lt;code&gt;gitlab-runner-values.yml&lt;/code&gt; and&lt;br&gt;
add this content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;gitlabUrl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://gitlab.com/&lt;/span&gt;
&lt;span class="na"&gt;runnerRegistrationToken&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;# copy this from your gitlab project settings: &lt;/span&gt;
&lt;span class="na"&gt;rbac&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;clusterWideAccess&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;runners&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;privileged&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;imagePullSecrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gitlab.com"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now let's install the runner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;helm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--namespace&lt;/span&gt; default gitlab-runner  &lt;span class="nt"&gt;-f&lt;/span&gt; gitlab-runner-values.yml gitlab/gitlab-runner
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Connect a gitlab project to the cluster
&lt;/h3&gt;

&lt;p&gt;This is something you can do through the Gitlab interface, but I like automating&lt;br&gt;
it here as well. We need to provide the Gitlab project in terraform like this.&lt;br&gt;
If you don't have a project yet, you should create it on &lt;code&gt;gitlab.com&lt;/code&gt; and add the&lt;br&gt;
ID here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data "gitlab_project" "project-x" {
  id = [your-project-id]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then we can use this reference to create the settings in our project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "gitlab_project_cluster" "gitlab-kubernetes" {
  project                       = data.gitlab_project.project-x.id
  name                          = "my-awesome-cluster"
  domain                        = "[mydomain.com]"
  enabled                       = true
  kubernetes_api_url            = digitalocean_kubernetes_cluster.cl01.endpoint
  kubernetes_token              = digitalocean_kubernetes_cluster.cl01.kube_config[0].token
  kubernetes_ca_cert            = base64decode(digitalocean_kubernetes_cluster.cl01.kube_config[0].cluster_ca_certificate)
  kubernetes_namespace          = ""
  kubernetes_authorization_type = "rbac"
  environment_scope             = "*"
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And lastly we are not going the skip the database, as that is too easy to do.&lt;br&gt;
You could potentially setup a database persistent volume in kubernetes and have&lt;br&gt;
db_pods spinup, but I think that it is much easier to have the&lt;br&gt;
database as a separate service outside of kubernetes. The DigitalOcean managed&lt;br&gt;
database service is a great option. You pay a little extra, but it takes care of&lt;br&gt;
backups etc. It is just one of those things that you don't want to worry about,&lt;br&gt;
right?&lt;/p&gt;

&lt;p&gt;Of course, we can easily add the creation of a database cluster to our terraform&lt;br&gt;
setup. Note that I am using only 1 node for the cluster in this example. For actual failsafe production usage, you probably want to have at least 2 nodes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"digitalocean_database_cluster"&lt;/span&gt; &lt;span class="s2"&gt;"postgres-db"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"postgres-db-cluster"&lt;/span&gt;
  &lt;span class="nx"&gt;engine&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"pg"&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"11"&lt;/span&gt;
  &lt;span class="nx"&gt;size&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"db-s-1vcpu-1gb"&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ams3"&lt;/span&gt;
  &lt;span class="nx"&gt;node_count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And while we are at it, let's create the database and firewall settings as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"digitalocean_database_db"&lt;/span&gt; &lt;span class="s2"&gt;"test-prod"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cluster_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;digitalocean_database_cluster&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postgres&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"test_prod"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"digitalocean_database_firewall"&lt;/span&gt; &lt;span class="s2"&gt;"db-fw"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cluster_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;digitalocean_database_cluster&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postgres&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="nx"&gt;rule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"k8s"&lt;/span&gt;
    &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;digitalocean_kubernetes_cluster&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cl01&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This concludes our cluster setup. To get our app deployed we'll have to add some&lt;br&gt;
setup to our project. We'll need to dockerize our project, add kubectl deploy&lt;br&gt;
config files and add a gitlab-ci.yml that will trigger the gitlab-ci pipeline.&lt;/p&gt;
&lt;h2&gt;
  
  
  A project to deploy
&lt;/h2&gt;

&lt;p&gt;Now we have our kubernetes cluster up-and-running it is time to see how we would&lt;br&gt;
deploy our app to the cluster.&lt;/p&gt;

&lt;p&gt;I have a git tag prepared for you to use from my real_world_phoenix project. You&lt;br&gt;
can clone that checkout to use to deploy to the cluster. It has the necessary&lt;br&gt;
files to trigger deployment which I'll explain next.&lt;/p&gt;

&lt;p&gt;Go ahead and clone the project from the tag I created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://gitlab.com/drumusician/real_world_phoenix.git --branch kubernetes-deploy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Gitlab pipeline
&lt;/h3&gt;

&lt;p&gt;To get our app deployed to kubernetes we'll need to package it up in a container&lt;br&gt;
and we'll use Gitlab CI system for all of this. That means we'll&lt;br&gt;
prepare our app and package it using a 2-step dockerfile. The advantage of the&lt;br&gt;
2-step dockerfile method is that we can use a larger docker image to package our&lt;br&gt;
app that has all the packages for building and packaging our release and use a much slimmer container to just run our release. The release will be self-contained, so there is not a lot needed for it to run.&lt;/p&gt;

&lt;p&gt;The steps we'll need in our CI file are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;init             # some compiling and pushing of artifacts&lt;/li&gt;
&lt;li&gt;build and push   # build our container and push to our registry&lt;/li&gt;
&lt;li&gt;deploy           # deploy to our kubernetes cluster&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For pushing we'll use the gitlab container registry that is available in every&lt;br&gt;
gitlab project. The deployment will be a collection of kubernetes yaml files&lt;br&gt;
that we can version control in our project. &lt;/p&gt;
&lt;h3&gt;
  
  
  Dockerfile
&lt;/h3&gt;


&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ---- Build Stage ----&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; elixir:alpine AS app_builder&lt;/span&gt;

&lt;span class="c"&gt;# Set environment variables for building the application&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; MIX_ENV=prod \&lt;/span&gt;
    TEST=1 \
    LANG=C.UTF-8

&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--update&lt;/span&gt; git nodejs npm &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/cache/apk/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="c"&gt;# Install hex and rebar&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;mix local.hex &lt;span class="nt"&gt;--force&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    mix local.rebar &lt;span class="nt"&gt;--force&lt;/span&gt;

&lt;span class="c"&gt;# Create the application build directory&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; /app
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Copy over all the necessary application files and directories&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; config ./config&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; lib ./lib&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; priv ./priv&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; assets ./assets&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; mix.exs .&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; mix.lock .&lt;/span&gt;

&lt;span class="c"&gt;# Fetch the application dependencies and build the application&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;mix deps.get
&lt;span class="k"&gt;RUN &lt;/span&gt;mix deps.compile
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run deploy &lt;span class="nt"&gt;--prefix&lt;/span&gt; ./assets
&lt;span class="k"&gt;RUN &lt;/span&gt;mix phx.digest
&lt;span class="k"&gt;RUN &lt;/span&gt;mix release

&lt;span class="c"&gt;# ---- Application Stage ----&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; alpine AS app&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; LANG=C.UTF-8&lt;/span&gt;

&lt;span class="c"&gt;# Install openssl&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--update&lt;/span&gt; openssl ncurses-libs postgresql-client &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/cache/apk/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="c"&gt;# Copy over the build artifact from the previous step and create a non root user&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;adduser &lt;span class="nt"&gt;-D&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; /home/app app
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /home/app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=app_builder /app/_build .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; app: ./prod
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; entrypoint.sh .&lt;/span&gt;

&lt;span class="c"&gt;# Run the Phoenix app&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["./entrypoint.sh"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Entrypoint
&lt;/h3&gt;

&lt;p&gt;The entrypoint.sh is basically just a bash script that verifies the db is up&lt;br&gt;
and running before starting and running any pending migrations before startup as&lt;br&gt;
well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="c"&gt;# Docker entrypoint script.&lt;/span&gt;

&lt;span class="c"&gt;# Wait until Postgres is ready&lt;/span&gt;
&lt;span class="c"&gt;# while ! pg_isready -q -h $DB_HOST -p 5432 -U $DB_USER&lt;/span&gt;
&lt;span class="c"&gt;# do&lt;/span&gt;
&lt;span class="c"&gt;#   echo "$(date) - waiting for database to start"&lt;/span&gt;
&lt;span class="c"&gt;#   sleep 2&lt;/span&gt;
&lt;span class="c"&gt;# done&lt;/span&gt;

./prod/rel/real_world_phoenix/bin/real_world_phoenix &lt;span class="nb"&gt;eval &lt;/span&gt;RealWorldPhoenix.Release.migrate

./prod/rel/real_world_phoenix/bin/real_world_phoenix start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Gitlab CI Yaml file
&lt;/h3&gt;

&lt;p&gt;To trigger the gitlab-ci pipeline we'll need to add a &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; file in&lt;br&gt;
the root of our project.&lt;/p&gt;

&lt;p&gt;This is the content needed to initialize, build, push and deploy using the CI&lt;br&gt;
pipeline. We start off creating a few variables we'll need and then define the&lt;br&gt;
stages. I have added some comments in the yaml file below that should explain&lt;br&gt;
the steps in detail.&lt;/p&gt;

&lt;p&gt;Note the use of &lt;a href="https://github.com/GoogleContainerTools/kaniko"&gt;Kaniko&lt;/a&gt; to build the container and push it to our registry. Kaniko doesn't depend on a Docker daemon and executes each command within a Dockerfile completely in userspace. This enables building container images in environments that can't easily or securely run a Docker daemon, such as a our Kubernetes cluster. Very nice!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;REGISTRY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;registry.gitlab.com&lt;/span&gt;
  &lt;span class="na"&gt;CONTAINER_RELEASE_IMAGE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$REGISTRY/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME:$CI_COMMIT_SHORT_SHA&lt;/span&gt;

&lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;init&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;deploy&lt;/span&gt;

&lt;span class="c1"&gt;# the dot syntax makes this a hidden step that we can include in other places further down.&lt;/span&gt;
&lt;span class="s"&gt;.elixir_default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;elixir_default&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;elixir:1.9&lt;/span&gt;
  &lt;span class="na"&gt;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mix local.hex --force&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mix local.rebar --force&lt;/span&gt;

&lt;span class="s"&gt;.javascript_default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;javascript_default&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node:alpine&lt;/span&gt;
  &lt;span class="na"&gt;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cd assets&lt;/span&gt;

&lt;span class="c1"&gt;# Compile our elixir artifacts&lt;/span&gt;
&lt;span class="na"&gt;elixir_compile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*elixir_default&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;init&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mix deps.get --only test&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mix compile&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mix compile --warnings-as-errors&lt;/span&gt;
  &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mix.lock&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;_build&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;deps&lt;/span&gt;

&lt;span class="c1"&gt;# compile javascript artifacts&lt;/span&gt;
&lt;span class="na"&gt;javascript_deps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*javascript_default&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;init&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm install --progress=false&lt;/span&gt;
  &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;assets/node_modules&lt;/span&gt;

&lt;span class="c1"&gt;# build our container image&lt;/span&gt;
&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcr.io/kaniko-project/executor:debug&lt;/span&gt;
    &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" &amp;gt; /kaniko/.docker/config.json&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CONTAINER_RELEASE_IMAGE&lt;/span&gt;

&lt;span class="c1"&gt;# and deploy our kubernetes cluster&lt;/span&gt;
&lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lwolf/kubectl_deployer:latest&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cat deploy.yml | envsubst | kubectl apply -f -&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cat service.yml | envsubst | kubectl apply -f -&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cat ingress.yml | envsubst | kubectl apply -f -&lt;/span&gt;
  &lt;span class="na"&gt;only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;refs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Yaml deployment files: service, ingress and deploy
&lt;/h3&gt;

&lt;p&gt;The last thing we need is the kubernetes yaml files that are used in the ci&lt;br&gt;
pipeline:&lt;/p&gt;
&lt;h4&gt;
  
  
  service.yml
&lt;/h4&gt;

&lt;p&gt;The service pod running our application. This is basically our app running in a docker container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;real-world-phoenix-service&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8001&lt;/span&gt;
    &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;real_world_phoenix&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  ingress.yml
&lt;/h4&gt;

&lt;p&gt;The mapping of our domain name to our service&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;extensions/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;real-world-phoenix-ingress&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$DOMAIN&lt;/span&gt;
    &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/"&lt;/span&gt;
        &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;real-world-phoenix-service&lt;/span&gt;
          &lt;span class="na"&gt;servicePort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  deploy.yml
&lt;/h4&gt;

&lt;p&gt;Our deployment configuration. This provides Kubernetes the info about which container image to use and how many replicas of our app we want to have running.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;real-world-phoenix&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;real_world_phoenix&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;revisionHistoryLimit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;real_world_phoenix&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;real_world_phoenix&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;imagePullSecrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gitlab.com"&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;real-world-phoenix&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;registry.gitlab.com/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME:$CI_COMMIT_SHORT_SHA&lt;/span&gt;
        &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Always&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;real-world-phoenix-deployment&lt;/span&gt;
        &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8001&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SECRET_KEY_BASE&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$SECRET_KEY_BASE&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DB_USER&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$DB_USER&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DB_PASSWORD&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$DB_PASS&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DB_NAME&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test_prod"&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DB_HOST&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$DB_CLUSTER&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DB_PORT&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;25060"&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;APP_PORT&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8001"&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;APP_HOSTNAME&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$DOMAIN&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The above yaml file has references to a number of environment variables. The nice thing is that we can add all of these secrets inside of our Gitlab project and the deployment will use these values. In your project go to &lt;code&gt;Settings -&amp;gt; CI/CD -&amp;gt; Variables&lt;/code&gt;.&lt;br&gt;
Now if we have all of this in our project all we have to do is commit and push it to our gitlab repo. Then Gitlab CI will do it's magic and our application will be up and running after the pipeline jobs succeed. Nice!&lt;/p&gt;

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

&lt;p&gt;While it is not necessarily the easiest setup, I really like the fact that once I have this setup I can fairly easily put some configuration in any of my gitlab projects and CI will take care of the rest, even the ssl certificate is handled for me so I don't have to think about renewing them and all that stuff.&lt;/p&gt;

&lt;p&gt;The reduction of manual work by using terraform is also a really nice benefit here. Terraform is a powerful tool, so use with care! If there is any interest in exploring terraform more, please leave a comment and I'll plan in some more in-depth post exploring the world of terraform.&lt;/p&gt;

&lt;p&gt;I hope this post was easy to follow and I hope that for those who followed along everything worked as expected? If not, do let me know, because I want to make sure everything here works as expected.&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>phoenix</category>
      <category>k8s</category>
    </item>
    <item>
      <title>When Elixir's performance becomes Rust-y</title>
      <dc:creator>Tjaco Oostdijk</dc:creator>
      <pubDate>Tue, 14 Jan 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/kabisasoftware/when-elixir-s-performance-becomes-rust-y-5709</link>
      <guid>https://dev.to/kabisasoftware/when-elixir-s-performance-becomes-rust-y-5709</guid>
      <description>&lt;p&gt;Elixir is a good language to develop fault tolerant and predictable, performant systems. However for heavy computational work it's less suitable. How can we keep using Elixir for the things it's good at, but achieve better performance for computational heavy work?&lt;/p&gt;

&lt;p&gt;The post assumes basic Elixir knowledge. Rust knowledge is not required.&lt;/p&gt;

&lt;h2&gt;
  
  
  The use case
&lt;/h2&gt;

&lt;p&gt;In this use case, we want to calculate the prime numbers in the range of 1 up to 1 million. I'd like to show off something a bit more computational intensive than a simple addition of numbers, and also show that Elixir data types map well into Rust.&lt;/p&gt;

&lt;p&gt;The code passes a list of numbers instead of just an integer denoting the maximum, to show off that data structures such as lists also translate fine between Elixir and Rust. There are a lot more efficient algorithms available for determining prime numbers, but this approach is fine to show the performance difference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting off with pure Elixir
&lt;/h2&gt;

&lt;p&gt;So let's get coding! We'll start off with the Elixir implementation after making a new project. In this example the project is called &lt;code&gt;rust_nif&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;RustNif&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;ElixirPrimes&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# http://erlang.org/doc/efficiency_guide/listHandling.html&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;prime_numbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;prime_numbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;prime_numbers&lt;/span&gt;&lt;span class="p"&gt;([],&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;prime_numbers&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;new_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;add_if_prime_number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;prime_numbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;add_if_prime_number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;add_if_prime_number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;add_if_prime_number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;range&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;any?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;range&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;rem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="no"&gt;false&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc_now&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;RustNif&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;ElixirPrimes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prime_numbers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;execution_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc_now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Elixir task finished after &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;execution_time&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; seconds"&lt;/span&gt;
&lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We prepend each number to the list, and reverse the list in the final result. See &lt;a href="http://erlang.org/doc/efficiency_guide/listHandling.html"&gt;this section&lt;/a&gt; as to why we do this.&lt;br&gt;
Running this, the results on my machine were:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;43&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;53&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;61&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;67&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;71&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;73&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;79&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;83&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;89&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;97&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;103&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;107&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;109&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;113&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;127&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;131&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;137&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;139&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;149&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;151&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;157&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;163&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;167&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;173&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;179&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;181&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;191&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;193&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;197&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;199&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;211&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;223&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;span class="no"&gt;Elixir&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="n"&gt;finished&lt;/span&gt; &lt;span class="k"&gt;after&lt;/span&gt; &lt;span class="mi"&gt;33&lt;/span&gt; &lt;span class="n"&gt;seconds&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That takes quite some time. Surely we can do better.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what are our alternatives?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Ports &amp;amp; Nifs
&lt;/h3&gt;

&lt;p&gt;We could resort to ports or NIFs, if you would want to improve this by writing it in another language.&lt;br&gt;
There's a good summary of ports and nifs here: &lt;a href="https://spin.atomicobject.com/2015/03/16/Elixir-native-interoperability-ports-vs-nifs/"&gt;https://spin.atomicobject.com/2015/03/16/Elixir-native-interoperability-ports-vs-nifs/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In short:&lt;/p&gt;

&lt;p&gt;Ports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Safety&lt;/li&gt;
&lt;li&gt;Error trapping&lt;/li&gt;
&lt;li&gt;Flexible communication&lt;/li&gt;
&lt;li&gt;No external Erlang/Elixir specific libraries required&lt;/li&gt;
&lt;li&gt;Communication via STDIN and STDOUT&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;NIFS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fast and simple implementationn&lt;/li&gt;
&lt;li&gt;No context switch required&lt;/li&gt;
&lt;li&gt;Simple debugging&lt;/li&gt;
&lt;li&gt;Not very safe (a bug in your NIF could crash the whole VM)&lt;/li&gt;
&lt;li&gt;NIF-specific C libraries&lt;/li&gt;
&lt;li&gt;Native implementation can leak into Elixir code&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  (Web) service
&lt;/h3&gt;

&lt;p&gt;We could define a separate web service, exposing an API that could handle this, in a language better suited for computation heavy tasks. We'd need to add a new service to our stack though, and also account for potential network failures and sending potentially large payloads.&lt;/p&gt;
&lt;h3&gt;
  
  
  Background job
&lt;/h3&gt;

&lt;p&gt;Depending on the case, it would be acceptable to move this computation into a background task (we don't need to return the immediate result to the end user). But in our scenario, this isn't the case&lt;/p&gt;
&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;A NIF looks nice at a glance, but we'd need to eliminate the risk of the NIF being able to crash the beam VM. But what if I told you that we can do that?&lt;/p&gt;
&lt;h2&gt;
  
  
  Introducing Rust
&lt;/h2&gt;

&lt;p&gt;This is where Rust comes in. Rust is a low level language, like C, developed by Mozilla. It's designed to provide better memory safety than C does, while also maintaining solid performance.&lt;/p&gt;

&lt;p&gt;There's a library which allows creating Elixir/Erlang NIF's which make use of Rust, called &lt;a href="https://github.com/rusterlium/rustler"&gt;rustler&lt;/a&gt;. One of its main features is that its safety should prevent the Beam from crashing if something unexpected happens. In the meanwhile, we get to leverage Rust's performance in our Elixir apps when we need it!&lt;/p&gt;
&lt;h3&gt;
  
  
  Writing the Rust code
&lt;/h3&gt;

&lt;p&gt;Disclaimer: I don't have much prior knowledge with Rust, I'm sure this code can be improved. We have to start somewhere!&lt;/p&gt;

&lt;p&gt;We'll go through the code piece by piece, the total solution will be posted at the end of the article.&lt;/p&gt;

&lt;p&gt;The first thing we need to do is add rustler to the project, so let's do that!&lt;br&gt;
Let's add &lt;code&gt;{:rustler, "~&amp;gt; 0.21.0"}&lt;/code&gt;, to our dependencies in &lt;code&gt;mix.exs&lt;/code&gt; and install it with &lt;code&gt;mix deps.get&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That'll allow us to run &lt;code&gt;mix rustler.new&lt;/code&gt;. Let's call our module &lt;code&gt;PrimeNumbers&lt;/code&gt; (which will call the rust crate &lt;code&gt;primenumbers&lt;/code&gt; by default, which is fine).&lt;/p&gt;

&lt;p&gt;We'll also need to add rustler as compiler in the project, and define our Rust crate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="ss"&gt;compilers:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:rustler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:phoenix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:gettext&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="no"&gt;Mix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compilers&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="ss"&gt;rustler_crates:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="ss"&gt;primenumbers:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="c1"&gt;# other project options&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Let's start writing the Rust code! there will be a rust file at &lt;code&gt;native/primenumbers/src/lib.rs&lt;/code&gt;, which looks like this currently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rustler&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Encoder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Term&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;atoms&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;rustler_atoms!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;atom&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;atom&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c"&gt;//atom __true__ = "true";&lt;/span&gt;
        &lt;span class="c"&gt;//atom __false__ = "false";&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nn"&gt;rustler&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;rustler_export_nifs!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"Elixir.PrimeNumbers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"add"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="nb"&gt;None&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Env&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Term&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Term&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;num1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nf"&gt;.decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;num2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nf"&gt;.decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nn"&gt;atoms&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;num1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;num2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The first statement imports the Encoder, Env, Error and Term types from rustler, so we can make use of them.&lt;br&gt;
The atoms section declares atoms which will map to Elixir's :ok annd :error atoms, so we can provide an interface similar to Elixir.&lt;/p&gt;

&lt;p&gt;Methods defined in the &lt;code&gt;rustler::rustler_export_nifs!&lt;/code&gt; statement are made available to Elixir, in the PrimeNumbers module. In the example, &lt;code&gt;add&lt;/code&gt; is exported and defined with an arity of 2.&lt;/p&gt;

&lt;p&gt;The add function decodes the arguments that are provided, adds them together and yields them back to Elixir as &lt;code&gt;{:ok, &amp;lt;result&amp;gt;}&lt;/code&gt;. (Although I won't dive deeper into it, rustler makes this possible by implementing the &lt;a href="http://Erlang.org/doc/apps/erts/erl_ext_dist.html"&gt;External Term Format&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;So let's modify that so we can get us some prime numbers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rustler&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Encoder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Term&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;atoms&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;rustler&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;rustler_atoms!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c"&gt;// &amp;lt;- We actually prefixed this with rustler! it's a small oversight in the template generation. see https://github.com/rusterlium/rustler/issues/260&lt;/span&gt;
        &lt;span class="n"&gt;atom&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;atom&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The first part remains the same, we'll declare the ok and error atoms for use with Elixir.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nn"&gt;rustler&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;rustler_export_nifs!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"Elixir.RustNif.PrimeNumbers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"prime_numbers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prime_numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;rustler&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;SchedulerFlags&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;DirtyCpu&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="nb"&gt;None&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We'll register a &lt;code&gt;prime_numbers&lt;/code&gt; method to rustler with an arity of 1 to be exported. This will be made available to Elixir.&lt;/p&gt;

&lt;p&gt;One important addition we've made compared to the given example is to mark the function as being a 'Dirty NIF' by sending along &lt;code&gt;SchedulerFlags::DirtyCpu&lt;/code&gt;. It's important to mark your NIF as 'Dirty' if their operation will take longer than a millisecond to complete. Not doing so could result in blocking your main operation in the Beam VM, potentially causing weird side effects.&lt;br&gt;
More information about this behavior can be found in the &lt;a href="http://erlang.org/doc/man/erl_nif.html#dirty_nifs"&gt;Erlang documentation&lt;/a&gt;. (Thank you for this addition, Redmar Kerkhoff!)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;is_prime_number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;end_of_range&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;end_of_range&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.any&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We'll define a method to determine whether a number is a prime number. It accepts the number as argument and returns a boolean.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;prime_numbers&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Env&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Term&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Term&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;is_list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nf"&gt;.is_list&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;is_list&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nn"&gt;atoms&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"No list supplied"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;i64&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nf"&gt;.decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;vec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;is_prime_number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nn"&gt;atoms&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;*&lt;/span&gt;&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can use the same function signature that the example add function used (note the &amp;lt;'a&amp;gt; are &lt;a href="https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html"&gt;lifetimes&lt;/a&gt;, not generics or similar).&lt;/p&gt;

&lt;p&gt;In this case we will verify whether the first argument is a list, and stop the function and yield &lt;code&gt;{:error, "No list suppled"}&lt;/code&gt; if it's not.&lt;/p&gt;

&lt;p&gt;Afterwards, we use rustler's &lt;code&gt;decode()&lt;/code&gt; method to convert the data type into a Vec of &lt;code&gt;i64&lt;/code&gt;. We'll make a mutable Vec (you need to specify mutability explicitely in Rust), and loop through the numbers to determine the prime number. If a number is determined to be a prime number, we push it into the Vec.&lt;/p&gt;

&lt;h3&gt;
  
  
  Glueing Elixir and Rust together
&lt;/h3&gt;

&lt;p&gt;We'll need a module in Elixir that will be the interface to the Rust code. The module name corresponds to the name known in the Rust code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# prime_numbers.ex&lt;/span&gt;
&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;RustNif&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PrimeNumbers&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Rustler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;otp_app:&lt;/span&gt; &lt;span class="ss"&gt;:rust_nif&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;crate:&lt;/span&gt; &lt;span class="ss"&gt;:primenumbers&lt;/span&gt;

  &lt;span class="c1"&gt;# Overriden when module is loaded&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;prime_numbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_nums&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="ss"&gt;:erlang&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nif_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:nif_not_loaded&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We define the OTP app we use, and the Rust crate we're loading, and that's it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison of performance
&lt;/h2&gt;

&lt;p&gt;Now that we have both the Elixir and Rust(Elixir) versions of the code, let's compare them on their execution speed!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Benchmark&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;benchmark&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;benchmark_elixir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:link&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;benchmark_rust&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:link&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="no"&gt;nil&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;benchmark_elixir&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc_now&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;RustNif&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;ElixirPrimes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prime_numbers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;execution_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc_now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;

    &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inspect&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
    &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Elixir task finished after &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;execution_time&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; seconds"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;benchmark_rust&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc_now&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;RustNif&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PrimeNumbers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prime_numbers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;execution_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc_now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;

    &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inspect&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
    &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Rust task finished after &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;execution_time&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; seconds"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Benchmark&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;benchmark&lt;/span&gt;
&lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;43&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;53&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;61&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;67&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;71&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;73&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;79&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;83&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;89&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;97&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;103&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;107&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;109&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;113&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;127&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;131&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;137&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;139&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;149&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;151&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;157&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;163&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;167&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;173&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;179&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;181&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;191&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;193&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;197&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;199&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;211&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;223&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;span class="no"&gt;Rust&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="n"&gt;finished&lt;/span&gt; &lt;span class="k"&gt;after&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="n"&gt;seconds&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;43&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;53&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;61&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;67&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;71&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;73&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;79&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;83&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;89&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;97&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;103&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;107&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;109&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;113&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;127&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;131&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;137&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;139&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;149&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;151&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;157&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;163&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;167&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;173&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;179&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;181&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;191&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;193&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;197&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;199&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;211&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;223&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;span class="no"&gt;Elixir&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="n"&gt;finished&lt;/span&gt; &lt;span class="k"&gt;after&lt;/span&gt; &lt;span class="mi"&gt;33&lt;/span&gt; &lt;span class="n"&gt;seconds&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Well that's quite a lot better!&lt;/p&gt;

&lt;h2&gt;
  
  
  Do other people do this?
&lt;/h2&gt;

&lt;p&gt;Yes! An example of a company having this setup is Discord, see &lt;a href="https://blog.discordapp.com/using-rust-to-scale-Elixir-for-11-million-concurrent-users-c6f19fc029d3"&gt;Using Rust to Scale Elixir for 11 Million Concurrent Users&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Let me preface the conclusion by stating you shouldn't just introduce Rust in your Elixir stack like this because you &lt;em&gt;think&lt;/em&gt; you'll need it.&lt;/p&gt;

&lt;p&gt;It's great to see the ease in which a low level, but safe language such as Rust, can be added to our stack to overcome one of Elixir's weaker points with relative ease.&lt;/p&gt;

&lt;p&gt;I'm sure both implementations can be further refined. The point that I want to make is that it's easy to resort to a more performant language when need does arise.&lt;/p&gt;

&lt;h2&gt;
  
  
  The full code samples
&lt;/h2&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Elixir_primes.ex&lt;/span&gt;
&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;RustNif&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;ElixirPrimes&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# http://erlang.org/doc/efficiency_guide/listHandling.html&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;prime_numbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;prime_numbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;prime_numbers&lt;/span&gt;&lt;span class="p"&gt;([],&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;prime_numbers&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;new_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;add_if_prime_number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;prime_numbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;add_if_prime_number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;add_if_prime_number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;add_if_prime_number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;range&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;any?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;range&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;rem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="no"&gt;false&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# prime_numbers.ex&lt;/span&gt;
&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;RustNif&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PrimeNumbers&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Rustler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;otp_app:&lt;/span&gt; &lt;span class="ss"&gt;:rust_nif&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;crate:&lt;/span&gt; &lt;span class="ss"&gt;:primenumbers&lt;/span&gt;

  &lt;span class="c1"&gt;# Overriden when module is loaded&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;prime_numbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_nums&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="ss"&gt;:erlang&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nif_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:nif_not_loaded&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c"&gt;// lib.rs&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rustler&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Encoder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Term&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;atoms&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;rustler&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;rustler_atoms!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;atom&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;atom&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nn"&gt;rustler&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;rustler_export_nifs!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"Elixir.RustNif.PrimeNumbers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"prime_numbers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prime_numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;rustler&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;SchedulerFlags&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;DirtyCpu&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="nb"&gt;None&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;is_prime_number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;end_of_range&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;end_of_range&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.any&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;prime_numbers&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Env&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Term&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Term&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;is_list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nf"&gt;.is_list&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;is_list&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nn"&gt;atoms&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"No list supplied"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;i64&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nf"&gt;.decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;vec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;is_prime_number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nn"&gt;atoms&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;*&lt;/span&gt;&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



</description>
      <category>elixir</category>
      <category>rusty</category>
    </item>
    <item>
      <title>When Elixir's performance becomes Rust-y</title>
      <dc:creator>Tjaco Oostdijk</dc:creator>
      <pubDate>Tue, 14 Jan 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/kabisasoftware/when-elixir-s-performance-becomes-rust-y-4fkp</link>
      <guid>https://dev.to/kabisasoftware/when-elixir-s-performance-becomes-rust-y-4fkp</guid>
      <description>&lt;p&gt;Elixir is a good language to develop fault tolerant and predictable, performant systems. However for heavy computational work it’s less suitable. How can we keep using Elixir for the things it’s good at, but achieve better performance for computational heavy work?&lt;/p&gt;

&lt;p&gt;The post assumes basic Elixir knowledge. Rust knowledge is not required.&lt;/p&gt;

&lt;h2&gt;
  
  
  The use case
&lt;/h2&gt;

&lt;p&gt;In this use case, we want to calculate the prime numbers in the range of 1 up to 1 million. I’d like to show off something a bit more computational intensive than a simple addition of numbers, and also show that Elixir data types map well into Rust.&lt;/p&gt;

&lt;p&gt;The code passes a list of numbers instead of just an integer denoting the maximum, to show off that data structures such as lists also translate fine between Elixir and Rust. There are a lot more efficient algorithms available for determining prime numbers, but this approach is fine to show the performance difference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting off with pure Elixir
&lt;/h2&gt;

&lt;p&gt;So let’s get coding! We’ll start off with the Elixir implementation after making a new project. In this example the project is called &lt;code&gt;rust_nif&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

defmodule RustNif.ElixirPrimes do
  # http://erlang.org/doc/efficiency_guide/listHandling.html
  def prime_numbers(numbers) do
    prime_numbers(numbers, [])
  end

  defp prime_numbers([], result) do
    {:ok, result |&amp;gt; Enum.reverse() }
  end

  defp prime_numbers([number | rest], result) do
    new_result = result
    |&amp;gt; add_if_prime_number(number)

    prime_numbers(rest, new_result)
  end

  defp add_if_prime_number(numbers, 1), do: numbers

  defp add_if_prime_number(numbers, 2) do
    [2 | numbers]
  end

  defp add_if_prime_number(numbers, n) do
    range = 2..(n - 1)
    case Enum.any?(range, fn x -&amp;gt; rem(n, x) == 0 end) do
      false -&amp;gt; [n | numbers]
      _ -&amp;gt; numbers
    end
  end
end

time = Time.utc_now
result = Enum.into(1..100000, []) |&amp;gt; RustNif.ElixirPrimes.prime_numbers()
execution_time = Time.diff Time.utc_now, time
IO.puts "Elixir task finished after #{execution_time} seconds"
IO.inspect(result)

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

&lt;/div&gt;



&lt;p&gt;We prepend each number to the list, and reverse the list in the final result. See &lt;a href="http://erlang.org/doc/efficiency_guide/listHandling.html"&gt;this section&lt;/a&gt; as to why we do this.Running this, the results on my machine were:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5

{:ok,
 [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
  73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151,
  157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, ...]}
Elixir task finished after 33 seconds

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

&lt;/div&gt;



&lt;p&gt;That takes quite some time. Surely we can do better.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what are our alternatives?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Ports &amp;amp; Nifs
&lt;/h3&gt;

&lt;p&gt;We could resort to ports or NIFs, if you would want to improve this by writing it in another language.There’s a good summary of ports and nifs here: &lt;a href="https://spin.atomicobject.com/2015/03/16/Elixir-native-interoperability-ports-vs-nifs/"&gt;https://spin.atomicobject.com/2015/03/16/Elixir-native-interoperability-ports-vs-nifs/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In short:&lt;/p&gt;

&lt;p&gt;Ports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Safety&lt;/li&gt;
&lt;li&gt;Error trapping&lt;/li&gt;
&lt;li&gt;Flexible communication&lt;/li&gt;
&lt;li&gt;No external Erlang/Elixir specific libraries required&lt;/li&gt;
&lt;li&gt;Communication via STDIN and STDOUT&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;NIFS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fast and simple implementationn&lt;/li&gt;
&lt;li&gt;No context switch required&lt;/li&gt;
&lt;li&gt;Simple debugging&lt;/li&gt;
&lt;li&gt;Not very safe (a bug in your NIF could crash the whole VM)&lt;/li&gt;
&lt;li&gt;NIF-specific C libraries&lt;/li&gt;
&lt;li&gt;Native implementation can leak into Elixir code&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  (Web) service
&lt;/h3&gt;

&lt;p&gt;We could define a separate web service, exposing an API that could handle this, in a language better suited for computation heavy tasks. We’d need to add a new service to our stack though, and also account for potential network failures and sending potentially large payloads.&lt;/p&gt;

&lt;h3&gt;
  
  
  Background job
&lt;/h3&gt;

&lt;p&gt;Depending on the case, it would be acceptable to move this computation into a background task (we don’t need to return the immediate result to the end user). But in our scenario, this isn’t the case&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;A NIF looks nice at a glance, but we’d need to eliminate the risk of the NIF being able to crash the beam VM. But what if I told you that we can do that?&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing Rust
&lt;/h2&gt;

&lt;p&gt;This is where Rust comes in. Rust is a low level language, like C, developed by Mozilla. It’s designed to provide better memory safety than C does, while also maintaining solid performance.&lt;/p&gt;

&lt;p&gt;There’s a library which allows creating Elixir/Erlang NIF’s which make use of Rust, called &lt;a href="https://github.com/rusterlium/rustler"&gt;rustler&lt;/a&gt;. One of its main features is that its safety should prevent the Beam from crashing if something unexpected happens. In the meanwhile, we get to leverage Rust’s performance in our Elixir apps when we need it!&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing the Rust code
&lt;/h3&gt;

&lt;p&gt;Disclaimer: I don’t have much prior knowledge with Rust, I’m sure this code can be improved. We have to start somewhere!&lt;/p&gt;

&lt;p&gt;We’ll go through the code piece by piece, the total solution will be posted at the end of the article.&lt;/p&gt;

&lt;p&gt;The first thing we need to do is add rustler to the project, so let’s do that!Let’s add &lt;code&gt;{:rustler, "~&amp;gt; 0.21.0"}&lt;/code&gt;, to our dependencies in &lt;code&gt;mix.exs&lt;/code&gt; and install it with &lt;code&gt;mix deps.get&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That’ll allow us to run &lt;code&gt;mix rustler.new&lt;/code&gt;. Let’s call our module &lt;code&gt;PrimeNumbers&lt;/code&gt; (which will call the rust crate &lt;code&gt;primenumbers&lt;/code&gt; by default, which is fine).&lt;/p&gt;

&lt;p&gt;We’ll also need to add rustler as compiler in the project, and define our Rust crate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8

def project
  [
    compilers: [:rustler, :phoenix, :gettext] ++ Mix.compilers(),
    rustler_crates: [
      primenumbers: []
    ],
    ... # other project options
  ]

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

&lt;/div&gt;



&lt;p&gt;Let’s start writing the Rust code! there will be a rust file at &lt;code&gt;native/primenumbers/src/lib.rs&lt;/code&gt;, which looks like this currently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

use rustler::{Encoder, Env, Error, Term};

mod atoms {
    rustler_atoms! {
        atom ok;
        atom error;
        //atom __true__ = "true";
        //atom __false__ = "false";
    }
}

rustler::rustler_export_nifs! {
    "Elixir.PrimeNumbers",
    [
        ("add", 2, add)
    ],
    None
}

fn add&amp;lt;'a&amp;gt;(env: Env&amp;lt;'a&amp;gt;, args: &amp;amp;[Term&amp;lt;'a&amp;gt;]) -&amp;gt; Result&amp;lt;Term&amp;lt;'a&amp;gt;, Error&amp;gt; {
    let num1: i64 = args[0].decode()?;
    let num2: i64 = args[1].decode()?;

    Ok((atoms::ok(), num1 + num2).encode(env))
}

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

&lt;/div&gt;



&lt;p&gt;The first statement imports the Encoder, Env, Error and Term types from rustler, so we can make use of them.The atoms section declares atoms which will map to Elixir’s :ok annd :error atoms, so we can provide an interface similar to Elixir.&lt;/p&gt;

&lt;p&gt;Methods defined in the &lt;code&gt;rustler::rustler_export_nifs!&lt;/code&gt; statement are made available to Elixir, in the PrimeNumbers module. In the example, &lt;code&gt;add&lt;/code&gt; is exported and defined with an arity of 2.&lt;/p&gt;

&lt;p&gt;The add function decodes the arguments that are provided, adds them together and yields them back to Elixir as &lt;code&gt;{:ok, &amp;lt;result&amp;gt;}&lt;/code&gt;. (Although I won’t dive deeper into it, rustler makes this possible by implementing the &lt;a href="http://Erlang.org/doc/apps/erts/erl_ext_dist.html"&gt;External Term Format&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;So let’s modify that so we can get us some prime numbers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8

use rustler::{Encoder, Env, Error, Term};

mod atoms {
    rustler::rustler_atoms! { // &amp;lt;- We actually prefixed this with rustler! it's a small oversight in the template generation. see https://github.com/rusterlium/rustler/issues/260
        atom ok;
        atom error;
    }
}

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

&lt;/div&gt;



&lt;p&gt;The first part remains the same, we’ll declare the ok and error atoms for use with Elixir.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7

rustler::rustler_export_nifs! {
    "Elixir.RustNif.PrimeNumbers",
    [
        ("prime_numbers", 1, prime_numbers, rustler::SchedulerFlags::DirtyCpu)
    ],
    None
}

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

&lt;/div&gt;



&lt;p&gt;We’ll register a &lt;code&gt;prime_numbers&lt;/code&gt; method to rustler with an arity of 1 to be exported. This will be made available to Elixir.&lt;/p&gt;

&lt;p&gt;One important addition we’ve made compared to the given example is to mark the function as being a ‘Dirty NIF’ by sending along &lt;code&gt;SchedulerFlags::DirtyCpu&lt;/code&gt;. It’s important to mark your NIF as ‘Dirty’ if their operation will take longer than a millisecond to complete. Not doing so could result in blocking your main operation in the Beam VM, potentially causing weird side effects.More information about this behavior can be found in the &lt;a href="http://erlang.org/doc/man/erl_nif.html#dirty_nifs"&gt;Erlang documentation&lt;/a&gt;. (Thank you for this addition, Redmar Kerkhoff!)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9

fn is_prime_number(x: i64) -&amp;gt; bool {
    let end_of_range = x - 1;

    if x == 1 {
        false
    } else {
        !(2..end_of_range).any(|num| x % num == 0)
    }
}

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

&lt;/div&gt;



&lt;p&gt;We’ll define a method to determine whether a number is a prime number. It accepts the number as argument and returns a boolean.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

fn prime_numbers&amp;lt;'a&amp;gt;(env: Env&amp;lt;'a&amp;gt;, args: &amp;amp;[Term&amp;lt;'a&amp;gt;]) -&amp;gt; Result&amp;lt;Term&amp;lt;'a&amp;gt;, Error&amp;gt; {
    let is_list: bool = args[0].is_list();

    if !is_list {
        Ok((atoms::error(), "No list supplied").encode(env))
    } else {
        let numbers: Vec&amp;lt;i64&amp;gt; = args[0].decode()?;
        let mut vec = Vec::new();

        for number in numbers {
            if is_prime_number(number) {
                vec.push(number)
            }
        }

        Ok((atoms::ok(), &amp;amp;*vec).encode(env))
    }
}

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

&lt;/div&gt;



&lt;p&gt;We can use the same function signature that the example add function used (note the &amp;lt;’a&amp;gt; are &lt;a href="https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html"&gt;lifetimes&lt;/a&gt;, not generics or similar).&lt;/p&gt;

&lt;p&gt;In this case we will verify whether the first argument is a list, and stop the function and yield &lt;code&gt;{:error, "No list suppled"}&lt;/code&gt; if it’s not.&lt;/p&gt;

&lt;p&gt;Afterwards, we use rustler’s &lt;code&gt;decode()&lt;/code&gt; method to convert the data type into a Vec of &lt;code&gt;i64&lt;/code&gt;. We’ll make a mutable Vec (you need to specify mutability explicitely in Rust), and loop through the numbers to determine the prime number. If a number is determined to be a prime number, we push it into the Vec.&lt;/p&gt;

&lt;h3&gt;
  
  
  Glueing Elixir and Rust together
&lt;/h3&gt;

&lt;p&gt;We’ll need a module in Elixir that will be the interface to the Rust code. The module name corresponds to the name known in the Rust code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7

# prime_numbers.ex
defmodule RustNif.PrimeNumbers do
  use Rustler, otp_app: :rust_nif, crate: :primenumbers

  # Overriden when module is loaded
  def prime_numbers(_nums), do: :erlang.nif_error(:nif_not_loaded)
end

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

&lt;/div&gt;



&lt;p&gt;We define the OTP app we use, and the Rust crate we’re loading, and that’s it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison of performance
&lt;/h2&gt;

&lt;p&gt;Now that we have both the Elixir and Rust(Elixir) versions of the code, let’s compare them on their execution speed!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

defmodule Benchmark do
  def benchmark do
    Process.spawn(fn -&amp;gt; benchmark_elixir() end, [:link])
    Process.spawn(fn -&amp;gt; benchmark_rust() end, [:link])
    nil
  end

  def benchmark_elixir do
    time = Time.utc_now
    result = Enum.into(1..100000, []) |&amp;gt; RustNif.ElixirPrimes.prime_numbers()
    execution_time = Time.diff Time.utc_now, time

    IO.inspect result
    IO.puts "Elixir task finished after #{execution_time} seconds"
  end

  def benchmark_rust do
    time = Time.utc_now
    result = Enum.into(1..100000, []) |&amp;gt; RustNif.PrimeNumbers.prime_numbers()
    execution_time = Time.diff Time.utc_now, time

    IO.inspect result
    IO.puts "Rust task finished after #{execution_time} seconds"
  end
end

iex(1)&amp;gt; Benchmark.benchmark
nil
{:ok,
 [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
  73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151,
  157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, ...]}
Rust task finished after 4 seconds
{:ok,
 [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
  73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151,
  157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, ...]}
Elixir task finished after 33 seconds

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

&lt;/div&gt;



&lt;p&gt;Well that’s quite a lot better!&lt;/p&gt;

&lt;h2&gt;
  
  
  Do other people do this?
&lt;/h2&gt;

&lt;p&gt;Yes! An example of a company having this setup is Discord, see &lt;a href="https://blog.discordapp.com/using-rust-to-scale-Elixir-for-11-million-concurrent-users-c6f19fc029d3"&gt;Using Rust to Scale Elixir for 11 Million Concurrent Users&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Let me preface the conclusion by stating you shouldn’t just introduce Rust in your Elixir stack like this because you &lt;em&gt;think&lt;/em&gt; you’ll need it.&lt;/p&gt;

&lt;p&gt;It’s great to see the ease in which a low level, but safe language such as Rust, can be added to our stack to overcome one of Elixir’s weaker points with relative ease.&lt;/p&gt;

&lt;p&gt;I’m sure both implementations can be further refined. The point that I want to make is that it’s easy to resort to a more performant language when need does arise.&lt;/p&gt;

&lt;h2&gt;
  
  
  The full code samples
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# Elixir_primes.ex
defmodule RustNif.ElixirPrimes do
  # http://erlang.org/doc/efficiency_guide/listHandling.html
  def prime_numbers(numbers) do
    prime_numbers(numbers, [])
  end

  defp prime_numbers([], result) do
    {:ok, result |&amp;gt; Enum.reverse() }
  end

  defp prime_numbers([number | rest], result) do
    new_result = result
    |&amp;gt; add_if_prime_number(number)

    prime_numbers(rest, new_result)
  end

  defp add_if_prime_number(numbers, 1), do: numbers

  defp add_if_prime_number(numbers, 2) do
    [2 | numbers]
  end

  defp add_if_prime_number(numbers, n) do
    range = 2..(n - 1)
    case Enum.any?(range, fn x -&amp;gt; rem(n, x) == 0 end) do
      false -&amp;gt; [n | numbers]
      _ -&amp;gt; numbers
    end
  end
end



1
2
3
4
5
6
7

# prime_numbers.ex
defmodule RustNif.PrimeNumbers do
  use Rustler, otp_app: :rust_nif, crate: :primenumbers

  # Overriden when module is loaded
  def prime_numbers(_nums), do: :erlang.nif_error(:nif_not_loaded)
end



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

// lib.rs
use rustler::{Encoder, Env, Error, Term};

mod atoms {
    rustler::rustler_atoms! {
        atom ok;
        atom error;
    }
}

rustler::rustler_export_nifs! {
    "Elixir.RustNif.PrimeNumbers",
    [
        ("prime_numbers", 1, prime_numbers, rustler::SchedulerFlags::DirtyCpu)
    ],
    None
}

fn is_prime_number(x: i64) -&amp;gt; bool {
    let end_of_range = x - 1;

    if x == 1 {
        false
    } else {
        !(2..end_of_range).any(|num| x % num == 0)
    }
}

fn prime_numbers&amp;lt;'a&amp;gt;(env: Env&amp;lt;'a&amp;gt;, args: &amp;amp;[Term&amp;lt;'a&amp;gt;]) -&amp;gt; Result&amp;lt;Term&amp;lt;'a&amp;gt;, Error&amp;gt; {
    let is_list: bool = args[0].is_list();

    if !is_list {
        Ok((atoms::error(), "No list supplied").encode(env))
    } else {
        let numbers: Vec&amp;lt;i64&amp;gt; = args[0].decode()?;
        let mut vec = Vec::new();

        for number in numbers {
            if is_prime_number(number) {
                vec.push(number)
            }
        }

        Ok((atoms::ok(), &amp;amp;*vec).encode(env))
    }
}

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

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>Real World Phoenix |&gt; Let's d4y</title>
      <dc:creator>Tjaco Oostdijk</dc:creator>
      <pubDate>Tue, 31 Dec 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/kabisasoftware/real-world-phoenix-let-s-d4y-1pf3</link>
      <guid>https://dev.to/kabisasoftware/real-world-phoenix-let-s-d4y-1pf3</guid>
      <description>&lt;p&gt;I believe there is a strong case to be made for shipping your app / idea as soon as possible as this pushes you to think about creating something small that is functional and finished.&lt;/p&gt;

&lt;p&gt;So today I want to cover a topic that has been discussed a lot in the community and there seems to be a tendency in Elixir land that they are hard. Let’s discuss deployments! The fact that it is often categorised as difficult or hard seems to scare people away and confuses a lot of people. I’m here to tell you that deployments are not hard, once you know your options.&lt;/p&gt;

&lt;h2&gt;
  
  
  Releases - yes you should use them!
&lt;/h2&gt;

&lt;p&gt;There are basically two ways to run your elixir app in production. One is using &lt;code&gt;mix&lt;/code&gt; and the other is using &lt;code&gt;releases&lt;/code&gt;. I want to focus today on using releases as using a build tool to deploy and run your app seems silly… And I think demistifying all the fuss around releases will make sure we get more comfortable using them.&lt;/p&gt;

&lt;p&gt;We’ll take 3 steps in learning about deployment. First we’ll package and run our release locally to test out how releases work, next we’ll take one of the easiest platforms I’ve seen to deploy our app, &lt;a href="https://render.com/"&gt;render.com&lt;/a&gt;, and lastly we’ll go the other deep end and run our app in a kubernetes cluster!&lt;/p&gt;

&lt;p&gt;In this post we’ll cover steps 1 and 2 and I’m going to leave the kubernetes way for the next blog post as that needs some more indepth explaination. You might think that I’m nuts going the kubernetes route, but I wanted to play around with it and see if it is really that hard to grasp… tl;dr =&amp;gt; It really isn’t nowadays due to all the managed kubernetes services that handle a lot of the hard stuff behind the scenes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Baking and running a release locally
&lt;/h2&gt;

&lt;p&gt;After all the hard work that has been done on &lt;a href="https://github.com/bitwalker/distillery"&gt;distillery&lt;/a&gt; by Paul Schoenfelder (a.k.a. bitwalker), Elixir has embraced releases as part of core. So no more excuses not to use releases! And ofcourse Phoenix has gone along with this trend and is enabling deployment with releases out of the box.&lt;/p&gt;

&lt;p&gt;So let’s see how easy it is to get this up and running. A lot of guides out there omit the database part, but I’ll add that here so we know how to enable that as well. For production we’ll set a &lt;code&gt;DATABASE_URL&lt;/code&gt; environment variable as that is most convenient to use when deploying. We will also set the &lt;code&gt;SECRET_KEY_BASE&lt;/code&gt; as an environment variable. Locally we’ll test our release in development mode, because setting a local release to production mode requires all kinds of extra settings that we really don’t need to test this out.&lt;/p&gt;

&lt;p&gt;If you want to try this out while reading this, you can create a fresh Phoenix app and follow along:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2

mix archive.install hex phx_new 1.4.11
mix phx.new test_deployment

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

&lt;/div&gt;



&lt;p&gt;Now let’s take this step by step:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;setup a releases.exs file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1

mv config/prod.secret.exs config/releases.exs

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;change &lt;code&gt;use Mix.Config&lt;/code&gt; to &lt;code&gt;import Config&lt;/code&gt; in &lt;code&gt;releases.exs&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1

sed -i'' -e 's/use Mix.Config/import Config/g' config/releases.exs

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;set &lt;code&gt;server: true&lt;/code&gt; in &lt;code&gt;config/releases.exs&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1

sed -i'' -e 's/^#.*config :test_deployment, TestDeploymentWeb.Endpoint, server: true/config :test_deployment, TestDeploymentWeb.Endpoint, server: true/g' config/releases.exs

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;remove &lt;code&gt;import_config prod.secret.exs&lt;/code&gt; from &lt;code&gt;config/prod.exs&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1

sed -i'' -e 's/import_config "prod.secret.exs"//g' config/prod.exs

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;create our database locally (so we can actually test it with a database). Note that we create the development db with this command
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1

mix ecto.create

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;set environment variables
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2

export SECRET_KEY_BASE=$(mix phx.gen.secret)
export DATABASE_URL=ecto://postgres:postgres@localhost/test_deployment_dev

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;prepare our app
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7

# fetch dependencies
mix deps.get --only prod
MIX_ENV=prod mix compile

# compile assets
npm run deploy --prefix ./assets
mix phx.digest

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;build our release
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1

MIX_ENV=prod mix release

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;run it!
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1

_build/prod/rel/test_deployment/bin/test_deployment start

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

&lt;/div&gt;



&lt;p&gt;After that visit &lt;a href="http://localhost:4000"&gt;http://localhost:4000&lt;/a&gt; and see your fresh release shine!&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying a release to render.com
&lt;/h2&gt;

&lt;p&gt;Now let’s see what it takes to put this live on &lt;code&gt;render.com&lt;/code&gt;. If you are following along, make sure you have the repo you want to use on &lt;code&gt;gitlab&lt;/code&gt; or &lt;code&gt;github&lt;/code&gt; so you can pull that in easily.&lt;/p&gt;

&lt;p&gt;Creating an account on render.com is easy and deploying and running your app is charged by the second, so you can safely follow along this turorial and shut the app down once done so you have an idea of the workflow involved in getting your app live on render.com. Note that signing up with &lt;code&gt;github&lt;/code&gt; or &lt;code&gt;gitlab&lt;/code&gt; gives you the extra benefit of being able to select the repo to deploy right from render, Which makes it even easier!&lt;/p&gt;

&lt;p&gt;So go to &lt;a href="https://dashboard.render.com/select-repo?type=web"&gt;https://dashboard.render.com/select-repo?type=web&lt;/a&gt; and select the repository you want to deploy. After that fill in the neccessary details. So choose a descriptive name and set the environment to Elixir. You’ll notice that the default settings to deploy here are &lt;code&gt;mix phx.digest&lt;/code&gt; and &lt;code&gt;mix phx.server&lt;/code&gt;.In our case we don’t want to use that, because we are using releases. To use releases in render we have to change these two settings.&lt;/p&gt;

&lt;p&gt;We’ll set the build command to: &lt;code&gt;build.sh&lt;/code&gt;, which we will create in our repo shortly. The start command will be the command we used when testing the release locally. In our case this would be: &lt;code&gt;_build/prod/rel/test_deployment/bin/test_deployment start&lt;/code&gt;. There are two more settings needed in the advanced setting. First one is the &lt;code&gt;SECRET_KEY_BASE&lt;/code&gt; environment variable(you can just generate one with mix phx.gen.secret and paste that in here) and the other one is the database url. We’ll need to create the database first for this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up the database
&lt;/h3&gt;

&lt;p&gt;To set up a database for our app go to &lt;a href="https://dashboard.render.com/new/database"&gt;https://dashboard.render.com/new/database&lt;/a&gt; and create a database with the settings you prefer. After creating the databaseyou can go into the settings page and copy the &lt;code&gt;External connection string&lt;/code&gt;. This string should go into the advanced setting of your app as &lt;code&gt;DATABASE_URL&lt;/code&gt; (exactly as we did when testing it locally).&lt;/p&gt;

&lt;h3&gt;
  
  
  Build script
&lt;/h3&gt;

&lt;p&gt;The build script gives us all the power to customise building our app when deploying. In our case this means entering the commands that will be run to prepare our app for deployment.&lt;/p&gt;

&lt;p&gt;This is the content of our build.sh file. As you can see we are basically addingthe steps we used when creating the release on our local machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

#!/usr/bin/env elixir

# Initial setup
mix deps.get --only prod
MIX_ENV=prod mix compile

# Compile assets
npm install --prefix ./assets
npm run deploy --prefix ./assets
mix phx.digest

# Remove the existing release directory and build the release
rm -rf "_build"
MIX_ENV=prod mix release

# for auto DB migration upon deploy
MIX_ENV=prod mix ecto.migrate

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

&lt;/div&gt;



&lt;p&gt;After creating the &lt;code&gt;build.sh&lt;/code&gt; file, make sure to make it executable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1

chmod a+x build.sh

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  And….. deploy!
&lt;/h3&gt;

&lt;p&gt;Once we have the database up and running and the environment variables setup we can proceed to deploy. And the beauty here is that once you push to master with the &lt;code&gt;build.sh&lt;/code&gt; included it will automagically trigger a deploy on render.com&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom domain
&lt;/h3&gt;

&lt;p&gt;Once you have your app deployed, you’ll get a url generated by render to access your app. However it is fairly easy to add your custom domain in your app settings.&lt;/p&gt;

&lt;p&gt;Alright, hope this was helpful and I certainly hope that this illustrates how straightforward it is to use releases to deploy Elixir / Phoenix apps. As promised in the next post I’ll cover a deployment setup that is a bit more complex using kubernetes. That is actually also the setup that I’ve used to deploy &lt;a href="https://realworldphoenix.com/"&gt;realworldphoenix.com&lt;/a&gt;, so more good stuff is coming in 2020!&lt;/p&gt;

&lt;p&gt;Happy New Year to all of you and may 2020 be the year that Elixir shines for us all!&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>phoenix</category>
      <category>d4y</category>
    </item>
    <item>
      <title>Real World Phoenix |&gt; Let's Send Some Emails</title>
      <dc:creator>Tjaco Oostdijk</dc:creator>
      <pubDate>Tue, 24 Dec 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/kabisasoftware/real-world-phoenix-let-s-send-some-emails-3do6</link>
      <guid>https://dev.to/kabisasoftware/real-world-phoenix-let-s-send-some-emails-3do6</guid>
      <description>&lt;p&gt;Sending emails is something probably almost all apps will have to be able to do at some point in time. So how do we go about doing such a thing with Phoenix and what libraries can we use for this, if any.&lt;/p&gt;

&lt;p&gt;The mindset in the Elixir ecosystem is not to make use of a library right off the bat. Since setting up and sending emails is a common task that takes quite some effort to take from scratch, a library is very welcome. Luckily we have a few very good options in Elixir land. As a side note, if you want to see what options you have for libraries in certain categories, &lt;a href="https://elixir.libhunt.com/categories/705-email"&gt;Elixir libhunt&lt;/a&gt; is a very nice place to investigate the stats for some similar libraries. It is like the Ruby Toolbox, if you are coming from ruby. You can check the comparison of some popular email sending libraries &lt;a href="https://elixir.libhunt.com/categories/705-email"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We are not going for the number one in this case(gen_smtp) as, although it looks really nice, it’s a bit too low level OTP for our use case. The two main ones that work well with Phoenix are &lt;code&gt;Swoosh&lt;/code&gt; and &lt;code&gt;Bamboo&lt;/code&gt;. They are very similar and today I’m going with the one created by Thoughtbot, &lt;code&gt;Bamboo&lt;/code&gt;, mainly because I’ve used that before. However there is little difference between the two and both actually support most transactional email providers out of the box. For the sake of this tutorial I’ll use a free account setup with sendgrid. Sendgrid provides a generous trial period and also have a pretty extensive free number of emails you can send every month. So more than enough for some simple experiments.&lt;/p&gt;

&lt;p&gt;Ok, so let’s get this show on the road. My main goal for this post is to let my app send a welcome mail for new signups of my app. The app is a student management app for music teachers, and there is a signup form that is built using LiveView that is used to sign up either as a teacher or as a student. The authentication mechanism is built using Pow.&lt;/p&gt;

&lt;p&gt;Let’s start by adding &lt;code&gt;Bamboo&lt;/code&gt; to our mix project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6

# mix.exs
...
  def deps do
    [{:bamboo, "~&amp;gt; 1.3"}]
  end
...

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

&lt;/div&gt;



&lt;p&gt;And make sure we pull it in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1

$ mix deps.get

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

&lt;/div&gt;



&lt;p&gt;We’ll need a mailer module for sending the actual emails and one or more email modules for creating emails to be sent. We’ll start with the mailer module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3

defmodule StudentManager.Mailer do
  use Bamboo.Mailer, otp_app: :student_manager
end

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

&lt;/div&gt;



&lt;p&gt;And here is an example email module for crafting your emails. We create a &lt;code&gt;base_email/0&lt;/code&gt; function for some general options and two functions for a teacher and student welcome email respectively. This should all be fairly self-explanatory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

defmodule StudentManager.Email do
  use Bamboo.Phoenix, view: StudentManagerWeb.EmailView

  def welcome_email(%{roles: ["teacher"]} = user) do
    base_email()
    |&amp;gt; to(user.email)
    |&amp;gt; subject("Welcome to StudMan App")
    |&amp;gt; assign(:user, user)
    |&amp;gt; render("welcome_teacher.html")
  end

  def welcome_email(%{roles: ["student"]} = user) do
    base_email()
    |&amp;gt; to(user.email)
    |&amp;gt; subject("Welcome to StudMan App")
    |&amp;gt; assign(:user, user)
    |&amp;gt; render("welcome_student.html")
  end

  defp base_email() do
    new_email()
    |&amp;gt; from("support@studman.nl")
  end
end

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

&lt;/div&gt;



&lt;p&gt;If you look closely you’ll notice I’m using pattern matching to send out a student or teacher mail based on the role that gets assigned when they sign up. I’m also using &lt;code&gt;Bamboo.Phoenix&lt;/code&gt; here in order to take advantage of the template engine included in Phoenix. This really makes it a breeze to create html emails. Normally you would configure this to also send out a plain text version, but for now we’ll just create the html version.&lt;/p&gt;

&lt;p&gt;One last thing we need to do is add some configuration for our mailer in &lt;code&gt;config/config.exs&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3

config :student_manager, StudentManager.Mailer,
  adapter: Bamboo.SendGridAdapter,
  api_key: "my_sendgrid_api_key"

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

&lt;/div&gt;



&lt;p&gt;So all very straightforward stuff. Cool! Just following the guides from &lt;code&gt;Bamboo&lt;/code&gt; should get you up and running quickly. We can now add sending of the welcome email in the signup process we created in the &lt;a href="https://www.theguild.nl/real-world-phoenix-sign-up-flow-spa-style-with-liveview/"&gt;last blogpost&lt;/a&gt;. Remember from last post that we used LiveView for our registration form? Because of that, it’s easy to add our email in the signup flow like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13
14

  def handle_event("save", %{"user" =&amp;gt; user_params}, socket) do
    case create_user(socket, user_params) do
      {:ok, user} -&amp;gt;
        Email.welcome_email(user) |&amp;gt; Mailer.deliver_later()

        {:stop,
         socket
         |&amp;gt; put_flash(:info, "user created")
         |&amp;gt; redirect(to: "/")}

      {:error, %Ecto.Changeset{} = changeset} -&amp;gt;
        {:noreply, assign(socket, changeset: changeset)}
    end
  end

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

&lt;/div&gt;



&lt;p&gt;The fact that I’m using LiveView here makes it pretty easy to hook into the registration process. If we were using the regular sign-up process of Pow we would need to do a little more work to get this email delivered. Let’s see how that would work. Luckily the author of Pow also has us covered. The library provides callback hooks which you can use to hook into the sign-up process in certain spots. In our case, the after_create hook for the registration_controller is the hook we want to grab.&lt;/p&gt;

&lt;p&gt;Lets’s create a file for our controller_callbacks: &lt;code&gt;student_manager_web/pow/callbacks_controller.ex&lt;/code&gt; with the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6
7
8
9
10
11
12
13

defmodule StudentManagerWeb.Pow.CallbacksController do
  @moduledoc false

  use Pow.Extension.Phoenix.ControllerCallbacks.Base

  def before_respond(Pow.Phoenix.RegistrationController, :create, {:ok, user, conn}, _config) do
    user
    |&amp;gt; StudentManager.Email.welcome_email()
    |&amp;gt; StudentManager.Mailer.deliver_later()

    {:ok, user, conn}
  end
end

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

&lt;/div&gt;



&lt;p&gt;Now to be able to trigger these callbacks, we’ll need to add this in our pow config in &lt;code&gt;config/config.exs&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1
2
3
4
5
6

# Pow Configuration
config :student_manager, :pow,
  user: StudentManager.Accounts.User,
  repo: StudentManager.Repo,
  web_module: StudentManagerWeb,
  controller_callbacks: StudentManagerWeb.Pow.ControllerCallbacks

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

&lt;/div&gt;



&lt;p&gt;That should do it for sending out a welcoming email on signup. Hope this was helpful!&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;

</description>
      <category>phoenix</category>
      <category>elixir</category>
      <category>kabisa</category>
    </item>
    <item>
      <title>Real World Phoenix |&gt; Let's Send Some Emails</title>
      <dc:creator>Tjaco Oostdijk</dc:creator>
      <pubDate>Tue, 24 Dec 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/kabisasoftware/real-world-phoenix-let-s-send-some-emails-1ec7</link>
      <guid>https://dev.to/kabisasoftware/real-world-phoenix-let-s-send-some-emails-1ec7</guid>
      <description>&lt;p&gt;Sending emails is something probably almost all apps will have to be able to do at some point in time. So how do we go about doing such a thing with Phoenix and what libraries can we use for this, if any. &lt;/p&gt;

&lt;p&gt;The mindset in the Elixir ecosystem is not to make use of a library right off the bat. Since setting up and sending emails is a common task that takes quite some effort to take from scratch, a library is very welcome. Luckily we have a few very good options in Elixir land. As a side note, if you want to see what options you have for libraries in certain categories, &lt;a href="https://elixir.libhunt.com/categories/705-email"&gt;Elixir libhunt&lt;/a&gt; is a very nice place to investigate the stats for some similar libraries. It is like the Ruby Toolbox, if you are coming from ruby. You can check the comparison of some popular email sending libraries &lt;a href="https://elixir.libhunt.com/categories/705-email"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We are not going for the number one in this case(gen_smtp) as, although it looks really nice, it's a bit too low level OTP for our use case. The two main ones that work well with Phoenix are &lt;code&gt;Swoosh&lt;/code&gt; and &lt;code&gt;Bamboo&lt;/code&gt;. They are very similar and today I'm going with the one created by Thoughtbot,  &lt;code&gt;Bamboo&lt;/code&gt;, mainly because I've used that before. However there is little difference between the two and both actually support most transactional email providers out of the box. For the sake of this tutorial I'll use a free account setup with sendgrid. Sendgrid provides a generous trial period and also have a pretty extensive free number of emails you can send every month. So more than enough for some simple experiments.&lt;/p&gt;

&lt;p&gt;Ok, so let's get this show on the road. My main goal for this post is to let my app send a welcome mail for new signups of my app. The app is a student management app for music teachers, and there is a signup form that is built using LiveView that is used to sign up either as a teacher or as a student. The authentication mechanism is built using Pow. &lt;/p&gt;

&lt;p&gt;Let's start by adding &lt;code&gt;Bamboo&lt;/code&gt; to our mix project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# mix.exs&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;deps&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="ss"&gt;:bamboo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 1.3"&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And make sure we pull it in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;mix deps.get
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We'll need a mailer module for sending the actual emails and one or more email modules for creating emails to be sent. We'll start with the mailer module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;StudentManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Mailer&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Bamboo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Mailer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;otp_app:&lt;/span&gt; &lt;span class="ss"&gt;:student_manager&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And here is an example email module for crafting your emails. We create a &lt;code&gt;base_email/0&lt;/code&gt; function for some general options and two functions for a teacher and student welcome email respectively. This should all be fairly self-explanatory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;StudentManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Email&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Bamboo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Phoenix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;view:&lt;/span&gt; &lt;span class="no"&gt;StudentManagerWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;EmailView&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;welcome_email&lt;/span&gt;&lt;span class="p"&gt;(%{&lt;/span&gt;&lt;span class="ss"&gt;roles:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"teacher"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;base_email&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Welcome to StudMan App"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"welcome_teacher.html"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;welcome_email&lt;/span&gt;&lt;span class="p"&gt;(%{&lt;/span&gt;&lt;span class="ss"&gt;roles:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"student"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;base_email&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Welcome to StudMan App"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"welcome_student.html"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;base_email&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;new_email&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"support@studman.nl"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you look closely you'll notice I'm using pattern matching to send out a student or teacher mail based on the role that gets assigned when they sign up. I'm also using &lt;code&gt;Bamboo.Phoenix&lt;/code&gt; here in order to take advantage of the template engine included in Phoenix. This really makes it a breeze to create html emails. Normally you would configure this to also send out a plain text version, but for now we'll just create the html version.&lt;/p&gt;

&lt;p&gt;One last thing we need to do is add some configuration for our mailer in &lt;code&gt;config/config.exs&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:student_manager&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;StudentManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Mailer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;adapter:&lt;/span&gt; &lt;span class="no"&gt;Bamboo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;SendGridAdapter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;api_key:&lt;/span&gt; &lt;span class="s2"&gt;"my_sendgrid_api_key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So all very straightforward stuff. Cool! Just following the guides from &lt;code&gt;Bamboo&lt;/code&gt; should get you up and running quickly. We can now add sending of the welcome email in the signup process we created in the &lt;a href="https://www.theguild.nl/real-world-phoenix-sign-up-flow-spa-style-with-liveview/"&gt;last blogpost&lt;/a&gt;. Remember from last post that we used LiveView for our registration form? Because of that, it's easy to add our email in the signup flow like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;handle_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"save"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;user_params&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="no"&gt;Email&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;welcome_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Mailer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deliver_later&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:stop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;socket&lt;/span&gt;
         &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;put_flash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"user created"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;to:&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;

      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Changeset&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:noreply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;changeset:&lt;/span&gt; &lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The fact that I'm using LiveView here makes it pretty easy to hook into the registration process. If we were using the regular sign-up process of Pow we would need to do a little more work to get this email delivered. Let's see how that would work. Luckily the author of Pow also has us covered. The library provides callback hooks which you can use to hook into the sign-up process in certain spots. In our case, the after_create hook for the registration_controller is the hook we want to grab.&lt;/p&gt;

&lt;p&gt;Lets's create a file for our controller_callbacks: &lt;code&gt;student_manager_web/pow/callbacks_controller.ex&lt;/code&gt; with the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;StudentManagerWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Pow&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;CallbacksController&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@moduledoc&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;

  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Pow&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Extension&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Phoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;ControllerCallbacks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;before_respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Pow&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Phoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;RegistrationController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;StudentManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Email&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;welcome_email&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;StudentManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Mailer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deliver_later&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now to be able to trigger these callbacks, we'll need to add this in our pow config in &lt;code&gt;config/config.exs&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Pow Configuration&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:student_manager&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:pow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;user:&lt;/span&gt; &lt;span class="no"&gt;StudentManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Accounts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;repo:&lt;/span&gt; &lt;span class="no"&gt;StudentManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;web_module:&lt;/span&gt; &lt;span class="no"&gt;StudentManagerWeb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;controller_callbacks:&lt;/span&gt; &lt;span class="no"&gt;StudentManagerWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Pow&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;ControllerCallbacks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That should do it for sending out a welcoming email on signup. Hope this was helpful!&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>phoenix</category>
      <category>emails</category>
    </item>
  </channel>
</rss>
