<?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: Byron Hambly</title>
    <description>The latest articles on DEV Community by Byron Hambly (@delta1).</description>
    <link>https://dev.to/delta1</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%2F47140%2Fe89a9d4f-ffeb-4d4c-9806-375a70fcf36e.PNG</url>
      <title>DEV Community: Byron Hambly</title>
      <link>https://dev.to/delta1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/delta1"/>
    <language>en</language>
    <item>
      <title>Rust NIFs in Elixir</title>
      <dc:creator>Byron Hambly</dc:creator>
      <pubDate>Tue, 03 Mar 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/delta1/rust-nifs-in-elixir-1c3j</link>
      <guid>https://dev.to/delta1/rust-nifs-in-elixir-1c3j</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Rustler&lt;/strong&gt; - Native Implemented Functions (NIFs) in Rust from Elixir&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iz1F-MRu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hambly.dev/assets/images/rustler.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iz1F-MRu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hambly.dev/assets/images/rustler.png" alt="Image of the word Rustler covered in rust" width="880" height="239"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://elixir-lang.org/"&gt;Elixir&lt;/a&gt; is a dynamic, strongly typed, functional programming language that runs on the Erlang virtual machine - the &lt;a href="https://en.wikipedia.org/wiki/BEAM_(Erlang_virtual_machine)"&gt;BEAM&lt;/a&gt;. It inherits that distributed and fault-tolerant architecture, while adding a Ruby-like syntax, modern build tool and package manager &lt;a href="https://hexdocs.pm/mix/Mix.html"&gt;Mix&lt;/a&gt;, and a built-in testing framework. It really &lt;em&gt;sparks joy&lt;/em&gt; every time I get to use it!&lt;/p&gt;

&lt;p&gt;Given its trade-offs for scalability and managed memory, when you require better CPU and memory performance it’s necessary to reach for Native Implemented Functions. &lt;a href="http://erlang.org/doc/tutorial/nif.html"&gt;NIFs&lt;/a&gt; allow you to call functions from compiled, shared libraries directly in your Elixir/Erlang code. However, a native function that crashes or misbehaves can &lt;a href="https://erlang.org/doc/man/erl_nif.html"&gt;crash the whole VM&lt;/a&gt; or cause scheduling and memory consumption issues.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/rusterlium/rustler"&gt;Rustler&lt;/a&gt; is a library for writing NIFs in Rust, giving us better &lt;a href="https://doc.rust-lang.org/nomicon/what-unsafe-does.html"&gt;safety&lt;/a&gt; from undefined behavior, out-of-bounds access, use-after-free bugs, null pointer dereferences, and data races. All of which can have disasterous consequences on the Erlang VM.&lt;/p&gt;

&lt;p&gt;Discord wrote a great article where they &lt;a href="https://blog.discordapp.com/using-rust-to-scale-elixir-for-11-million-concurrent-users-c6f19fc029d3"&gt;used Rust NIFs to scale their Elixir service&lt;/a&gt;. They created a sorted “Member List” data structure that’s very fast to process mutations in the list, and returns the indices where items in the list are mutated. They released that work as an open source library called &lt;a href="https://github.com/discordapp/sorted_set_nif"&gt;Discord.SortedSet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are some brilliant quality of life improvements landing in Rustler 0.22, so let’s learn how to use Rustler to create a NIF module in Elixir!&lt;/p&gt;

&lt;h3&gt;
  
  
  Rustler by example
&lt;/h3&gt;

&lt;p&gt;This source code is available at &lt;a href="https://github.com/delta1/rustler-demo-app"&gt;delta1/rustler-demo-app&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start off by installing &lt;a href="https://elixir-lang.org/install.html"&gt;Elixir&lt;/a&gt; and &lt;a href="https://www.rust-lang.org/tools/install"&gt;Rust&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Open a terminal and your favorite text editor or IDE.&lt;/li&gt;
&lt;li&gt;Create a new Mix application
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mix new my_app &amp;amp;&amp;amp; cd my_app

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Edit &lt;code&gt;mix.exs&lt;/code&gt; to add Rustler as a dependency.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# mix.exs
defp deps do
  [
    {:rustler, github: "rusterlium/rustler", 
      ref: "e343b8ca", sparse: "rustler_mix"}
  ]
end

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

&lt;/div&gt;



&lt;p&gt;Here we are importing the dependency directly from GitHub, to get the latest changes coming in the next version of Rustler! The &lt;code&gt;ref&lt;/code&gt; key is the git commit hash, and &lt;code&gt;sparse&lt;/code&gt; indicates that the Elixir dependency is in a specific folder of the repository.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install the dependency
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mix deps.get

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create a new NIF using Rustler - use &lt;code&gt;MyNif&lt;/code&gt; as the module name, and &lt;code&gt;mynif&lt;/code&gt; for the Rust crate name
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mix rustler.new

This is the name of the Elixir module the NIF module will be registered to.
Module name &amp;gt; MyNif
This is the name used for the generated Rust crate. The default is most likely fine.
Library name (mynif) &amp;gt;
* creating native/mynif/.cargo/config
* creating native/mynif/README.md
* creating native/mynif/Cargo.toml
* creating native/mynif/src/lib.rs
Ready to go! See my_app/native/mynif/README.md for further instructions.

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Follow the instructions in the readme to update &lt;code&gt;mix.exs&lt;/code&gt; by adding &lt;code&gt;:rustler&lt;/code&gt; to the project compilers, and &lt;code&gt;mynif&lt;/code&gt; to the rustler crates
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# mix.exs
def project do
  [
    # ...
    compilers: [:rustler] ++ Mix.compilers(),
    rustler_crates: [mynif: []]
  ]
end

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Now update the Rustler dependency in &lt;code&gt;native/mynif/Cargo.toml&lt;/code&gt;, using the &lt;code&gt;rev&lt;/code&gt; key to specify the same git revision
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Cargo.toml
[dependencies]
rustler = { git = "https://github.com/rusterlium/rustler", 
            rev = "e343b8ca"}

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Now we can compile! This will download our Rust deps, compile the crate, and compile our Elixir code
&lt;/li&gt;
&lt;/ul&gt;

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

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Take a look at &lt;code&gt;native/mynif/src/lib.rs&lt;/code&gt; where the auto generated &lt;code&gt;add&lt;/code&gt; function is defined and exported to the &lt;code&gt;Elixir.MyNif&lt;/code&gt; module
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#[rustler::nif]
fn add(a: i64, b: i64) -&amp;gt; i64 {
    a + b
}

rustler::init!("Elixir.MyNif", [add]);

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;To use our NIF from Elixir, we need to create the MyNif module in &lt;code&gt;lib/my_nif.ex&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;defmodule MyNif do
  use Rustler, otp_app: :my_app, crate: "mynif"

  # When your NIF is loaded, it will override this function.
  def add(_a, _b), do: :erlang.nif_error(:nif_not_loaded)
end

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Now let’s create a test for it!
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# test/my_nif_test.exs
defmodule MyNifTest do
  use ExUnit.Case

  test "adds using NIF" do
    assert MyNif.add(300, 120) == 420
  end
end

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;And run the tests
&lt;/li&gt;
&lt;/ul&gt;

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

...

Finished in 0.03 seconds
1 doctest, 2 tests, 0 failures

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Now let’s do something more interesting by trying to crash the Erlang VM. Update &lt;code&gt;lib.rs&lt;/code&gt; with a &lt;code&gt;panic&lt;/code&gt; function, and add it to the list of exported functions
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// lib.rs
// ...

#[rustler::nif]
fn panic() {
    panic!("Boom!")
}

rustler::init!("Elixir.MyNif", [add, panic]);

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Add the declaration to the &lt;code&gt;MyNif&lt;/code&gt; Elixir module
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# lib/my_nif.ex
# ...

def panic(), do: :erlang.nif_error(:nif_not_loaded)

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Fire up an IEx console and let’s see what happens
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iex -S mix

iex(1)&amp;gt; MyNif.panic()
thread '&amp;lt;unnamed&amp;gt;' panicked at 'Boom!', src/lib.rs:8:5

** (ErlangError) Erlang error: :nif_panicked
    (my_app 0.1.0) MyNif.panic()

iex(1)&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;Our IEx console is still running! This is part of the safety provided by using Rust for our NIFs, even if our NIF panics, Rustler wraps it up nicely in an &lt;code&gt;ErlangError&lt;/code&gt; which we can use to either catch the exception, or just &lt;a href="https://verraes.net/2014/12/erlang-let-it-crash/"&gt;let it crash&lt;/a&gt; and restart the process!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Okay so let’s &lt;strong&gt;really crash&lt;/strong&gt; the VM! Create and export another function in &lt;code&gt;lib.rs&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;// lib.rs
// ...

#[rustler::nif]
fn crash() {
    // create a null pointer
    let p: *const i32 = std::ptr::null();
    // https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html
    unsafe {
        // dereferencing a raw pointer can only be done in an unsafe block
        // but dereferencing a NULL pointer is undefined behavior!
        // and likely to cause a segmentation fault and program crash
        println!("{:?}", *p);
    }
}

rustler::init!("Elixir.MyNif", [add, panic, crash]);

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Don’t forget to add the declaration to the &lt;code&gt;MyNif&lt;/code&gt; Elixir module
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# lib/my_nif.ex
# ...

def crash(), do: :erlang.nif_error(:nif_not_loaded)

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Start IEx again and run the crash function..
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iex -S mix

iex(1)&amp;gt; MyNif.crash()

user@host:my_app$

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

&lt;/div&gt;



&lt;p&gt;There we go! We completely crashed the Erlang VM and process supervisor, killing the IEx session and dumping us back into the terminal.&lt;/p&gt;

&lt;p&gt;Of course this is &lt;strong&gt;not&lt;/strong&gt; what you want your NIF to be doing, which is why it’s useful to write these in Rust instead of C - hopefully catching these kinds of issues before they happen.&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance
&lt;/h3&gt;

&lt;p&gt;Let’s see the performance difference in action, by generating and sorting random numbers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To generate random numbers in Elixir, use the Erlang &lt;a href="https://erlang.org/doc/man/rand.html#uniform-1"&gt;&lt;code&gt;:rand&lt;/code&gt;&lt;/a&gt; module. Add this function in the &lt;code&gt;MyApp&lt;/code&gt; module
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# lib/my_app.ex
def generate(num, upper) do
  1..num
  |&amp;gt; Enum.map(fn _ -&amp;gt; :rand.uniform(upper) end)
end

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;To do the same thing in Rust, we’ll depend on the &lt;a href="https://docs.rs/crate/rand"&gt;&lt;code&gt;Rand&lt;/code&gt;&lt;/a&gt; crate. Add the dependency in &lt;code&gt;Cargo.toml&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;[dependencies]
# ...
rand = "0.7.3"

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Then create and export our new function in &lt;code&gt;lib.rs&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;#[rustler::nif]
fn generate(num: i64, upper: i64) -&amp;gt; Vec&amp;lt;i64&amp;gt; {
    extern crate rand;
    use rand::Rng;

    let mut rng = rand::thread_rng();

    // rust ranges are "half open"
    // so (0..5) has a length of 5
    // https://doc.rust-lang.org/std/ops/struct.Range.html
    (0..num)
        .map(|_| {
            // gen_range includes the lower bound
            // but excludes the upper bound
            // so we add 1 to upper to match the elixir implementation
            // https://docs.rs/rand/0.7.3/rand/trait.Rng.html
            rng.gen_range(1, upper + 1)
        })
        .collect()
}

rustler::init!("Elixir.MyNif", [add, panic, crash, generate]);

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;And load it in the &lt;code&gt;MyNif&lt;/code&gt; module
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# lib/my_nif.ex
# ...

def generate(_num, _upper), do: :erlang.nif_error(:nif_not_loaded)

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Now let’s time these functions using the Erlang &lt;a href="https://erlang.org/doc/man/timer.html#tc-3"&gt;&lt;code&gt;:timer&lt;/code&gt;&lt;/a&gt; module, in a new file &lt;code&gt;timing.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;# timing.exs

{microseconds, _} = :timer.tc(MyApp, :generate, [1_000_000, 1_000_000])
IO.puts("MyApp.generate #{microseconds / 1_000_000} seconds")

{microseconds, _} = :timer.tc(MyApp, :generate, [10_000_000, 1_000_000])
IO.puts("MyApp.generate #{microseconds / 1_000_000} seconds")

{microseconds, _} = :timer.tc(MyApp, :generate, [30_000_000, 1_000_000])
IO.puts("MyApp.generate #{microseconds / 1_000_000} seconds")

{microseconds, _} = :timer.tc(MyNif, :generate, [1_000_000, 1_000_000])
IO.puts("MyNif.generate #{microseconds / 1_000_000} seconds")

{microseconds, _} = :timer.tc(MyNif, :generate, [10_000_000, 1_000_000])
IO.puts("MyNif.generate #{microseconds / 1_000_000} seconds")

{microseconds, _} = :timer.tc(MyNif, :generate, [30_000_000, 1_000_000])
IO.puts("MyNif.generate #{microseconds / 1_000_000} seconds")

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Run the script
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mix run timing.exs

MyApp.generate 0.375 seconds
MyApp.generate 3.875 seconds
MyApp.generate 11.063 seconds

MyNif.generate 0.047 seconds
MyNif.generate 0.328 seconds
MyNif.generate 1.39 seconds

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

&lt;/div&gt;



&lt;p&gt;The results are far from scientific, but we can see that the NIF is roughly 10x faster in this example.&lt;/p&gt;

&lt;p&gt;We have generated random integers, let’s see what happens when we sort them!&lt;/p&gt;

&lt;p&gt;For Elixir, we’ll use &lt;a href="https://hexdocs.pm/elixir/Enum.html#sort/1"&gt;Enum.sort&lt;/a&gt; - which uses the Erlang &lt;a href="https://erlang.org/doc/man/lists.html#sort-1"&gt;:lists&lt;/a&gt; module under the hood.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To implement our Rust sort, create and export the following function in &lt;code&gt;lib.rs&lt;/code&gt; where we use the &lt;a href="https://doc.rust-lang.org/std/vec/struct.Vec.html#method.sort"&gt;&lt;code&gt;sort&lt;/code&gt;&lt;/a&gt; method on Vec structs in the standard library
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#[rustler::nif]
fn sort(ints: Vec&amp;lt;i64&amp;gt;) -&amp;gt; Vec&amp;lt;i64&amp;gt; {
    let mut copy = ints.clone();
    copy.sort();

    copy
}

rustler::init!("Elixir.MyNif", [add, panic, crash, generate, sort]);

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Load the function in &lt;code&gt;MyNif&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;# my_nif.ex
# ...

def sort(ints), do: :erlang.nif_error(:nif_not_loaded)

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Now update our timing script to sort our generated lists
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{_, ints1} = :timer.tc(MyNif, :generate, [1_000_000, 1_000_000])
{_, ints2} = :timer.tc(MyNif, :generate, [10_000_000, 1_000_000])
{_, ints3} = :timer.tc(MyNif, :generate, [30_000_000, 1_000_000])

{microseconds, _} = :timer.tc(Enum, :sort, [ints1])
IO.puts("Enum.sort #{microseconds / 1_000_000} seconds")
{microseconds, _} = :timer.tc(Enum, :sort, [ints2])
IO.puts("Enum.sort #{microseconds / 1_000_000} seconds")
{microseconds, _} = :timer.tc(Enum, :sort, [ints3])
IO.puts("Enum.sort #{microseconds / 1_000_000} seconds")

{microseconds, _} = :timer.tc(MyNif, :sort, [ints1])
IO.puts("MyNif.sort #{microseconds / 1_000_000} seconds")
{microseconds, _} = :timer.tc(MyNif, :sort, [ints2])
IO.puts("MyNif.sort #{microseconds / 1_000_000} seconds")
{microseconds, _} = :timer.tc(MyNif, :sort, [ints3])
IO.puts("MyNif.sort #{microseconds / 1_000_000} seconds")

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;And let’s take a look!
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mix run timing.exs

Enum.sort 0.328 seconds
Enum.sort 6.156 seconds
Enum.sort 21.656 seconds

MyNif.sort 0.125 seconds
MyNif.sort 1.438 seconds
MyNif.sort 4.75 seconds

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

&lt;/div&gt;



&lt;p&gt;For the given lists, the NIF appears to be roughly 3 to 5 times as fast. Below is a comparison where I also generated and sorted 50 million and 100 million items.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--as6fpn95--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/j16sfnbunrdwsi2lmoh3.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--as6fpn95--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/j16sfnbunrdwsi2lmoh3.PNG" alt="graph" width="880" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Elixir is an excellent language for building distributed and fault-tolerant applications. Like most dynamic and memory-managed languages, it trades some performance for ease of use and reliability.&lt;/p&gt;

&lt;p&gt;When maximum performance is required, it’s possible to call Native Implemented Functions from Elixir. &lt;a href="https://github.com/rusterlium/rustler"&gt;Rustler&lt;/a&gt; allows the use of Rust for these native functions, giving us stronger safety guarantees against issues that could affect the reliability of the application.&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>rust</category>
      <category>rustler</category>
      <category>erlang</category>
    </item>
    <item>
      <title>Is home online?</title>
      <dc:creator>Byron Hambly</dc:creator>
      <pubDate>Mon, 11 Nov 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/delta1/is-home-online-59le</link>
      <guid>https://dev.to/delta1/is-home-online-59le</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Tracking electricity and internet uptime with a Raspberry Pi, GNU utils, and Apache on a Linux VPS.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here in South Africa, we have ongoing issues with &lt;a href="https://www.enca.com/news/load-shedding-schedules-info-and-how-it-affects-you"&gt;load-shedding&lt;/a&gt; - planned electricity interruptions to “prevent the collapse of the power system”. This is mostly due to the corruption of the South African government, and the ineptitude of the failing public utility Eskom. On top of that the municipal providers also have major issues keeping their substations working - even outside of load-shedding!&lt;/p&gt;

&lt;p&gt;Which is to say, my house is often without power, enough that I have finally made a system to “track” whether the electricity is on at home that I can check remotely. This basically requires that the internet also be working, but given that my ISP is more reliable than the electricity supply it’s not a problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project goals
&lt;/h3&gt;

&lt;p&gt;Using the Linux VPS and Raspberry Pi I already have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a password protected webpage that can tell me if the electricity/internet is on at home&lt;/li&gt;
&lt;li&gt;If it’s off, show me when last it was on&lt;/li&gt;
&lt;li&gt;Make it as simple and reliable as possible&lt;/li&gt;
&lt;li&gt;No database or programming language setup&lt;/li&gt;
&lt;li&gt;Keep historical data for interest/analytics&lt;/li&gt;
&lt;li&gt;If possible, use only built-in GNU/Linux tools!&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Check out the code in the Github repository &lt;a href="https://github.com/delta1/uptime"&gt;delta1/uptime&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here’s the result!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1eDwbcLM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/mhg0m9e5zhdj0rswjtzn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1eDwbcLM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/mhg0m9e5zhdj0rswjtzn.png" alt="" width="880" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;⚡ Electricity tracker online!&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://en.wikipedia.org/wiki/Cron"&gt;cron&lt;/a&gt; job on the Raspberry Pi at home uses &lt;a href="https://en.wikipedia.org/wiki/Cron"&gt;curl&lt;/a&gt; to request a basic HTML page with a specific query string from the VPS every minute it’s online.&lt;/li&gt;
&lt;li&gt;The Ubuntu 18.04 VPS runs &lt;a href="https://httpd.apache.org/"&gt;Apache&lt;/a&gt; HTTP Server to serve a virtual host on a custom subdomain. Apache logs all access requests to a specific file.&lt;/li&gt;
&lt;li&gt;The uptime report page is a &lt;a href="https://en.wikipedia.org/wiki/Common_Gateway_Interface"&gt;CGI&lt;/a&gt; bash script. It uses GNU/Linux tools to filter the access log and generate the HTML for the page. It also uses &lt;a href="https://stedolan.github.io/jq/"&gt;jq&lt;/a&gt; to generate a JSON file from the logs to use in a &lt;a href="https://www.chartjs.org/"&gt;Chart.js&lt;/a&gt; graph.&lt;/li&gt;
&lt;li&gt;This page is secured by &lt;a href="https://en.wikipedia.org/wiki/Basic_access_authentication"&gt;basic authentication&lt;/a&gt;, and TLS thanks to &lt;a href="https://certbot.eff.org/"&gt;Certbot&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Learning
&lt;/h3&gt;

&lt;p&gt;I love hacking on things like this and trying to find different or interesting ways to solve problems! It’s always a rabbit hole. Here are the things I learnt about in the process of creating this system.&lt;/p&gt;

&lt;h4&gt;
  
  
  htpasswd
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://httpd.apache.org/docs/current/programs/htpasswd.html"&gt;htpasswd&lt;/a&gt; is a command line utility that ships with Apache, used for managing the files that store user credentials for basic authentication.&lt;/p&gt;

&lt;p&gt;Using it is fairly simple, firstly your Apache configuration defines the basic auth requirements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Directory "/path/to/folder"&amp;gt;
        # ...
        AuthType basic
        AuthName "Authorization Required"
        AuthUserFile /path/to/folder/.htpasswd
        Require valid-user
        # ...
&amp;lt;/Directory&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;Then use the htpasswd utility to create the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# create a new password file ".htpasswd" for user "admin"
htpasswd -c /path/to/folder/.htpasswd admin
# you will be prompted to enter the password

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

&lt;/div&gt;



&lt;p&gt;By default the password is hashed with the &lt;a href="https://httpd.apache.org/docs/2.4/misc/password_encryptions.html#basic"&gt;Apache MD5 algorithm&lt;/a&gt; - let’s see what the file looks like for the password “test”&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cat /path/to/folder/.htpasswd
admin:$apr1$JTztWOb0$MC.LHwVsu/ZR0iVxq/Ma..

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

&lt;/div&gt;



&lt;p&gt;More information about &lt;a href="https://httpd.apache.org/docs/current/programs/htpasswd.html#options"&gt;htpasswd options and examples&lt;/a&gt; on the documentation site.&lt;/p&gt;

&lt;h4&gt;
  
  
  logrotate
&lt;/h4&gt;

&lt;p&gt;By default, Apache log files are “rotated” on Ubuntu by the system utility &lt;a href="https://linux.die.net/man/8/logrotate"&gt;logrotate&lt;/a&gt;. Log rotation is the automated process of archiving, compressing, renaming, and/or deleting log files - in order to restrict their size and keep current logs small enough to be viewed.&lt;/p&gt;

&lt;p&gt;Configuration files for logrotate can be found at &lt;code&gt;/etc/logrotate.d/&lt;/code&gt;. In this case, I want to keep old logs around for analysis later and used the following configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# /etc/logrotate.d/apache2
/path/to/log/*.log {
        # rotate the log files each month
        monthly
        # keep the last 120 versions (10 years of monthly logs)
        rotate 120
        # compress them - if enabled in logrotate.conf base file
        compress
        # delay compression if the log is not closed immediately
        delaycompress
        # use the following Linux permissions, user, and group
        create 640 www-data www-data
        # apache defaults omitted ...
}

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

&lt;/div&gt;



&lt;h4&gt;
  
  
  Common Gateway Interface (CGI)
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Common_Gateway_Interface"&gt;CGI&lt;/a&gt; is a protocol that enables web servers to execute command line programs on the server to dynamically generate HTML. It was first introduced in 1993, making it the &lt;a href="https://royal.pingdom.com/a-history-of-the-dynamic-web/#post-254"&gt;first method of creating dynamic webpages&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;I used it here since it was easy to set up in Apache, and as simple as using a &lt;a href="https://en.wikipedia.org/wiki/Bash_(Unix_shell)"&gt;bash&lt;/a&gt; script to set some variables I could use on the report page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Apache config
&amp;lt;Directory "/path/to/folder"&amp;gt;
        # ...
        Options +ExecCGI
        AddHandler cgi-script .cgi
        # ...
&amp;lt;/Directory&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;The contents of the CGI report script: &lt;a href="https://github.com/delta1/uptime/blob/master/cgi/report.cgi"&gt;&lt;code&gt;/path/to/folder/cgi/report.cgi&lt;/code&gt;&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;#!/bin/bash

# we need to write the content type header first
# before any content
# followed by an empty line
echo "Content-type: text/html"
echo ""

# filter out the log lines we want that contain action=track
grep action=track ../log/access.log &amp;gt; action.log

# put the last 24+ hours worth into a json file
# but just keep the date strings
# so we can parse them into date objects for the graph
tail -n 1440 action.log | cut -d "[" -f 2 | cut -d "]" -f 1 | sed 's/:/ /' | sed 's/\//-/g' | jq -R -s -c 'split("\n") | map(select(. != ""))' &amp;gt; 1440.json

# set environment variables
export CURRENT_DATETIME=$(date +"%A %d %b %Y - %T");
export LAST_TRACK=$(tail -n 1 action.log);
export LAST_IP=$(tail -n 1 action.log | cut -d " " -f 1);
export LAST_ONLINE=$(tail -n 1 action.log | cut -d "[" -f 2 | cut -d "]" -f 1 | cut -d " " -f 1 | sed 's/:/ /' | sed 's/\// /g');
export LAST_DATE=$(cut -d " " -f 1-3 &amp;lt;&amp;lt;&amp;lt; $LAST_ONLINE);
export LAST_TIME=$(cut -d " " -f 4 &amp;lt;&amp;lt;&amp;lt; $LAST_ONLINE);

# substitute environment variables into the template file
envsubst &amp;lt; template.html

exit 0

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

&lt;/div&gt;



&lt;p&gt;Those linux pipelines are pretty terse, here’s a breakdown of &lt;a href="https://github.com/delta1/uptime/blob/master/cgi/report.cgi#L15"&gt;line 15&lt;/a&gt; above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# create a json file of date strings
# each line in action.log resembles this format:
# 111.22.111.33 - [07/Nov/2019:18:47:02 +0200] "GET /?action=track HTTP/1.1"

# take the last 1440 lines from action.log
tail -n 1440 action.log |
# 'cut' each line at the '[' char and take the 2nd part
cut -d "[" -f 2 |
# 'cut' each line at the ']' char and take the 1st part
cut -d "]" -f 1 |
# replace the first ':' char with a space
sed 's/:/ /' |
# replace every '/' char with a dash
sed 's/\//-/g' |
# at this point all that's left are the dates, eg:
# 27-Nov-2019 18:47:02 +0200
# now use the jq utility to create a json file
# with an array of these dates
jq -R -s -c 'split("\n") | map(select(. != ""))' &amp;gt; 1440.json
# explanation of this 'jq' line follows

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

&lt;/div&gt;



&lt;p&gt;The command line utilities &lt;a href="https://www.gnu.org/software/coreutils/manual/html_node/tail-invocation.html#tail-invocation"&gt;tail&lt;/a&gt;, &lt;a href="https://www.gnu.org/software/coreutils/manual/html_node/cut-invocation.html#cut-invocation"&gt;cut&lt;/a&gt;, and &lt;a href="https://www.gnu.org/software/sed/"&gt;sed&lt;/a&gt; are free software designed and developed by &lt;a href="https://www.gnu.org/"&gt;GNU&lt;/a&gt;. They are generally already installed on Linux distributions, and are very useful tools to know.&lt;/p&gt;

&lt;p&gt;The last line above uses &lt;a href="https://stedolan.github.io/jq/"&gt;jq&lt;/a&gt; - a “lightweight and flexible command-line JSON processor” - to create a json file with an array of dates.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;jq&lt;/code&gt; options and filter used above are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-R / --raw-input&lt;/code&gt; : Don’t parse the input as JSON - since we are inputting lines of strings.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-s / --slurp&lt;/code&gt; : Read the entire input stream into a large array and run the filter just once.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-c / --compact-output&lt;/code&gt; : Compact output instead of pretty print.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;split("\n") | map(select(. != ""))&lt;/code&gt; : split the input by newlines into an array, and only keep the values that are not empty&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can read more in the &lt;a href="https://stedolan.github.io/jq/manual/"&gt;jq manual&lt;/a&gt; and &lt;a href="https://stedolan.github.io/jq/tutorial/"&gt;tutorial&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  envsubst
&lt;/h4&gt;

&lt;p&gt;Instead of printing HTML strings with &lt;code&gt;echo&lt;/code&gt;, I wanted to find an elegant way to replace values in a template. I stumbled upon &lt;a href="https://serverfault.com/a/960077"&gt;this serverfault.com question and answer&lt;/a&gt; which introduced me to &lt;a href="https://www.gnu.org/software/gettext/manual/html_node/envsubst-Invocation.html"&gt;envsubst&lt;/a&gt; - another beautiful piece of GNU software that “substitutes the values of environment variables” into the input string!&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;envsubst&lt;/code&gt; made the CGI program above as easy as exporting some environment variables, and using the HTML page as an input.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# read the file "template.html" into envsubst,
# which substitutes strings like ${NAME} with the env variable NAME
# and writes the resulting output to stdout
# which the webserver sends as a response to the web request
envsubst &amp;lt; template.html

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

&lt;/div&gt;



&lt;p&gt;You can see my &lt;a href="https://github.com/delta1/uptime/blob/master/cgi/template.html"&gt;HTML template in the Github repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This was probably my favourite new piece of software I learnt in this project, it easily accomplished what I was trying to do - with a simple built-in command!&lt;/p&gt;

&lt;h4&gt;
  
  
  Chart.js
&lt;/h4&gt;

&lt;p&gt;I took this opportunity to learn the basics of &lt;a href="https://www.chartjs.org/"&gt;Chart.js&lt;/a&gt;, a popular and free graphing library. The &lt;a href="https://www.chartjs.org/samples/latest/"&gt;list of samples&lt;/a&gt; is impressive, and you can see how I created the graph in the &lt;a href="https://github.com/delta1/uptime/blob/master/cgi/uptime.js"&gt;&lt;code&gt;uptime.js&lt;/code&gt;&lt;/a&gt; file of the repo.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="https://momentjs.com/"&gt;Moment.js&lt;/a&gt; it was easy to parse and work with dates. Thankfully I don’t have to support any old browsers so I was able to use “new” Javascript features like &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch"&gt;fetch&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await"&gt;async/await&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions"&gt;arrow functions&lt;/a&gt;, and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals"&gt;template literals&lt;/a&gt; - without needing a transpilation step.&lt;/p&gt;

</description>
      <category>apache</category>
      <category>htpasswd</category>
      <category>logrotate</category>
      <category>cgi</category>
    </item>
    <item>
      <title>Style updates</title>
      <dc:creator>Byron Hambly</dc:creator>
      <pubDate>Fri, 18 Oct 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/delta1/style-updates-33ec</link>
      <guid>https://dev.to/delta1/style-updates-33ec</guid>
      <description>&lt;p&gt;Implementing user experience improvements on my blog, including dark mode and a reading progress bar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reading on dev.to? See the actual effects here: &lt;a href="https://hambly.dev/style-updates.html"&gt;hambly.dev/style-updates.html&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Flashing cursor
&lt;/h3&gt;

&lt;p&gt;I thought it would be cool to make the link in the header look like it has a flashing cursor. So I slapped a &lt;code&gt;&amp;lt;blink&amp;gt;&lt;/code&gt; tag on it! Just kidding, the blink tag is &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/blink"&gt;obsolete&lt;/a&gt;, so I found a CSS animation and tweaked it as required. Simply animate the text color to transparent and back each second, infinitely.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// SCSS&lt;/span&gt;

&lt;span class="nc"&gt;.blink&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$grey-color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1s&lt;/span&gt; &lt;span class="nb"&gt;blink&lt;/span&gt; &lt;span class="n"&gt;step-end&lt;/span&gt; &lt;span class="n"&gt;infinite&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="nt"&gt;blink&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;from&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nt"&gt;to&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;50&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$grey-color&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;h3&gt;
  
  
  Reading time estimate
&lt;/h3&gt;

&lt;p&gt;I added a reading time estimate next to the published date, with the following Liquid include. It works by counting the number of words in the content, and dividing it by an average reading speed. I had to wrap the snippet below in &lt;a href="https://shopify.github.io/liquid/tags/raw/"&gt;raw tags&lt;/a&gt; so that it isn’t evaluated by Jekyll at build time!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight liquid"&gt;&lt;code&gt;
&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nv"&gt;content&lt;/span&gt;&lt;span class="err"&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="nf"&gt;number_of_words&lt;/span&gt;&lt;span class="err"&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="nf"&gt;plus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="err"&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="nf"&gt;divided_by&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="err"&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="nf"&gt;prepend&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="err"&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="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;" min read"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;

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



&lt;h3&gt;
  
  
  Reading progress bar
&lt;/h3&gt;

&lt;p&gt;I like the cosmetic effect of an animated progress bar while reading, enough to sacrifice my “Javascript only for analytics” rule. The site still works perfectly with Javascript disabled, which is what really matters - to the tech crowd anyway. Minimizing Javascript is good for mobile sites in general, as it all needs to be &lt;a href="https://v8.dev/blog/cost-of-javascript-2019"&gt;downloaded, parsed, and compiled&lt;/a&gt;. This increases CPU load and uses more energy from your precious battery.&lt;/p&gt;

&lt;p&gt;The progress bar works by adding a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress"&gt;&lt;code&gt;&amp;lt;progress&amp;gt;&lt;/code&gt;&lt;/a&gt; element to the page, and updating its &lt;code&gt;value&lt;/code&gt; attribute when scrolling. An event listener is added to the &lt;code&gt;scroll&lt;/code&gt; event, but we &lt;a href="https://www.html5rocks.com/en/tutorials/speed/animations/"&gt;decouple the animation&lt;/a&gt; from the input events by using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame"&gt;&lt;code&gt;requestAnimationFrame&lt;/code&gt;&lt;/a&gt; to cue the animation for the next browser repaint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// add a &amp;lt;progress&amp;gt; element to the page, style and position it to taste&lt;/span&gt;
&lt;span class="c1"&gt;// &amp;lt;progress id="progress" max="100" value="0"&amp;gt;&amp;lt;/progress&amp;gt;&lt;/span&gt;

&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;load&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resize&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;listening&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;scrollPosition&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="nx"&gt;ticking&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;progress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;progress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;width&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="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollHeight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offsetHeight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientHeight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollHeight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offsetHeight&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;maxScroll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;scrollPosition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollY&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageYOffset&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ticking&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;scrollPosition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;maxScroll&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;ticking&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="nx"&gt;ticking&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&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="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;listening&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// only add the scrolling event listener once!&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scroll&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;listening&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&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;h3&gt;
  
  
  Dark mode
&lt;/h3&gt;

&lt;p&gt;I recently found out about the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme"&gt;prefers-color-scheme&lt;/a&gt; CSS media feature after seeing &lt;a href="https://tombrow.com/dark-mode-website-css"&gt;this post by Tom Brow&lt;/a&gt;, and I had to have it. Something about great artists stealing, and all that. 🔪&lt;/p&gt;

&lt;p&gt;Initially I was looking for a dark mode option in browser settings, before finding out it’s an operating system wide setting. &lt;a href="https://hambly.dev/style-updates.html#dark-mode"&gt;Here’s what it looks like on Windows 10&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is nice and easy to do, just add another &lt;code&gt;@media&lt;/code&gt; declaration to your css, and then overwrite your styling as required. A really nice tidbit from Tom’s article is to apply a slight grayscale &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/filter"&gt;&lt;code&gt;filter&lt;/code&gt;&lt;/a&gt; to the images in dark mode, so they are subtly less bright.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// SCSS&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#222&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#eee&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;pre&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nt"&gt;code&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#444&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;grayscale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;20%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;text-decoration&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="nl"&gt;border-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="nv"&gt;$orange-color&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="m"&gt;1s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$orange-color&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="nt"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#ddd&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;Here’s how to enable dark mode on &lt;a href="https://support.apple.com/en-us/HT208976"&gt;MacOS&lt;/a&gt;, &lt;a href="https://www.macworld.com/article/3405138/how-to-enable-dark-mode-on-iphone-and-ipad.html"&gt;iOS&lt;/a&gt;, and &lt;a href="https://9to5google.com/2018/12/17/android-dark-mode-theme-pie/"&gt;Android&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With style updates done, for now, I can concentrate on writing something a bit more technical. Until next time!&lt;/p&gt;

</description>
      <category>darkmode</category>
      <category>css</category>
      <category>javascript</category>
      <category>progressbar</category>
    </item>
    <item>
      <title>Hacktoberfest - 2019</title>
      <dc:creator>Byron Hambly</dc:creator>
      <pubDate>Fri, 11 Oct 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/delta1/hacktoberfest-2019-6i0</link>
      <guid>https://dev.to/delta1/hacktoberfest-2019-6i0</guid>
      <description>&lt;p&gt;A quick rundown of the pull requests I submitted for Hacktoberfest.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://hacktoberfest.digitalocean.com/"&gt;Hacktoberfest&lt;/a&gt; is an annual event to encourage involvement in open source software. Anyone around the world can earn a limited edition T-shirt, by submitting 4 or more pull requests to Github-hosted repositories during the month of October.&lt;/p&gt;

&lt;p&gt;I wanted to take part for the past 2 years but didn’t manage in time. I was determined to earn my shirt this year! I knocked out the 4 PRs in the first week of October, and now it’s just a matter of waiting for the shirt to ship and hoping it doesn’t get “lost” in the SA customs/postal system.&lt;/p&gt;

&lt;p&gt;This year I wanted to work on Elixir related documentation and code. I submitted 3 PRs to &lt;a href="https://github.com/elixirschool/elixirschool"&gt;ElixirSchool&lt;/a&gt;, and 1 to &lt;a href="https://github.com/zhyu/nadia"&gt;Nadia&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NMu5GUQ7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hambly.dev/assets/images/hack.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NMu5GUQ7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hambly.dev/assets/images/hack.jpg" alt="Hacktoberfest 2019 powered by DigitalOcean and DEV.to" width="880" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Elixir School
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://elixirschool.com/en/"&gt;Elixir School&lt;/a&gt; is “the premier destination for people looking to learn and master the Elixir programming language.”&lt;/p&gt;

&lt;p&gt;It’s a really great community of passionate Elixir developers, writing lessons and blog posts about their favorite programming language and ecosystem. They are very welcoming and friendly, and it was great to get involved.&lt;/p&gt;

&lt;p&gt;My pull requests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/elixirschool/elixirschool/pull/2008"&gt;#2008&lt;/a&gt; - Documentation in the “collections” lesson.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/elixirschool/elixirschool/pull/2009"&gt;#2009&lt;/a&gt; - Add an RSS feed for the site’s blog posts.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/elixirschool/elixirschool/pull/2011"&gt;#2011&lt;/a&gt; - Documentation in the “Ecto changesets” lesson.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Nadia
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://hexdocs.pm/nadia/api-reference.html"&gt;Nadia&lt;/a&gt; is a “Telegram Bot API Wrapper written in Elixir”.&lt;/p&gt;

&lt;p&gt;I’ve been using this library on a side-project, and wanted to contribute back. There was a relatively simple request to add the “Pin Chat Message” API call in &lt;a href="https://github.com/zhyu/nadia/issues/94"&gt;issue #94&lt;/a&gt;, so I decided to work on that.&lt;/p&gt;

&lt;p&gt;In this PR I learned about &lt;a href="https://github.com/parroty/exvcr"&gt;ExVCR&lt;/a&gt;, an Elixir library inspired by Ruby’s &lt;a href="https://github.com/vcr/vcr"&gt;VCR&lt;/a&gt;, which allows you to record HTTP responses in your test suite so that future test runs use the recorded responses instead.&lt;/p&gt;

&lt;p&gt;Since it was already in use in the Nadia library, it was fairly simple to add tests for the pin/unpin chat message functionality. I already had a Telegram bot I used for testing, so I created a new group, and then posted a message using the bot. That provided the chat ID, message ID, and API responses when calling PinChatMessage and UnpinChatMessage.&lt;/p&gt;

&lt;p&gt;The ExVCR.Config module also provides the &lt;a href="https://hexdocs.pm/exvcr/ExVCR.Config.html#filter_sensitive_data/2"&gt;&lt;code&gt;filter_sensitive_data/2&lt;/code&gt;&lt;/a&gt; function to strip sensitive data out of the recorded response!&lt;/p&gt;

&lt;p&gt;My pull request:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/zhyu/nadia/pull/96"&gt;#96&lt;/a&gt; - Implement pinChatMessage and unpinChatMessage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Have you earned your shirt yet? Head to the &lt;a href="https://hacktoberfest.digitalocean.com/"&gt;Hacktoberfest website&lt;/a&gt; and start hacking!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>hacktoberfest</category>
      <category>elixir</category>
      <category>elixirschool</category>
    </item>
  </channel>
</rss>
