<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Nando Vieira</title>
    <description>Articles about Ruby, Rails, JavaScript, and more. By Nando Vieira.
</description>
    <language>en</language>
    <link>https://nandovieira.com</link>
    <atom:link href="https://nandovieira.com/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Wed, 28 Dec 2022 01:22:00 -0800</pubDate>
    <lastBuildDate>Wed, 28 Dec 2022 01:22:00 -0800</lastBuildDate>
    <item>
      <title>Using 1password-cli to avoid hardcoded secrets in your terminal profile</title>
      <description>
        <![CDATA[<p>One of the things that had always bothered me when creating terminal profiles is
hardcoding secrets like in <code>export GITHUB_TOKEN=&lt;SECRET&gt;</code>. This is even more
annoying when you have more than one computer and need to synchronize secrets
between them.</p>

<p>I finally decided to handle this issue with <a href="https://1password.com/downloads/command-line/">1password-cli</a>, a command-line 
utility that can interact with <a href="https://1password.com/">1Password</a>.</p>

<p>For the sake of this article, I&rsquo;ll skip the 1password-cli setup and I&rsquo;m assuming
the command <code>op</code> is available on your terminal.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>which op
<span class="go">/Users/fnando/local/bin/op
</span></code></pre></div>
<p>The first step is signing in by running the command <code>op signin</code>. Once you&rsquo;re
done you can list your vaults by running the command <code>op vault ls</code>.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>op vault <span class="nb">ls</span>
<span class="go">ID                            NAME
zyxlrxyu53kax7qqrxz554fgbq    Private
oblww37jzanxpl4wruzjz43i71    dev
</span></code></pre></div>
<p>In my case, I&rsquo;ll use the vault <code>dev</code> to store my development credentials. You
can either create a new item by using the command-line or 1Password&rsquo;s UI. I&rsquo;m
using a <code>server</code> type:</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>op item create <span class="nt">--title</span> main <span class="nt">--category</span> server <span class="nt">--vault</span> dev
<span class="go">ID:          kbfa7n6hcziy6ck7ys63pzta3a
Title:       main
Vault:       dev (oulww37jzinxpl4wruzjz43i44)
Created:     now
Updated:     now
Favorite:    false
Version:     0
Category:    SERVER
Fields:
  Admin Console:

  Hosting Provider:

</span></code></pre></div>
<p>I recommend opening 1Password and removing the default sections; unfortunately,
I couldn&rsquo;t find a way of removing sections by using the command-line. You can
also remove the default fields, as we won&rsquo;t use it (password, username and url).</p>

<p>Now you can add secrets to that item by using the command <code>op item edit</code>. Let&rsquo;s
add two secrets by using the command-line. Or you can do the same using
1Password&rsquo;s user interface.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>op item edit <span class="nt">--vault</span> dev main <span class="s1">'dev.SOME_API_KEY[password]=SOME_KEY'</span> <span class="s1">'dev.ANOTHER_API_KEY[password]=SOME_OTHER_KEY'</span>
<span class="go">ID:          kbfa7n6hcziy6ck7ys63pzta3a
Title:       main
Vault:       dev (oulww37jzinxpl4wruzjz43i44)
Created:     40 seconds ago
Updated:     now by Nando Vieira
Favorite:    false
Version:     2
Category:    SERVER
Fields:
  dev:
    SOME_API_KEY:       SOME_KEY
    ANOTHER_API_KEY:    SOME_OTHER_KEY
</span></code></pre></div>
<p>The secrets above will be added to a section called <code>dev</code>. Here&rsquo;s a screenshot
showing the secrets in 1Password&rsquo;s app.</p>

<p><img src="https://nandovieira.s3.us-east-1.amazonaws.com/media/1password-cli-entries.jpg" alt="1Password screenshot showing the items we&#39;ve created"></p>

<p>Once you&rsquo;ve added all secrets to 1Password, it&rsquo;s time to retrieve the secrets.
You can do it so by running the command <code>op item get</code>.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>op item get <span class="nt">--vault</span> dev main <span class="nt">--format</span> json
<span class="go">{
  "id": "kbfa7n6hcziy6ck7ys63pzta3a",
  "title": "main",
  "version": 3,
  "vault": {
    "id": "oulww37jzinxpl4wruzjz43i44",
    "name": "dev"
  },
  "category": "SERVER",
  "last_edited_by": "XCFD5SQIJNCZZOF2PBC7XED6QE",
  "created_at": "2022-12-28T08:44:55Z",
  "updated_at": "2022-12-28T00:45:35.033579-08:00",
  "sections": [
    {
      "id": "Section_6r6xfx7vkbniby2fogqg7tvmee",
      "label": "dev"
    }
  ],
  "fields": [
    {
      "id": "notesPlain",
      "type": "STRING",
      "purpose": "NOTES",
      "label": "notesPlain",
      "reference": "op://dev/main/notesPlain"
    },
    {
      "id": "xzuhdgcumcnz5peyvjckq2z4gi",
      "section": {
        "id": "Section_6r6xfx7vkbniby2fogqg7tvmee",
        "label": "dev"
      },
      "type": "CONCEALED",
      "label": "SOME_API_KEY",
      "value": "SOME_KEY",
      "reference": "op://dev/main/dev/SOME_API_KEY"
    },
    {
      "id": "4mg4ejw7q3pnsyworyiflofemq",
      "section": {
        "id": "Section_6r6xfx7vkbniby2fogqg7tvmee",
        "label": "dev"
      },
      "type": "CONCEALED",
      "label": "ANOTHER_API_KEY",
      "value": "SOME_OTHER_KEY",
      "reference": "op://dev/main/dev/ANOTHER_API_KEY"
    }
  ]
}
</span></code></pre></div>
<p>Notice that 1Password will always return the field <code>notesPlain</code>, as it&rsquo;s
built-in and can&rsquo;t even be removed.</p>

<p>The next step is using this list and convert it into a format that your terminal
will understand. I&rsquo;m going to use <a href="https://stedolan.github.io/jq/">jq</a> for this task.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>op item get main <span class="nt">--vault</span> dev <span class="nt">--format</span> json | jq <span class="nt">-r</span> <span class="s1">'.fields | map(select(has("value"))) | map("export " + .label + "=\"" + .value + "\"") | join("\n")'</span>
<span class="go">export SOME_API_KEY="SOME_KEY"
export ANOTHER_API_KEY="SOME_OTHER_KEY"
</span></code></pre></div>
<p>You can either use <code>eval</code> with the above output, or output to a file that your
terminal will load. I&rsquo;m going with the second option, but first let&rsquo;s clean up
the house and create an executable script. I&rsquo;m going to call it <code>op-env</code>.</p>
<div class="highlight"><pre class="highlight shell"><code><span class="c">#!/usr/bin/env bash</span>

<span class="nb">set</span> <span class="nt">-e</span>

op item get main <span class="nt">--vault</span> dev <span class="nt">--format</span> json | jq <span class="nt">-r</span> <span class="s1">'.fields | map(select(has("value"))) | map("export " + .label + "=\"" + .value + "\"") | join("\n")'</span>
</code></pre></div>
<p>Make it executable by running <code>chmod +x op-env</code>. You should see the exports
being printed to the screen if you run <code>op-env</code>.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>op-env
<span class="go">export SOME_API_KEY="SOME_KEY"
export ANOTHER_API_KEY="SOME_OTHER_KEY"
</span></code></pre></div>
<p>Now you can add the following lines to your profile. If you&rsquo;re using <a href="https://www.zsh.org">ZSH</a>,
<code>~/.zshrc</code> will do trick. On <a href="https://www.gnu.org/software/bash/">Bash</a> you can use <code>~/.bashrc</code>:</p>
<div class="highlight"><pre class="highlight shell"><code><span class="k">if</span> <span class="o">[</span> <span class="o">!</span> <span class="nt">-f</span> ~/.op-env <span class="o">]</span><span class="p">;</span> <span class="k">then
  </span>op run <span class="nt">--</span> <span class="nb">true
  </span>op-env <span class="o">&gt;</span> ~/.op-env
  <span class="nb">chmod </span>600 ~/.op-env
<span class="k">fi

</span><span class="nb">source</span> ~/.op-env
</code></pre></div>
<p>The code above will create the file <code>~/.op-env</code> unless one already exists, which
means your secrets will be cached until this file is removed or you refresh it
by running <code>op-env &gt; ~/.op-env</code>. This is required because 1Password will request
authorization every first call to <code>op</code> in a terminal session; that is, if you
open a new tab, you&rsquo;ll need to re-authorize <code>op</code>. By caching the results, you
only need to do this every once in a while.</p>
<h2 tabindex="-1" id="wrapping-up">Wrapping up<a class="anchor" href="#wrapping-up" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>That&rsquo;s it! Using the 1password-cli allowed me to drop hardcoded secrets, making
the secrets sharing way easier.</p>

<p>If you&rsquo;re using 1password-cli, make sure you also take a look at the <a href="https://developer.1password.com/docs/cli/secrets-environment-variables/"><code>.env</code>
file support</a>, which is also a neat way of sharing secrets
between team members.</p>

<p>How do you handle this problem? Do you use something else? Share your favorite
solution in the comments below.</p>
]]>
      </description>
      <content:encoded>
        <![CDATA[<p>One of the things that had always bothered me when creating terminal profiles is
hardcoding secrets like in <code>export GITHUB_TOKEN=&lt;SECRET&gt;</code>. This is even more
annoying when you have more than one computer and need to synchronize secrets
between them.</p>

<p>I finally decided to handle this issue with <a href="https://1password.com/downloads/command-line/">1password-cli</a>, a command-line 
utility that can interact with <a href="https://1password.com/">1Password</a>.</p>

<p>For the sake of this article, I&rsquo;ll skip the 1password-cli setup and I&rsquo;m assuming
the command <code>op</code> is available on your terminal.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>which op
<span class="go">/Users/fnando/local/bin/op
</span></code></pre></div>
<p>The first step is signing in by running the command <code>op signin</code>. Once you&rsquo;re
done you can list your vaults by running the command <code>op vault ls</code>.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>op vault <span class="nb">ls</span>
<span class="go">ID                            NAME
zyxlrxyu53kax7qqrxz554fgbq    Private
oblww37jzanxpl4wruzjz43i71    dev
</span></code></pre></div>
<p>In my case, I&rsquo;ll use the vault <code>dev</code> to store my development credentials. You
can either create a new item by using the command-line or 1Password&rsquo;s UI. I&rsquo;m
using a <code>server</code> type:</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>op item create <span class="nt">--title</span> main <span class="nt">--category</span> server <span class="nt">--vault</span> dev
<span class="go">ID:          kbfa7n6hcziy6ck7ys63pzta3a
Title:       main
Vault:       dev (oulww37jzinxpl4wruzjz43i44)
Created:     now
Updated:     now
Favorite:    false
Version:     0
Category:    SERVER
Fields:
  Admin Console:

  Hosting Provider:

</span></code></pre></div>
<p>I recommend opening 1Password and removing the default sections; unfortunately,
I couldn&rsquo;t find a way of removing sections by using the command-line. You can
also remove the default fields, as we won&rsquo;t use it (password, username and url).</p>

<p>Now you can add secrets to that item by using the command <code>op item edit</code>. Let&rsquo;s
add two secrets by using the command-line. Or you can do the same using
1Password&rsquo;s user interface.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>op item edit <span class="nt">--vault</span> dev main <span class="s1">'dev.SOME_API_KEY[password]=SOME_KEY'</span> <span class="s1">'dev.ANOTHER_API_KEY[password]=SOME_OTHER_KEY'</span>
<span class="go">ID:          kbfa7n6hcziy6ck7ys63pzta3a
Title:       main
Vault:       dev (oulww37jzinxpl4wruzjz43i44)
Created:     40 seconds ago
Updated:     now by Nando Vieira
Favorite:    false
Version:     2
Category:    SERVER
Fields:
  dev:
    SOME_API_KEY:       SOME_KEY
    ANOTHER_API_KEY:    SOME_OTHER_KEY
</span></code></pre></div>
<p>The secrets above will be added to a section called <code>dev</code>. Here&rsquo;s a screenshot
showing the secrets in 1Password&rsquo;s app.</p>

<p><img src="https://nandovieira.s3.us-east-1.amazonaws.com/media/1password-cli-entries.jpg" alt="1Password screenshot showing the items we&#39;ve created"></p>

<p>Once you&rsquo;ve added all secrets to 1Password, it&rsquo;s time to retrieve the secrets.
You can do it so by running the command <code>op item get</code>.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>op item get <span class="nt">--vault</span> dev main <span class="nt">--format</span> json
<span class="go">{
  "id": "kbfa7n6hcziy6ck7ys63pzta3a",
  "title": "main",
  "version": 3,
  "vault": {
    "id": "oulww37jzinxpl4wruzjz43i44",
    "name": "dev"
  },
  "category": "SERVER",
  "last_edited_by": "XCFD5SQIJNCZZOF2PBC7XED6QE",
  "created_at": "2022-12-28T08:44:55Z",
  "updated_at": "2022-12-28T00:45:35.033579-08:00",
  "sections": [
    {
      "id": "Section_6r6xfx7vkbniby2fogqg7tvmee",
      "label": "dev"
    }
  ],
  "fields": [
    {
      "id": "notesPlain",
      "type": "STRING",
      "purpose": "NOTES",
      "label": "notesPlain",
      "reference": "op://dev/main/notesPlain"
    },
    {
      "id": "xzuhdgcumcnz5peyvjckq2z4gi",
      "section": {
        "id": "Section_6r6xfx7vkbniby2fogqg7tvmee",
        "label": "dev"
      },
      "type": "CONCEALED",
      "label": "SOME_API_KEY",
      "value": "SOME_KEY",
      "reference": "op://dev/main/dev/SOME_API_KEY"
    },
    {
      "id": "4mg4ejw7q3pnsyworyiflofemq",
      "section": {
        "id": "Section_6r6xfx7vkbniby2fogqg7tvmee",
        "label": "dev"
      },
      "type": "CONCEALED",
      "label": "ANOTHER_API_KEY",
      "value": "SOME_OTHER_KEY",
      "reference": "op://dev/main/dev/ANOTHER_API_KEY"
    }
  ]
}
</span></code></pre></div>
<p>Notice that 1Password will always return the field <code>notesPlain</code>, as it&rsquo;s
built-in and can&rsquo;t even be removed.</p>

<p>The next step is using this list and convert it into a format that your terminal
will understand. I&rsquo;m going to use <a href="https://stedolan.github.io/jq/">jq</a> for this task.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>op item get main <span class="nt">--vault</span> dev <span class="nt">--format</span> json | jq <span class="nt">-r</span> <span class="s1">'.fields | map(select(has("value"))) | map("export " + .label + "=\"" + .value + "\"") | join("\n")'</span>
<span class="go">export SOME_API_KEY="SOME_KEY"
export ANOTHER_API_KEY="SOME_OTHER_KEY"
</span></code></pre></div>
<p>You can either use <code>eval</code> with the above output, or output to a file that your
terminal will load. I&rsquo;m going with the second option, but first let&rsquo;s clean up
the house and create an executable script. I&rsquo;m going to call it <code>op-env</code>.</p>
<div class="highlight"><pre class="highlight shell"><code><span class="c">#!/usr/bin/env bash</span>

<span class="nb">set</span> <span class="nt">-e</span>

op item get main <span class="nt">--vault</span> dev <span class="nt">--format</span> json | jq <span class="nt">-r</span> <span class="s1">'.fields | map(select(has("value"))) | map("export " + .label + "=\"" + .value + "\"") | join("\n")'</span>
</code></pre></div>
<p>Make it executable by running <code>chmod +x op-env</code>. You should see the exports
being printed to the screen if you run <code>op-env</code>.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>op-env
<span class="go">export SOME_API_KEY="SOME_KEY"
export ANOTHER_API_KEY="SOME_OTHER_KEY"
</span></code></pre></div>
<p>Now you can add the following lines to your profile. If you&rsquo;re using <a href="https://www.zsh.org">ZSH</a>,
<code>~/.zshrc</code> will do trick. On <a href="https://www.gnu.org/software/bash/">Bash</a> you can use <code>~/.bashrc</code>:</p>
<div class="highlight"><pre class="highlight shell"><code><span class="k">if</span> <span class="o">[</span> <span class="o">!</span> <span class="nt">-f</span> ~/.op-env <span class="o">]</span><span class="p">;</span> <span class="k">then
  </span>op run <span class="nt">--</span> <span class="nb">true
  </span>op-env <span class="o">&gt;</span> ~/.op-env
  <span class="nb">chmod </span>600 ~/.op-env
<span class="k">fi

</span><span class="nb">source</span> ~/.op-env
</code></pre></div>
<p>The code above will create the file <code>~/.op-env</code> unless one already exists, which
means your secrets will be cached until this file is removed or you refresh it
by running <code>op-env &gt; ~/.op-env</code>. This is required because 1Password will request
authorization every first call to <code>op</code> in a terminal session; that is, if you
open a new tab, you&rsquo;ll need to re-authorize <code>op</code>. By caching the results, you
only need to do this every once in a while.</p>
<h2 tabindex="-1" id="wrapping-up">Wrapping up<a class="anchor" href="#wrapping-up" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>That&rsquo;s it! Using the 1password-cli allowed me to drop hardcoded secrets, making
the secrets sharing way easier.</p>

<p>If you&rsquo;re using 1password-cli, make sure you also take a look at the <a href="https://developer.1password.com/docs/cli/secrets-environment-variables/"><code>.env</code>
file support</a>, which is also a neat way of sharing secrets
between team members.</p>

<p>How do you handle this problem? Do you use something else? Share your favorite
solution in the comments below.</p>
]]>
      </content:encoded>
      <link>https://nandovieira.com/using-1password-cli-to-avoid-hardcoded-secrets-in-your-terminal-profile</link>
      <guid>https://nandovieira.com/using-1password-cli-to-avoid-hardcoded-secrets-in-your-terminal-profile</guid>
      <pubDate>Wed, 28 Dec 2022 01:22:00 -0800</pubDate>
    </item>
    <item>
      <title>Creating custom assets with Stellar</title>
      <description>
        <![CDATA[<p><img src="https://s3.amazonaws.com/nandovieira/media/stellar-asset/stellar-logo.svg" alt="Stellar" class="align-right transparent" /></p>

<p>If you&rsquo;re watching the crypto space, you probably know about Stellar.
<a href="https://stellar.org">Stellar</a> is an open network for storing and moving money.
You can easily create, send and trade digital representations of all forms of
money by using a feature called
<a href="https://developers.stellar.org/docs/glossary/assets/">assets</a>.</p>

<p>Assets are, essentially, custom tokens. Any person can create an asset in just a
few steps, and in this article we&rsquo;ll create one called <code>ACME</code>. Let&rsquo;s get this
started!</p>
<h2 tabindex="-1" id="understanding-assets">Understanding assets<a class="anchor" href="#understanding-assets" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>Assets are identified by a code and a Stellar account public key. This
identifier is what you use to look up or interact with it on the Stellar
network. An asset identifier must be unique (i.e. both code and public key must
be unique); you can have multiple assets representing US dollars or Bitcoins
using the same code (<code>USD</code> and <code>BTC</code>), but not the same code plus public key.</p>

<p>Let&rsquo;s say both Binance and Coinbase want to issue Bitcoin assets on the Stellar
network; Binance&rsquo;s asset could be something like <code>BTC:GAAA...BINANCE</code>, and
Coinbase&rsquo;s asset could be <code>BTC:GAAA...COINBASE</code>.</p>

<p>Ultimately, is up to the user to decide which representation is more
trustworthy, but a product/service can also make a decision for users based on
partnerships; <a href="https://vibrantapp.com">Vibrant</a> uses
<a href="https://stablex.org">Stablex</a>&rsquo;s ARST for Argentine Pesos and
<a href="https://www.circle.com/">Circle</a>&rsquo;s USDC for US Dollar.</p>

<p>An asset is often used to represent something outside Stellar&rsquo;s network like
fiat currency, bonds, gold, etc, but it can be used to represent pretty much
anything, like other crypto currencies, reward points or karma (like in Reddit).</p>
<h2 tabindex="-1" id="creating-your-custom-asset">Creating your custom asset<a class="anchor" href="#creating-your-custom-asset" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>The process of creating a custom asset is fairly simple.</p>

<ol>
<li>You need two accounts; one to issue the asset, one to distribute the asset.</li>
<li>To receive the asset, accounts must add a
<a href="https://developers.stellar.org/docs/issuing-assets/anatomy-of-an-asset/#trustlines">trustline</a>,
which is a way of saying &ldquo;I want to receive/hold this asset&rdquo;. So, your
distribution account must have this trustline established as well.</li>
<li>The issuer account sends a payment with the info that defines the asset to
the distribution account.</li>
<li>The distribution account distributes the asset to any account that have a
trustline.</li>
<li>That&rsquo;s it!</li>
</ol>

<p>As you can see, the process is fairly straightforward. One thing most people
don&rsquo;t understand is that, in practice, there&rsquo;s no &ldquo;create asset&rdquo; step.
Basically, anyone can send custom assets to anyone as long as the receiving
account have a trustline established.</p>

<p>Technically speaking, we don&rsquo;t really need to have two separate accounts (issuer
and distribution), but that&rsquo;s a good rule to live by. Sometimes, it&rsquo;s also a
requirement; maybe you want to issue a limited number of assets and, in this
case, you&rsquo;ll need to set the issuer&rsquo;s signature weight to zero, locking the
account and preventing any additional issuance.</p>

<p>The easiest way of issuing a new asset is by using Stellar&rsquo;s
<a href="https://laboratory.stellar.org">Laboratory</a>. For the purpose of this article,
we&rsquo;ll create the asset on the Testnet, so you don&rsquo;t need to have any Stellar
Lumens (XLM). To create an asset on the public network, the process is virtually
the same; just grab some Stellar Lumens (XLM) required to create the accounts
and establish the trustlines.</p>
<h3 tabindex="-1" id="setting-up-accounts">Setting up accounts<a class="anchor" href="#setting-up-accounts" aria-hidden="true" tabindex="-1"></a>
</h3>
<p>First, let&rsquo;s create the issuer account (i.e. the account that &ldquo;owns&rdquo; the asset).
Visit <a href="https://laboratory.stellar.org">https://laboratory.stellar.org</a> and make sure you have the Testnet
selected. Under &ldquo;Create Account&rdquo;, hit the &ldquo;Generate keypair&rdquo; button to create a
new Stellar account. It&rsquo;s your responsibility to keep your private key safe, so
use something like a password manager.</p>

<p>Copy the public key and paste it under the Friendbot section, then hit the &ldquo;Get
test network lumens&rdquo; button. This step will effectively create your account on
the Stellar network.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/stellar-asset/create-issuer-account.png" alt="Create the issuer account"></p>

<p>If you visit
<code>https://horizon-testnet.stellar.org/accounts/GAGGA4J6MH6JUBUVAUELGVNR7HNH3BWSN7GJHYJUFZPS655TNOCXDIST</code>,
you can see that the account now exists on the network. Alternatively, you can
use the &ldquo;Explore Endpoints&rdquo; tab of the Laboratory.</p>

<p><em>Notice that the testnet is reset from time to time, so you may receive a page
not found back if you visit any of the urls mentioned in this article.</em></p>

<p>Repeat the same process, now for your distribution account.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/stellar-asset/create-dist-account.png" alt="Create the distribution account"></p>

<p>Again, you can check that the account exists on the network by visiting
<code>https://horizon-testnet.stellar.org/accounts/GBNXJNCPQQZPWRRSMUEXGNTIOZU7FBDVT62WHA6WM6VOFW3YE56ISSUE</code>.</p>

<p>Now that both accounts have been created, we need to make sure the distribution
account is ready to receive
<code>ACME:GBNXJNCPQQZPWRRSMUEXGNTIOZU7FBDVT62WHA6WM6VOFW3YE56ISSUE</code>. This is done by
establishing a <em>trustline</em>.</p>
<h3 tabindex="-1" id="understanding-trustlines">Understanding Trustlines<a class="anchor" href="#understanding-trustlines" aria-hidden="true" tabindex="-1"></a>
</h3>
<p><a href="https://developers.stellar.org/docs/issuing-assets/anatomy-of-an-asset/#trustlines">Trustline</a>
is simply a mechanism to say that an account can receive an asset. By default,
all Stellar accounts can only receive Stellar Lumens (XLM), the <em>native</em> asset.
Any other asset will require a trustline. Given that trustlines are opt-in,
Stellar accounts won&rsquo;t be filled with spam transactions for assets that won&rsquo;t
ever be used.</p>

<p>Each trustline increases the required account balance by one
<a href="https://developers.stellar.org/docs/glossary/minimum-balance/">base reserve</a>,
currently set to 0.5 XLM. This means that any account will need two base
reserves (required to create the account on the network) plus 0.5 XLM for each
trustline. There are other things that may increase the minimum balance required
for an account, so make sure you read the
<a href="https://developers.stellar.org/docs/glossary/minimum-balance/">Minimum Balance</a>
documentation.</p>
<h4 tabindex="-1" id="establishing-trustlines">Establishing trustlines<a class="anchor" href="#establishing-trustlines" aria-hidden="true" tabindex="-1"></a>
</h4>
<p>To receive the asset
<code>ACME:GBNXJNCPQQZPWRRSMUEXGNTIOZU7FBDVT62WHA6WM6VOFW3YE56ISSUE</code>, users will need
a trustline. Go to Laboratory&rsquo;s
<a href="https://laboratory.stellar.org/#txbuilder?network=test">Build Transaction</a> and
paste the account&rsquo;s public key. In this case, we&rsquo;re going to set up the
distribution account <code>GAGGA4J6MH6JUBUVAUELGVNR7HNH3BWSN7GJHYJUFZPS655TNOCXDIST</code>.</p>

<ul>
<li>Paste the public key under &ldquo;Source Account&rdquo;, then click &ldquo;Fetch next sequence
number for account starting with&hellip;&rdquo;.</li>
<li>Add a &ldquo;Change Trust&rdquo; operation to establish the trustline. Select
&ldquo;Alphanumeric 4&rdquo; and use <code>ACME</code> as the asset code and
<code>GBNXJNCPQQZPWRRSMUEXGNTIOZU7FBDVT62WHA6WM6VOFW3YE56ISSUE</code> as the issuer
account id. If you don&rsquo;t set a limit, then the maximum limit will be used
instead.</li>
<li>When you&rsquo;re done, click &ldquo;Sign in Transaction Signer&rdquo;.</li>
</ul>

<p><img src="https://nandovieira.s3.amazonaws.com/media/stellar-asset/dist-add-trustline.png" alt="Adding trustline to distribution account"></p>

<p>You&rsquo;ll be redirected to the Transaction Signer. Paste your private key (the one
that starts with a <code>S</code>) and click &ldquo;Submit in Transaction Submitter&rdquo;.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/stellar-asset/dist-sign-trustline-transaction.png" alt="Signing trustline transaction for distribution account"></p>

<p>Now, you&rsquo;ll be able to review the transaction. To submit it, click &ldquo;Submit
Transaction&rdquo;.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/stellar-asset/dist-submit-trustline-transaction.png" alt="Submit trustline transaction from distribution account"></p>

<p>The distribution account is all set up to receive
<code>ACME:GBNXJNCPQQZPWRRSMUEXGNTIOZU7FBDVT62WHA6WM6VOFW3YE56ISSUE</code>. Now, all we
need to do is sending some ACMEs from the issuer account to the distribution
account.</p>

<p>Go to the tab &ldquo;Build Transaction&rdquo; and paste the issuer account&rsquo;s public key
(<code>GBNXJNCPQQZPWRRSMUEXGNTIOZU7FBDVT62WHA6WM6VOFW3YE56ISSUE</code>), then click &ldquo;Fetch
next sequence number for account starting with&hellip;&rdquo;. Click &ldquo;Sign in Transaction
Signer&rdquo;.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/stellar-asset/issuer-payment-transaction.png" alt="Send payment from issuer to distribution account"></p>

<p>Paste the issuer&rsquo;s private key, then click &ldquo;Submit in Transaction Submitter&rdquo;.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/stellar-asset/issuer-sign-payment-transaction.png" alt="Sign payment transaction from issuer to distribution account"></p>

<p>Finally, submit the transaction.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/stellar-asset/issuer-submit-payment-transaction.png" alt="Submit payment transaction from issuer to distribution account"></p>

<p>That&rsquo;s it! If you visit
<code>https://horizon-testnet.stellar.org/accounts/GAGGA4J6MH6JUBUVAUELGVNR7HNH3BWSN7GJHYJUFZPS655TNOCXDIST</code>
you can see that the account now have an entry under &ldquo;balances&rdquo; with 1,000,000
ACME.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/stellar-asset/dist-account-with-acme-balance.png" alt="Distribution account with 1,000,000 ACME"></p>

<p>Anyone willing to receive this asset would have to go through this process and
establish a trustline. Some wallets like <a href="https://lobstr.co">LOBSTR</a> abstracts
this process, making it very user friendly.</p>

<p>Exercise: Create another account, and send some tokens from the distribution
account to it. Remember, the account needs to establish a trustline to
<code>ACME:GBNXJNCPQQZPWRRSMUEXGNTIOZU7FBDVT62WHA6WM6VOFW3YE56ISSUE</code>.</p>
<h2 tabindex="-1" id="wrapping-up">Wrapping up<a class="anchor" href="#wrapping-up" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>As you can see, creating new assets on the Stellar network is a very simple
process. Obviously, there are other aspects you need to consider, like how users
will redeem their assets in the real world, but that&rsquo;s more of an operational
process, and even more important than issuing the assets on the Stellar network.</p>

<p>Stellar is one of the most developer-friendly blockchains out there. It&rsquo;s fast,
and very cheap, especially when compared to networks like Ethereum or Bitcoin.</p>

<p>Make sure you check out the
<a href="https://developers.stellar.org/docs/">official documentation</a>, where you can
find more info about the Stellar network and its features.</p>
]]>
      </description>
      <content:encoded>
        <![CDATA[<p><img src="https://s3.amazonaws.com/nandovieira/media/stellar-asset/stellar-logo.svg" alt="Stellar" class="align-right transparent" /></p>

<p>If you&rsquo;re watching the crypto space, you probably know about Stellar.
<a href="https://stellar.org">Stellar</a> is an open network for storing and moving money.
You can easily create, send and trade digital representations of all forms of
money by using a feature called
<a href="https://developers.stellar.org/docs/glossary/assets/">assets</a>.</p>

<p>Assets are, essentially, custom tokens. Any person can create an asset in just a
few steps, and in this article we&rsquo;ll create one called <code>ACME</code>. Let&rsquo;s get this
started!</p>
<h2 tabindex="-1" id="understanding-assets">Understanding assets<a class="anchor" href="#understanding-assets" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>Assets are identified by a code and a Stellar account public key. This
identifier is what you use to look up or interact with it on the Stellar
network. An asset identifier must be unique (i.e. both code and public key must
be unique); you can have multiple assets representing US dollars or Bitcoins
using the same code (<code>USD</code> and <code>BTC</code>), but not the same code plus public key.</p>

<p>Let&rsquo;s say both Binance and Coinbase want to issue Bitcoin assets on the Stellar
network; Binance&rsquo;s asset could be something like <code>BTC:GAAA...BINANCE</code>, and
Coinbase&rsquo;s asset could be <code>BTC:GAAA...COINBASE</code>.</p>

<p>Ultimately, is up to the user to decide which representation is more
trustworthy, but a product/service can also make a decision for users based on
partnerships; <a href="https://vibrantapp.com">Vibrant</a> uses
<a href="https://stablex.org">Stablex</a>&rsquo;s ARST for Argentine Pesos and
<a href="https://www.circle.com/">Circle</a>&rsquo;s USDC for US Dollar.</p>

<p>An asset is often used to represent something outside Stellar&rsquo;s network like
fiat currency, bonds, gold, etc, but it can be used to represent pretty much
anything, like other crypto currencies, reward points or karma (like in Reddit).</p>
<h2 tabindex="-1" id="creating-your-custom-asset">Creating your custom asset<a class="anchor" href="#creating-your-custom-asset" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>The process of creating a custom asset is fairly simple.</p>

<ol>
<li>You need two accounts; one to issue the asset, one to distribute the asset.</li>
<li>To receive the asset, accounts must add a
<a href="https://developers.stellar.org/docs/issuing-assets/anatomy-of-an-asset/#trustlines">trustline</a>,
which is a way of saying &ldquo;I want to receive/hold this asset&rdquo;. So, your
distribution account must have this trustline established as well.</li>
<li>The issuer account sends a payment with the info that defines the asset to
the distribution account.</li>
<li>The distribution account distributes the asset to any account that have a
trustline.</li>
<li>That&rsquo;s it!</li>
</ol>

<p>As you can see, the process is fairly straightforward. One thing most people
don&rsquo;t understand is that, in practice, there&rsquo;s no &ldquo;create asset&rdquo; step.
Basically, anyone can send custom assets to anyone as long as the receiving
account have a trustline established.</p>

<p>Technically speaking, we don&rsquo;t really need to have two separate accounts (issuer
and distribution), but that&rsquo;s a good rule to live by. Sometimes, it&rsquo;s also a
requirement; maybe you want to issue a limited number of assets and, in this
case, you&rsquo;ll need to set the issuer&rsquo;s signature weight to zero, locking the
account and preventing any additional issuance.</p>

<p>The easiest way of issuing a new asset is by using Stellar&rsquo;s
<a href="https://laboratory.stellar.org">Laboratory</a>. For the purpose of this article,
we&rsquo;ll create the asset on the Testnet, so you don&rsquo;t need to have any Stellar
Lumens (XLM). To create an asset on the public network, the process is virtually
the same; just grab some Stellar Lumens (XLM) required to create the accounts
and establish the trustlines.</p>
<h3 tabindex="-1" id="setting-up-accounts">Setting up accounts<a class="anchor" href="#setting-up-accounts" aria-hidden="true" tabindex="-1"></a>
</h3>
<p>First, let&rsquo;s create the issuer account (i.e. the account that &ldquo;owns&rdquo; the asset).
Visit <a href="https://laboratory.stellar.org">https://laboratory.stellar.org</a> and make sure you have the Testnet
selected. Under &ldquo;Create Account&rdquo;, hit the &ldquo;Generate keypair&rdquo; button to create a
new Stellar account. It&rsquo;s your responsibility to keep your private key safe, so
use something like a password manager.</p>

<p>Copy the public key and paste it under the Friendbot section, then hit the &ldquo;Get
test network lumens&rdquo; button. This step will effectively create your account on
the Stellar network.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/stellar-asset/create-issuer-account.png" alt="Create the issuer account"></p>

<p>If you visit
<code>https://horizon-testnet.stellar.org/accounts/GAGGA4J6MH6JUBUVAUELGVNR7HNH3BWSN7GJHYJUFZPS655TNOCXDIST</code>,
you can see that the account now exists on the network. Alternatively, you can
use the &ldquo;Explore Endpoints&rdquo; tab of the Laboratory.</p>

<p><em>Notice that the testnet is reset from time to time, so you may receive a page
not found back if you visit any of the urls mentioned in this article.</em></p>

<p>Repeat the same process, now for your distribution account.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/stellar-asset/create-dist-account.png" alt="Create the distribution account"></p>

<p>Again, you can check that the account exists on the network by visiting
<code>https://horizon-testnet.stellar.org/accounts/GBNXJNCPQQZPWRRSMUEXGNTIOZU7FBDVT62WHA6WM6VOFW3YE56ISSUE</code>.</p>

<p>Now that both accounts have been created, we need to make sure the distribution
account is ready to receive
<code>ACME:GBNXJNCPQQZPWRRSMUEXGNTIOZU7FBDVT62WHA6WM6VOFW3YE56ISSUE</code>. This is done by
establishing a <em>trustline</em>.</p>
<h3 tabindex="-1" id="understanding-trustlines">Understanding Trustlines<a class="anchor" href="#understanding-trustlines" aria-hidden="true" tabindex="-1"></a>
</h3>
<p><a href="https://developers.stellar.org/docs/issuing-assets/anatomy-of-an-asset/#trustlines">Trustline</a>
is simply a mechanism to say that an account can receive an asset. By default,
all Stellar accounts can only receive Stellar Lumens (XLM), the <em>native</em> asset.
Any other asset will require a trustline. Given that trustlines are opt-in,
Stellar accounts won&rsquo;t be filled with spam transactions for assets that won&rsquo;t
ever be used.</p>

<p>Each trustline increases the required account balance by one
<a href="https://developers.stellar.org/docs/glossary/minimum-balance/">base reserve</a>,
currently set to 0.5 XLM. This means that any account will need two base
reserves (required to create the account on the network) plus 0.5 XLM for each
trustline. There are other things that may increase the minimum balance required
for an account, so make sure you read the
<a href="https://developers.stellar.org/docs/glossary/minimum-balance/">Minimum Balance</a>
documentation.</p>
<h4 tabindex="-1" id="establishing-trustlines">Establishing trustlines<a class="anchor" href="#establishing-trustlines" aria-hidden="true" tabindex="-1"></a>
</h4>
<p>To receive the asset
<code>ACME:GBNXJNCPQQZPWRRSMUEXGNTIOZU7FBDVT62WHA6WM6VOFW3YE56ISSUE</code>, users will need
a trustline. Go to Laboratory&rsquo;s
<a href="https://laboratory.stellar.org/#txbuilder?network=test">Build Transaction</a> and
paste the account&rsquo;s public key. In this case, we&rsquo;re going to set up the
distribution account <code>GAGGA4J6MH6JUBUVAUELGVNR7HNH3BWSN7GJHYJUFZPS655TNOCXDIST</code>.</p>

<ul>
<li>Paste the public key under &ldquo;Source Account&rdquo;, then click &ldquo;Fetch next sequence
number for account starting with&hellip;&rdquo;.</li>
<li>Add a &ldquo;Change Trust&rdquo; operation to establish the trustline. Select
&ldquo;Alphanumeric 4&rdquo; and use <code>ACME</code> as the asset code and
<code>GBNXJNCPQQZPWRRSMUEXGNTIOZU7FBDVT62WHA6WM6VOFW3YE56ISSUE</code> as the issuer
account id. If you don&rsquo;t set a limit, then the maximum limit will be used
instead.</li>
<li>When you&rsquo;re done, click &ldquo;Sign in Transaction Signer&rdquo;.</li>
</ul>

<p><img src="https://nandovieira.s3.amazonaws.com/media/stellar-asset/dist-add-trustline.png" alt="Adding trustline to distribution account"></p>

<p>You&rsquo;ll be redirected to the Transaction Signer. Paste your private key (the one
that starts with a <code>S</code>) and click &ldquo;Submit in Transaction Submitter&rdquo;.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/stellar-asset/dist-sign-trustline-transaction.png" alt="Signing trustline transaction for distribution account"></p>

<p>Now, you&rsquo;ll be able to review the transaction. To submit it, click &ldquo;Submit
Transaction&rdquo;.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/stellar-asset/dist-submit-trustline-transaction.png" alt="Submit trustline transaction from distribution account"></p>

<p>The distribution account is all set up to receive
<code>ACME:GBNXJNCPQQZPWRRSMUEXGNTIOZU7FBDVT62WHA6WM6VOFW3YE56ISSUE</code>. Now, all we
need to do is sending some ACMEs from the issuer account to the distribution
account.</p>

<p>Go to the tab &ldquo;Build Transaction&rdquo; and paste the issuer account&rsquo;s public key
(<code>GBNXJNCPQQZPWRRSMUEXGNTIOZU7FBDVT62WHA6WM6VOFW3YE56ISSUE</code>), then click &ldquo;Fetch
next sequence number for account starting with&hellip;&rdquo;. Click &ldquo;Sign in Transaction
Signer&rdquo;.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/stellar-asset/issuer-payment-transaction.png" alt="Send payment from issuer to distribution account"></p>

<p>Paste the issuer&rsquo;s private key, then click &ldquo;Submit in Transaction Submitter&rdquo;.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/stellar-asset/issuer-sign-payment-transaction.png" alt="Sign payment transaction from issuer to distribution account"></p>

<p>Finally, submit the transaction.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/stellar-asset/issuer-submit-payment-transaction.png" alt="Submit payment transaction from issuer to distribution account"></p>

<p>That&rsquo;s it! If you visit
<code>https://horizon-testnet.stellar.org/accounts/GAGGA4J6MH6JUBUVAUELGVNR7HNH3BWSN7GJHYJUFZPS655TNOCXDIST</code>
you can see that the account now have an entry under &ldquo;balances&rdquo; with 1,000,000
ACME.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/stellar-asset/dist-account-with-acme-balance.png" alt="Distribution account with 1,000,000 ACME"></p>

<p>Anyone willing to receive this asset would have to go through this process and
establish a trustline. Some wallets like <a href="https://lobstr.co">LOBSTR</a> abstracts
this process, making it very user friendly.</p>

<p>Exercise: Create another account, and send some tokens from the distribution
account to it. Remember, the account needs to establish a trustline to
<code>ACME:GBNXJNCPQQZPWRRSMUEXGNTIOZU7FBDVT62WHA6WM6VOFW3YE56ISSUE</code>.</p>
<h2 tabindex="-1" id="wrapping-up">Wrapping up<a class="anchor" href="#wrapping-up" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>As you can see, creating new assets on the Stellar network is a very simple
process. Obviously, there are other aspects you need to consider, like how users
will redeem their assets in the real world, but that&rsquo;s more of an operational
process, and even more important than issuing the assets on the Stellar network.</p>

<p>Stellar is one of the most developer-friendly blockchains out there. It&rsquo;s fast,
and very cheap, especially when compared to networks like Ethereum or Bitcoin.</p>

<p>Make sure you check out the
<a href="https://developers.stellar.org/docs/">official documentation</a>, where you can
find more info about the Stellar network and its features.</p>
]]>
      </content:encoded>
      <link>https://nandovieira.com/creating-custom-assets-with-stellar</link>
      <guid>https://nandovieira.com/creating-custom-assets-with-stellar</guid>
      <pubDate>Wed, 17 Feb 2021 10:38:00 -0700</pubDate>
    </item>
    <item>
      <title>Using Let&amp;#39;s Encrypt in Development with NGINX and AWS Route 53</title>
      <description>
        <![CDATA[<p><img src="https://s3.amazonaws.com/nandovieira/media/letsencrypt/letsencrypt-logo.png" alt="Let's Encrypt" class="align-right transparent" /></p>

<p>I frequently see people struggling to set up <a href="https://en.wikipedia.org/wiki/HTTPS">HTTPS</a> in development. If you&rsquo;re a
long time developer, you may have done this in the past with self-signed
certificates, buying your own certificates and tweaking your hosts file, or
using tools like <a href="https://github.com/puma/puma-dev">puma-dev</a>. While these approaches work to an extent,
<a href="https://letsencrypt.org">Let&rsquo;s Encrypt</a> changed the game, at least for me.</p>

<p>With Let&rsquo;s Encrypt and a DNS provider like AWS Route 53, you&rsquo;ll be able to run
HTTPS with wildcard subdomains without having to mess with your <code>/etc/hosts</code>
file, or having to install tools that create a custom DNS resolver.</p>

<p>I&rsquo;m going to focus on macOS, my development environment, but you can pretty much
follow the same instructions everywhere. Just install the software dependencies
as needed.</p>
<h2 tabindex="-1" id="configuring-aws-route-53">Configuring AWS Route 53<a class="anchor" href="#configuring-aws-route-53" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>On the dashboard, select &ldquo;Route 53&rdquo; under &ldquo;Networking &amp; Content Delivery&rdquo;. You
can also type &ldquo;route 53&rdquo; on the search field. You&rsquo;ll be redirected to AWS Route
53&rsquo;s dashboard.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/opening-aws-route53.png" alt="AWS Console: Opening AWS Route 53"></p>

<p>Now, on the sidebar, click on &ldquo;Hosted Zones&rdquo;.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/aws-route53-hosted-zones-link.png" alt="AWS Route 53: Hosted Zones"></p>

<p>You&rsquo;ll need a domain for this, so you have a few options:</p>

<ol>
<li>Use a domain you already have and it&rsquo;s not being used (e.g. <code>fnando.com</code>)</li>
<li>Use a subdomain on a existing domain that&rsquo;s being used (e.g.
<code>dev.fnando.com</code>).</li>
<li>Buy a new domain, maybe one of those fancy <code>.dev</code>, which convey exactly what
you&rsquo;re doing (e.g. <code>fnando.dev</code>).</li>
</ol>

<p>I decided to buy yet another domain and went with option #3, just because it&rsquo;s
shorter, specially when doing wildcard domains (<code>something.dev.fnando.com</code> vs
<code>something.fnando.dev</code>). It looks nicer too! 🤓</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/aws-route53-new-hosted-zone.png" alt="AWS Route 53: Creating a new hosted zone"></p>

<p>Once you create your hosted zone, you have to configure your domain and point
its DNS to AWS Route 53. The hosts you&rsquo;ll need are defined under the record type
<code>NS</code>. Go to your domain provider and set this up. I use <a href="https://namecheap.com">Namecheap</a>, so this is
how you do it:</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/namecheap.png" alt="Namecheap dashboard: DNS records"></p>

<p>Back to AWS Route 53. Let&rsquo;s create two <code>A</code> records that point your DNS to your
development machine, in this case the loopback address <code>127.0.0.1</code>.</p>

<p>The first record will handle <code>fnando.dev</code>. Click on &ldquo;Create Record Set&rdquo;, choose
&ldquo;A - IPv4 address&rdquo; under the record type and set the value to <code>127.0.0.1</code>. Make
sure you don&rsquo;t type anything under the name; otherwise, you&rsquo;d be pointing a
subdomain instead.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/aws-route53-loopback-appex.png" alt="AWS Route 53: Adding type A records"></p>

<p>The second record will handle wildcard subdomains. Click on &ldquo;Create Record Set&rdquo;
once again, choose &ldquo;A - IPv4 address&rdquo;, but this time use <code>*</code> as the record name.
The value should be <code>127.0.0.1</code>, just like before.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/aws-route53-loopback-wildcard.png" alt="AWS Route 53: Adding type A records"></p>

<p>And the waiting game starts. You now have to wait until your DNS is propagated
completely, but that shouldn&rsquo;t take long. You can check it using <code>dig</code> on the
command-line.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>dig +short fnando.dev A
<span class="go">127.0.0.1

</span><span class="gp">$</span><span class="w"> </span>dig +short <span class="s1">'*.fnando.dev'</span> A
<span class="go">127.0.0.1
</span></code></pre></div>
<p>While you wait, you can set up a new AWS credential restricted to this domain.
Before we move on, look at your browser&rsquo;s url: you&rsquo;ll need the zone id, so copy
this value or write it down somewhere.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/aws-route53-zone-id.png" alt="AWS Route 53: Getting the zone id"></p>

<p>Now, let&rsquo;s create the user and a policy. This can be done on <a href="https://console.aws.amazon.com/iam/home">AWS IAM</a>,
so search for this option under the services menu.</p>

<p>On the sidebar, click on &ldquo;Policies&rdquo;, then &ldquo;Create Policy&rdquo;.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/aws-iam-new-policy.png" alt="AWS IAM: Creating a new policy"></p>

<p>Use the JSON below as your policy. Remember to replace <code>YOUR_ZONE_ID</code> with your
zone id.</p>
<div class="highlight"><pre class="highlight json"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"Version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2012-10-17"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"Id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"letsencrypt-mac policy"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"Statement"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"Effect"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Allow"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Action"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"route53:ListHostedZones"</span><span class="p">,</span><span class="w"> </span><span class="s2">"route53:GetChange"</span><span class="p">],</span><span class="w">
      </span><span class="nl">"Resource"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"*"</span><span class="p">]</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"Effect"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Allow"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Action"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"route53:ChangeResourceRecordSets"</span><span class="p">],</span><span class="w">
      </span><span class="nl">"Resource"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"arn:aws:route53:::hostedzone/YOUR_ZONE_ID"</span><span class="p">]</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div>
<p>Now, click on &ldquo;Review policy&rdquo;. Give it a recognizable name and click on &ldquo;Create
policy&rdquo;.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/aws-iam-policy-name.png" alt="AWS IAM: Specifying the policy name and type"></p>

<p>It&rsquo;s time to create a new user. On the sidebar, click on &ldquo;Users&rdquo; and then &ldquo;Add
User&rdquo;. Give it a name like <code>letsencrypt-mac</code>, or something that describes your
machine. You&rsquo;ll also have to select &ldquo;Programmatic Access&rdquo; under &ldquo;Access type&rdquo;.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/aws-iam-add-user.png" alt="AWS IAM: Adding a new user"></p>

<p>Click &ldquo;Next&rdquo;. Now we&rsquo;re going to select the policy we&rsquo;ve created a few steps
before. Click on &ldquo;Attach existing policies directly&rdquo; and search for your policy,
in this case <code>letsencrypt-mac</code>.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/aws-iam-attach-policy.png" alt="AWS IAM: Attaching the policy to the user"></p>

<p>Click &ldquo;Next: Tags&rdquo;, then &ldquo;Next: Review&rdquo;. Finally, click &ldquo;Create User&rdquo;.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/aws-iam-create-user.png" alt="AWS IAM: Creating the user"></p>

<p>This step is important: you&rsquo;ll be presented with your access and secret keys.
Save both of them somewhere safe, like your <a href="https://1password.com">password manager</a>.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/aws-iam-credentials.png" alt="AWS IAM: Credentials"></p>

<p>As far as AWS goes, you&rsquo;re all set up. Now, let&rsquo;s configure certbot, the
command-line interface that&rsquo;ll interact with Let&rsquo;s Encrypt.</p>
<h2 tabindex="-1" id="configuring-certbot">Configuring <code>certbot</code><a class="anchor" href="#configuring-certbot" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>Since I&rsquo;m a macOS user, I use <a href="https://brew.sh">homebrew</a>. To install <a href="https://certbot.eff.org">certbot</a>, run
<code>brew install certbot</code>. You can also find instruction for other system on
certbot&rsquo;s website.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>brew <span class="nb">install </span>certbot
<span class="gp">==&gt;</span><span class="w"> </span>Downloading https://homebrew.bintray.com/bottles/certbot-1.4.0.catalina.bottle.tar.gz
<span class="go">Already downloaded: /Users/fnando/Library/Caches/Homebrew/downloads/f25750b88db0e526ac7fce38587eade58ef847892744acd31091a7d8fa4cdda4--certbot-1.4.0.catalina.bottle.tar.gz
</span><span class="gp">==&gt;</span><span class="w"> </span>Pouring certbot-1.4.0.catalina.bottle.tar.gz
<span class="go">🍺  /usr/local/Cellar/certbot/1.4.0: 1,451 files, 12.5MB
</span></code></pre></div>
<p>To automatically issue certificates that are validated against AWS Route 53&rsquo;s
DNS, we need to install a certbot plugin called
<a href="https://github.com/certbot/certbot/tree/master/certbot-dns-route53"><code>certbot-dns-route53</code></a>. We can Python&rsquo;s <code>pip</code>.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>pip3 <span class="nb">install </span>certbot-dns-route53
<span class="c">...
</span><span class="go">Installing collected packages: setuptools, zope.interface, pycparser, cffi, six, cryptography, PyOpenSSL, josepy, pytz, pyrfc3339, urllib3, certifi, chardet, idna, requests, requests-toolbelt, acme, docutils, python-dateutil, jmespath, botocore, s3transfer, boto3, parsedatetime, configobj, ConfigArgParse, distro, zope.deprecation, zope.event, zope.hookable, zope.proxy, zope.deferredimport, zope.component, certbot, certbot-dns-route53
Successfully installed ConfigArgParse-1.2.3 PyOpenSSL-19.1.0 acme-1.4.0 boto3-1.13.16 botocore-1.16.16 certbot-1.4.0 certbot-dns-route53-1.4.0 certifi-2020.4.5.1 cffi-1.14.0 chardet-3.0.4 configobj-5.0.6 cryptography-2.9.2 distro-1.5.0 docutils-0.15.2 idna-2.9 jmespath-0.10.0 josepy-1.3.0 parsedatetime-2.5 pycparser-2.20 pyrfc3339-1.1 python-dateutil-2.8.1 pytz-2020.1 requests-2.23.0 requests-toolbelt-0.9.1 s3transfer-0.3.3 setuptools-46.4.0 six-1.15.0 urllib3-1.25.9 zope.component-4.6.1 zope.deferredimport-4.3.1 zope.deprecation-4.4.0 zope.event-4.4 zope.hookable-5.0.1 zope.interface-5.1.0 zope.proxy-4.3.5
</span></code></pre></div>
<p>You can verify that certbot can see the plugin by running <code>certbot plugins</code>.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>certbot plugins
<span class="go">
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* dns-route53
Description: Obtain certificates using a DNS TXT record (if you are using AWS
Route53 for DNS).
Interfaces: IAuthenticator, IPlugin
Entry point: dns-route53 =
certbot_dns_route53._internal.dns_route53:Authenticator

* standalone
Description: Spin up a temporary webserver
Interfaces: IAuthenticator, IPlugin
Entry point: standalone = certbot._internal.plugins.standalone:Authenticator

* webroot
Description: Place files in webroot directory
Interfaces: IAuthenticator, IPlugin
Entry point: webroot = certbot._internal.plugins.webroot:Authenticator
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
</span></code></pre></div>
<p>Now, let&rsquo;s generate the certificates. The first thing to know is that you need
to export your AWS credentials as <code>AWS_ACCESS_KEY_ID</code> and
<code>AWS_SECRET_ACCESS_KEY</code> environment variables. It&rsquo;s up to you how you want to
manage these variables. Personally, I like adding them to <code>~/.zsh/user.sh</code>,
which is then loaded by my <code>~/.zshrc</code> file. For this article, I&rsquo;ll just export
them before using them.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span><span class="nb">export </span><span class="nv">AWS_ACCESS_KEY_ID</span><span class="o">=</span>AKIAY7V6RRKVYA3Z4MGA
<span class="gp">$</span><span class="w"> </span><span class="nb">export </span><span class="nv">AWS_SECRET_ACCESS_KEY</span><span class="o">=</span><span class="s1">'REDACTED_SECRET'</span>
</code></pre></div>
<p>To generate a certificate, use the command <code>certbot certonly</code>. Notice that I&rsquo;m
specifying local directories; this is required if you don&rsquo;t want to use <code>sudo</code>.
After the process is complete, the certificate will be saved to
<code>~/local/letsencrypt/live/fnando.dev</code>. If you&rsquo;re not sure if everything is set
up accordingly, use the switch <code>--dry-run</code>; this will run certbot on their
staging environment, which has a higher limit for failures. In production, you
will be blocked from generating new certificates for a hour after a certain
number of failures.</p>
<div class="highlight"><pre class="highlight shell"><code>certbot certonly <span class="se">\</span>
<span class="nt">-n</span> <span class="se">\</span>
<span class="nt">--agree-tos</span> <span class="se">\</span>
<span class="nt">--email</span> user@example.com <span class="se">\</span>
<span class="nt">-d</span> fnando.dev <span class="se">\</span>
<span class="nt">-d</span> <span class="s1">'*.fnando.dev'</span> <span class="se">\</span>
<span class="nt">--dns-route53</span> <span class="se">\</span>
<span class="nt">--preferred-challenges</span><span class="o">=</span>dns <span class="se">\</span>
<span class="nt">--logs-dir</span> /tmp/letsencrypt <span class="se">\</span>
<span class="nt">--config-dir</span> ~/local/letsencrypt <span class="se">\</span>
<span class="nt">--work-dir</span> /tmp/letsencrypt
</code></pre></div>
<p>Once the command finishes running, you&rsquo;ll see something like this:</p>
<div class="highlight"><pre class="highlight console"><code><span class="go">Saving debug log to /tmp/letsencrypt/letsencrypt.log
Found credentials in environment variables.
Plugins selected: Authenticator dns-route53, Installer None
Obtaining a new certificate
Performing the following challenges:
dns-01 challenge for fnando.dev
dns-01 challenge for fnando.dev
Waiting for verification...
Cleaning up challenges
Non-standard path(s), might not work with crontab installed by your operating system package manager

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /Users/fnando/local/letsencrypt/live/fnando.dev/fullchain.pem
   Your key file has been saved at:
   /Users/fnando/local/letsencrypt/live/fnando.dev/privkey.pem
   Your cert will expire on 2020-08-23. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le
</span></code></pre></div>
<p>This is all we need to do. When your certificates are about to expire, you&rsquo;ll
receive an email from Let&rsquo;s Encrypt.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/letsencrypt-email.png" alt="Let&#39;s Encrypt email about the expiring certificate"></p>

<p>You&rsquo;ll receive an reminder to renew your certificates via email. To renew your
certificates, just run the same command above (i.e. <code>certbot certonly</code>).</p>
<div class="highlight"><pre class="highlight shell"><code>certbot certonly <span class="se">\</span>
<span class="nt">-d</span> fnando.dev <span class="se">\</span>
<span class="nt">-d</span> <span class="s1">'*.fnando.dev'</span> <span class="se">\</span>
<span class="nt">--dns-route53</span> <span class="se">\</span>
<span class="nt">--preferred-challenges</span><span class="o">=</span>dns <span class="se">\</span>
<span class="nt">--logs-dir</span> /tmp/letsencrypt <span class="se">\</span>
<span class="nt">--config-dir</span> ~/local/letsencrypt <span class="se">\</span>
<span class="nt">--work-dir</span> /tmp/letsencrypt
</code></pre></div><div class="highlight"><pre class="highlight console"><code><span class="go">Saving debug log to /tmp/letsencrypt.log
Plugins selected: Authenticator dns-route53, Installer None
Cert is due for renewal, auto-renewing...
Renewing an existing certificate
Performing the following challenges:
dns-01 challenge for fnando.dev
dns-01 challenge for fnando.dev
Waiting 30 seconds for DNS changes to propagate
Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /Users/fnando/local/letsencrypt/live/fnando.dev/fullchain.pem
   Your key file has been saved at:
   /Users/fnando/local/letsencrypt/live/fnando.dev/privkey.pem
   Your cert will expire on 2021-01-29. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le
</span></code></pre></div>
<p>Once it&rsquo;s done, remember to restart the webserver.</p>
<h2 tabindex="-1" id="configuring-nginx">Configuring NGINX<a class="anchor" href="#configuring-nginx" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>Once again, we can install NGINX using homebrew. Just run the command
<code>brew install nginx</code>.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>brew <span class="nb">install </span>nginx
<span class="gp">==&gt;</span><span class="w"> </span>Downloading https://homebrew.bintray.com/bottles/nginx-1.17.10.catalina.bottle.tar.gz
<span class="go">Already downloaded: /Users/fnando/Library/Caches/Homebrew/downloads/d7118d9cc53ef3be545ac049f7e50aa30f2378f673aa925702deaa6117fb403c--nginx-1.17.10.catalina.bottle.tar.gz
</span><span class="gp">==&gt;</span><span class="w"> </span>Pouring nginx-1.17.10.catalina.bottle.tar.gz
<span class="gp">==&gt;</span><span class="w"> </span>Caveats
<span class="go">Docroot is: /usr/local/var/www

The default port has been set in /usr/local/etc/nginx/nginx.conf to 8080 so that
nginx can run without sudo.

nginx will load all files in /usr/local/etc/nginx/servers/.

To have launchd start nginx now and restart at login:
  brew services start nginx
Or, if you don't want/need a background service you can just run:
  nginx
</span><span class="gp">==&gt;</span><span class="w"> </span>Summary
<span class="go">🍺  /usr/local/Cellar/nginx/1.17.10: 25 files, 2.1MB
</span></code></pre></div>
<p>Given that I develop web applications all the time, I like starting NGINX on
boot. To do it so, run the command <code>sudo brew services start nginx</code>, and
homebrew will take care of copying the launch file to
<code>/Library/LaunchDaemons/homebrew.mxcl.nginx.plist</code>.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span><span class="nb">sudo </span>brew services start nginx
<span class="go">Warning: Taking root:admin ownership of some nginx paths:
  /usr/local/Cellar/nginx/1.17.10/bin
  /usr/local/Cellar/nginx/1.17.10/bin/nginx
  /usr/local/opt/nginx
  /usr/local/opt/nginx/bin
  /usr/local/var/homebrew/linked/nginx
This will require manual removal of these paths using `sudo rm` on
brew upgrade/reinstall/uninstall.
Warning: nginx must be run as non-root to start at user login!
</span><span class="gp">==&gt;</span><span class="w"> </span>Successfully started <span class="sb">`</span>nginx<span class="sb">`</span> <span class="o">(</span>label: homebrew.mxcl.nginx<span class="o">)</span>
</code></pre></div>
<p>Yeah, I know&hellip; <code>sudo</code>. But that&rsquo;s how you can hit <code>https://fnando.dev</code> instead
of having to specify a non-privileged port. Another thing you could do is
setting up <code>/usr/local</code> permission to the group <code>admin</code>, so it&rsquo;s up to you.</p>

<p>Your NGINX configuration must be added to <code>/usr/local/etc/nginx/servers/</code>. I
like to use the apex domain name (i.e. the domain name without subdomain) as the
file name, so I&rsquo;m going to create a
<code>/usr/local/etc/nginx/servers/fnando.dev.conf</code> file with the content below.</p>
<div class="highlight"><pre class="highlight nginx"><code><span class="k">upstream</span> <span class="s">fnando_dev</span> <span class="p">{</span>
  <span class="kn">server</span> <span class="nf">127.0.0.1</span><span class="p">:</span><span class="mi">3000</span> <span class="s">max_fails=0</span><span class="p">;</span>
  <span class="kn">server</span> <span class="nf">127.0.0.1</span><span class="p">:</span><span class="mi">4567</span> <span class="s">max_fails=0</span><span class="p">;</span>
  <span class="kn">server</span> <span class="nf">127.0.0.1</span><span class="p">:</span><span class="mi">5000</span> <span class="s">max_fails=0</span><span class="p">;</span>
  <span class="kn">server</span> <span class="nf">127.0.0.1</span><span class="p">:</span><span class="mi">5001</span> <span class="s">max_fails=0</span><span class="p">;</span>
  <span class="kn">server</span> <span class="nf">127.0.0.1</span><span class="p">:</span><span class="mi">9292</span> <span class="s">max_fails=0</span><span class="p">;</span>
  <span class="kn">server</span> <span class="nf">127.0.0.1</span><span class="p">:</span><span class="mi">9393</span> <span class="s">max_fails=0</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">server</span> <span class="p">{</span>
  <span class="kn">listen</span>              <span class="mi">80</span><span class="p">;</span>
  <span class="kn">listen</span>              <span class="mi">443</span> <span class="s">ssl</span><span class="p">;</span>
  <span class="kn">server_name</span>         <span class="s">fnando.dev</span><span class="p">;</span>

  <span class="kn">ssl_certificate</span>     <span class="n">/Users/fnando/local/letsencrypt/live/fnando.dev/fullchain.pem</span><span class="p">;</span>
  <span class="kn">ssl_certificate_key</span> <span class="n">/Users/fnando/local/letsencrypt/live/fnando.dev/privkey.pem</span><span class="p">;</span>

  <span class="kn">ssl_protocols</span>       <span class="s">TLSv1</span> <span class="s">TLSv1.1</span> <span class="s">TLSv1.2</span><span class="p">;</span>
  <span class="kn">ssl_ciphers</span>         <span class="s">HIGH:!aNULL:!MD5</span><span class="p">;</span>

  <span class="kn">location</span> <span class="n">/</span> <span class="p">{</span>
    <span class="kn">proxy_set_header</span> <span class="s">X-Forwarded-For</span> <span class="nv">$proxy_add_x_forwarded_for</span><span class="p">;</span>
    <span class="kn">proxy_set_header</span> <span class="s">X-Forwarded-Proto</span> <span class="nv">$scheme</span><span class="p">;</span>
    <span class="kn">proxy_set_header</span> <span class="s">Host</span> <span class="nv">$http_host</span><span class="p">;</span>
    <span class="kn">proxy_redirect</span> <span class="no">off</span><span class="p">;</span>
    <span class="kn">proxy_pass</span> <span class="s">http://fnando_dev</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="k">server</span> <span class="p">{</span>
  <span class="kn">listen</span>              <span class="mi">80</span><span class="p">;</span>
  <span class="kn">listen</span>              <span class="mi">443</span> <span class="s">ssl</span><span class="p">;</span>
  <span class="kn">server_name</span>         <span class="s">*.fnando.dev</span><span class="p">;</span>

  <span class="kn">ssl_certificate</span>     <span class="n">/Users/fnando/local/letsencrypt/live/fnando.dev/fullchain.pem</span><span class="p">;</span>
  <span class="kn">ssl_certificate_key</span> <span class="n">/Users/fnando/local/letsencrypt/live/fnando.dev/privkey.pem</span><span class="p">;</span>

  <span class="kn">ssl_protocols</span>       <span class="s">TLSv1</span> <span class="s">TLSv1.1</span> <span class="s">TLSv1.2</span><span class="p">;</span>
  <span class="kn">ssl_ciphers</span>         <span class="s">HIGH:!aNULL:!MD5</span><span class="p">;</span>

  <span class="kn">location</span> <span class="n">/</span> <span class="p">{</span>
    <span class="kn">proxy_set_header</span> <span class="s">X-Forwarded-For</span> <span class="nv">$proxy_add_x_forwarded_for</span><span class="p">;</span>
    <span class="kn">proxy_set_header</span> <span class="s">X-Forwarded-Proto</span> <span class="nv">$scheme</span><span class="p">;</span>
    <span class="kn">proxy_set_header</span> <span class="s">Host</span> <span class="nv">$http_host</span><span class="p">;</span>
    <span class="kn">proxy_redirect</span> <span class="no">off</span><span class="p">;</span>
    <span class="kn">proxy_pass</span> <span class="s">http://fnando_dev</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>Notice that you cannot use <code>~</code> to indicate your home directory, so use the full
path instead. Another thing is that you can specify any number of servers on the
<a href="https://nginx.org/en/docs/http/ngx_http_upstream_module.html#upstream">upstream</a> statement, so make sure your framework&rsquo;s port is listed there.</p>

<p>Now, restart the server with <code>sudo brew services restart nginx</code>.</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ brew services restart nginx
Stopping `nginx`... (might take a while)
==&gt; Successfully stopped `nginx` (label: homebrew.mxcl.nginx)
==&gt; Successfully started `nginx` (label: homebrew.mxcl.nginx)
</code></pre></div>
<p>If you&rsquo;re all set up, you can hit your application on your custom domain, like
<code>https://fnando.dev</code>. To quickly test it, start your web application and hit
that url.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/safari.png" alt="Sample app running on HTTPS"></p>

<p>As you can see, this is a 100% valid certificate.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/safari-certificate.png" alt="Certificate information on Safari"></p>

<p>And subdomains work just fine. 😎</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/safari-subdomain.png" alt="Sample app running subdomains with HTTPS"></p>
<h2 tabindex="-1" id="wrapping-up">Wrapping up<a class="anchor" href="#wrapping-up" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>You may be wondering why would you develop using HTTPS, and the answer is that
many things require HTTPS, like <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API">webauthn</a>. You can check a full list of
features that require secure context on <a href="https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts/features_restricted_to_secure_contexts">Mozilla&rsquo;s website</a>.</p>

<p>I tried all sort of combinations in the past, but this is the best option by
far. No hacks, no crazy &ldquo;add certificate roots to keychain&rdquo; setups. Thanks,
Let&rsquo;s Encrypt.</p>
]]>
      </description>
      <content:encoded>
        <![CDATA[<p><img src="https://s3.amazonaws.com/nandovieira/media/letsencrypt/letsencrypt-logo.png" alt="Let's Encrypt" class="align-right transparent" /></p>

<p>I frequently see people struggling to set up <a href="https://en.wikipedia.org/wiki/HTTPS">HTTPS</a> in development. If you&rsquo;re a
long time developer, you may have done this in the past with self-signed
certificates, buying your own certificates and tweaking your hosts file, or
using tools like <a href="https://github.com/puma/puma-dev">puma-dev</a>. While these approaches work to an extent,
<a href="https://letsencrypt.org">Let&rsquo;s Encrypt</a> changed the game, at least for me.</p>

<p>With Let&rsquo;s Encrypt and a DNS provider like AWS Route 53, you&rsquo;ll be able to run
HTTPS with wildcard subdomains without having to mess with your <code>/etc/hosts</code>
file, or having to install tools that create a custom DNS resolver.</p>

<p>I&rsquo;m going to focus on macOS, my development environment, but you can pretty much
follow the same instructions everywhere. Just install the software dependencies
as needed.</p>
<h2 tabindex="-1" id="configuring-aws-route-53">Configuring AWS Route 53<a class="anchor" href="#configuring-aws-route-53" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>On the dashboard, select &ldquo;Route 53&rdquo; under &ldquo;Networking &amp; Content Delivery&rdquo;. You
can also type &ldquo;route 53&rdquo; on the search field. You&rsquo;ll be redirected to AWS Route
53&rsquo;s dashboard.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/opening-aws-route53.png" alt="AWS Console: Opening AWS Route 53"></p>

<p>Now, on the sidebar, click on &ldquo;Hosted Zones&rdquo;.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/aws-route53-hosted-zones-link.png" alt="AWS Route 53: Hosted Zones"></p>

<p>You&rsquo;ll need a domain for this, so you have a few options:</p>

<ol>
<li>Use a domain you already have and it&rsquo;s not being used (e.g. <code>fnando.com</code>)</li>
<li>Use a subdomain on a existing domain that&rsquo;s being used (e.g.
<code>dev.fnando.com</code>).</li>
<li>Buy a new domain, maybe one of those fancy <code>.dev</code>, which convey exactly what
you&rsquo;re doing (e.g. <code>fnando.dev</code>).</li>
</ol>

<p>I decided to buy yet another domain and went with option #3, just because it&rsquo;s
shorter, specially when doing wildcard domains (<code>something.dev.fnando.com</code> vs
<code>something.fnando.dev</code>). It looks nicer too! 🤓</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/aws-route53-new-hosted-zone.png" alt="AWS Route 53: Creating a new hosted zone"></p>

<p>Once you create your hosted zone, you have to configure your domain and point
its DNS to AWS Route 53. The hosts you&rsquo;ll need are defined under the record type
<code>NS</code>. Go to your domain provider and set this up. I use <a href="https://namecheap.com">Namecheap</a>, so this is
how you do it:</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/namecheap.png" alt="Namecheap dashboard: DNS records"></p>

<p>Back to AWS Route 53. Let&rsquo;s create two <code>A</code> records that point your DNS to your
development machine, in this case the loopback address <code>127.0.0.1</code>.</p>

<p>The first record will handle <code>fnando.dev</code>. Click on &ldquo;Create Record Set&rdquo;, choose
&ldquo;A - IPv4 address&rdquo; under the record type and set the value to <code>127.0.0.1</code>. Make
sure you don&rsquo;t type anything under the name; otherwise, you&rsquo;d be pointing a
subdomain instead.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/aws-route53-loopback-appex.png" alt="AWS Route 53: Adding type A records"></p>

<p>The second record will handle wildcard subdomains. Click on &ldquo;Create Record Set&rdquo;
once again, choose &ldquo;A - IPv4 address&rdquo;, but this time use <code>*</code> as the record name.
The value should be <code>127.0.0.1</code>, just like before.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/aws-route53-loopback-wildcard.png" alt="AWS Route 53: Adding type A records"></p>

<p>And the waiting game starts. You now have to wait until your DNS is propagated
completely, but that shouldn&rsquo;t take long. You can check it using <code>dig</code> on the
command-line.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>dig +short fnando.dev A
<span class="go">127.0.0.1

</span><span class="gp">$</span><span class="w"> </span>dig +short <span class="s1">'*.fnando.dev'</span> A
<span class="go">127.0.0.1
</span></code></pre></div>
<p>While you wait, you can set up a new AWS credential restricted to this domain.
Before we move on, look at your browser&rsquo;s url: you&rsquo;ll need the zone id, so copy
this value or write it down somewhere.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/aws-route53-zone-id.png" alt="AWS Route 53: Getting the zone id"></p>

<p>Now, let&rsquo;s create the user and a policy. This can be done on <a href="https://console.aws.amazon.com/iam/home">AWS IAM</a>,
so search for this option under the services menu.</p>

<p>On the sidebar, click on &ldquo;Policies&rdquo;, then &ldquo;Create Policy&rdquo;.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/aws-iam-new-policy.png" alt="AWS IAM: Creating a new policy"></p>

<p>Use the JSON below as your policy. Remember to replace <code>YOUR_ZONE_ID</code> with your
zone id.</p>
<div class="highlight"><pre class="highlight json"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"Version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2012-10-17"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"Id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"letsencrypt-mac policy"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"Statement"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"Effect"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Allow"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Action"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"route53:ListHostedZones"</span><span class="p">,</span><span class="w"> </span><span class="s2">"route53:GetChange"</span><span class="p">],</span><span class="w">
      </span><span class="nl">"Resource"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"*"</span><span class="p">]</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"Effect"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Allow"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Action"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"route53:ChangeResourceRecordSets"</span><span class="p">],</span><span class="w">
      </span><span class="nl">"Resource"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"arn:aws:route53:::hostedzone/YOUR_ZONE_ID"</span><span class="p">]</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div>
<p>Now, click on &ldquo;Review policy&rdquo;. Give it a recognizable name and click on &ldquo;Create
policy&rdquo;.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/aws-iam-policy-name.png" alt="AWS IAM: Specifying the policy name and type"></p>

<p>It&rsquo;s time to create a new user. On the sidebar, click on &ldquo;Users&rdquo; and then &ldquo;Add
User&rdquo;. Give it a name like <code>letsencrypt-mac</code>, or something that describes your
machine. You&rsquo;ll also have to select &ldquo;Programmatic Access&rdquo; under &ldquo;Access type&rdquo;.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/aws-iam-add-user.png" alt="AWS IAM: Adding a new user"></p>

<p>Click &ldquo;Next&rdquo;. Now we&rsquo;re going to select the policy we&rsquo;ve created a few steps
before. Click on &ldquo;Attach existing policies directly&rdquo; and search for your policy,
in this case <code>letsencrypt-mac</code>.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/aws-iam-attach-policy.png" alt="AWS IAM: Attaching the policy to the user"></p>

<p>Click &ldquo;Next: Tags&rdquo;, then &ldquo;Next: Review&rdquo;. Finally, click &ldquo;Create User&rdquo;.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/aws-iam-create-user.png" alt="AWS IAM: Creating the user"></p>

<p>This step is important: you&rsquo;ll be presented with your access and secret keys.
Save both of them somewhere safe, like your <a href="https://1password.com">password manager</a>.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/aws-iam-credentials.png" alt="AWS IAM: Credentials"></p>

<p>As far as AWS goes, you&rsquo;re all set up. Now, let&rsquo;s configure certbot, the
command-line interface that&rsquo;ll interact with Let&rsquo;s Encrypt.</p>
<h2 tabindex="-1" id="configuring-certbot">Configuring <code>certbot</code><a class="anchor" href="#configuring-certbot" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>Since I&rsquo;m a macOS user, I use <a href="https://brew.sh">homebrew</a>. To install <a href="https://certbot.eff.org">certbot</a>, run
<code>brew install certbot</code>. You can also find instruction for other system on
certbot&rsquo;s website.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>brew <span class="nb">install </span>certbot
<span class="gp">==&gt;</span><span class="w"> </span>Downloading https://homebrew.bintray.com/bottles/certbot-1.4.0.catalina.bottle.tar.gz
<span class="go">Already downloaded: /Users/fnando/Library/Caches/Homebrew/downloads/f25750b88db0e526ac7fce38587eade58ef847892744acd31091a7d8fa4cdda4--certbot-1.4.0.catalina.bottle.tar.gz
</span><span class="gp">==&gt;</span><span class="w"> </span>Pouring certbot-1.4.0.catalina.bottle.tar.gz
<span class="go">🍺  /usr/local/Cellar/certbot/1.4.0: 1,451 files, 12.5MB
</span></code></pre></div>
<p>To automatically issue certificates that are validated against AWS Route 53&rsquo;s
DNS, we need to install a certbot plugin called
<a href="https://github.com/certbot/certbot/tree/master/certbot-dns-route53"><code>certbot-dns-route53</code></a>. We can Python&rsquo;s <code>pip</code>.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>pip3 <span class="nb">install </span>certbot-dns-route53
<span class="c">...
</span><span class="go">Installing collected packages: setuptools, zope.interface, pycparser, cffi, six, cryptography, PyOpenSSL, josepy, pytz, pyrfc3339, urllib3, certifi, chardet, idna, requests, requests-toolbelt, acme, docutils, python-dateutil, jmespath, botocore, s3transfer, boto3, parsedatetime, configobj, ConfigArgParse, distro, zope.deprecation, zope.event, zope.hookable, zope.proxy, zope.deferredimport, zope.component, certbot, certbot-dns-route53
Successfully installed ConfigArgParse-1.2.3 PyOpenSSL-19.1.0 acme-1.4.0 boto3-1.13.16 botocore-1.16.16 certbot-1.4.0 certbot-dns-route53-1.4.0 certifi-2020.4.5.1 cffi-1.14.0 chardet-3.0.4 configobj-5.0.6 cryptography-2.9.2 distro-1.5.0 docutils-0.15.2 idna-2.9 jmespath-0.10.0 josepy-1.3.0 parsedatetime-2.5 pycparser-2.20 pyrfc3339-1.1 python-dateutil-2.8.1 pytz-2020.1 requests-2.23.0 requests-toolbelt-0.9.1 s3transfer-0.3.3 setuptools-46.4.0 six-1.15.0 urllib3-1.25.9 zope.component-4.6.1 zope.deferredimport-4.3.1 zope.deprecation-4.4.0 zope.event-4.4 zope.hookable-5.0.1 zope.interface-5.1.0 zope.proxy-4.3.5
</span></code></pre></div>
<p>You can verify that certbot can see the plugin by running <code>certbot plugins</code>.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>certbot plugins
<span class="go">
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* dns-route53
Description: Obtain certificates using a DNS TXT record (if you are using AWS
Route53 for DNS).
Interfaces: IAuthenticator, IPlugin
Entry point: dns-route53 =
certbot_dns_route53._internal.dns_route53:Authenticator

* standalone
Description: Spin up a temporary webserver
Interfaces: IAuthenticator, IPlugin
Entry point: standalone = certbot._internal.plugins.standalone:Authenticator

* webroot
Description: Place files in webroot directory
Interfaces: IAuthenticator, IPlugin
Entry point: webroot = certbot._internal.plugins.webroot:Authenticator
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
</span></code></pre></div>
<p>Now, let&rsquo;s generate the certificates. The first thing to know is that you need
to export your AWS credentials as <code>AWS_ACCESS_KEY_ID</code> and
<code>AWS_SECRET_ACCESS_KEY</code> environment variables. It&rsquo;s up to you how you want to
manage these variables. Personally, I like adding them to <code>~/.zsh/user.sh</code>,
which is then loaded by my <code>~/.zshrc</code> file. For this article, I&rsquo;ll just export
them before using them.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span><span class="nb">export </span><span class="nv">AWS_ACCESS_KEY_ID</span><span class="o">=</span>AKIAY7V6RRKVYA3Z4MGA
<span class="gp">$</span><span class="w"> </span><span class="nb">export </span><span class="nv">AWS_SECRET_ACCESS_KEY</span><span class="o">=</span><span class="s1">'REDACTED_SECRET'</span>
</code></pre></div>
<p>To generate a certificate, use the command <code>certbot certonly</code>. Notice that I&rsquo;m
specifying local directories; this is required if you don&rsquo;t want to use <code>sudo</code>.
After the process is complete, the certificate will be saved to
<code>~/local/letsencrypt/live/fnando.dev</code>. If you&rsquo;re not sure if everything is set
up accordingly, use the switch <code>--dry-run</code>; this will run certbot on their
staging environment, which has a higher limit for failures. In production, you
will be blocked from generating new certificates for a hour after a certain
number of failures.</p>
<div class="highlight"><pre class="highlight shell"><code>certbot certonly <span class="se">\</span>
<span class="nt">-n</span> <span class="se">\</span>
<span class="nt">--agree-tos</span> <span class="se">\</span>
<span class="nt">--email</span> user@example.com <span class="se">\</span>
<span class="nt">-d</span> fnando.dev <span class="se">\</span>
<span class="nt">-d</span> <span class="s1">'*.fnando.dev'</span> <span class="se">\</span>
<span class="nt">--dns-route53</span> <span class="se">\</span>
<span class="nt">--preferred-challenges</span><span class="o">=</span>dns <span class="se">\</span>
<span class="nt">--logs-dir</span> /tmp/letsencrypt <span class="se">\</span>
<span class="nt">--config-dir</span> ~/local/letsencrypt <span class="se">\</span>
<span class="nt">--work-dir</span> /tmp/letsencrypt
</code></pre></div>
<p>Once the command finishes running, you&rsquo;ll see something like this:</p>
<div class="highlight"><pre class="highlight console"><code><span class="go">Saving debug log to /tmp/letsencrypt/letsencrypt.log
Found credentials in environment variables.
Plugins selected: Authenticator dns-route53, Installer None
Obtaining a new certificate
Performing the following challenges:
dns-01 challenge for fnando.dev
dns-01 challenge for fnando.dev
Waiting for verification...
Cleaning up challenges
Non-standard path(s), might not work with crontab installed by your operating system package manager

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /Users/fnando/local/letsencrypt/live/fnando.dev/fullchain.pem
   Your key file has been saved at:
   /Users/fnando/local/letsencrypt/live/fnando.dev/privkey.pem
   Your cert will expire on 2020-08-23. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le
</span></code></pre></div>
<p>This is all we need to do. When your certificates are about to expire, you&rsquo;ll
receive an email from Let&rsquo;s Encrypt.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/letsencrypt-email.png" alt="Let&#39;s Encrypt email about the expiring certificate"></p>

<p>You&rsquo;ll receive an reminder to renew your certificates via email. To renew your
certificates, just run the same command above (i.e. <code>certbot certonly</code>).</p>
<div class="highlight"><pre class="highlight shell"><code>certbot certonly <span class="se">\</span>
<span class="nt">-d</span> fnando.dev <span class="se">\</span>
<span class="nt">-d</span> <span class="s1">'*.fnando.dev'</span> <span class="se">\</span>
<span class="nt">--dns-route53</span> <span class="se">\</span>
<span class="nt">--preferred-challenges</span><span class="o">=</span>dns <span class="se">\</span>
<span class="nt">--logs-dir</span> /tmp/letsencrypt <span class="se">\</span>
<span class="nt">--config-dir</span> ~/local/letsencrypt <span class="se">\</span>
<span class="nt">--work-dir</span> /tmp/letsencrypt
</code></pre></div><div class="highlight"><pre class="highlight console"><code><span class="go">Saving debug log to /tmp/letsencrypt.log
Plugins selected: Authenticator dns-route53, Installer None
Cert is due for renewal, auto-renewing...
Renewing an existing certificate
Performing the following challenges:
dns-01 challenge for fnando.dev
dns-01 challenge for fnando.dev
Waiting 30 seconds for DNS changes to propagate
Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /Users/fnando/local/letsencrypt/live/fnando.dev/fullchain.pem
   Your key file has been saved at:
   /Users/fnando/local/letsencrypt/live/fnando.dev/privkey.pem
   Your cert will expire on 2021-01-29. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le
</span></code></pre></div>
<p>Once it&rsquo;s done, remember to restart the webserver.</p>
<h2 tabindex="-1" id="configuring-nginx">Configuring NGINX<a class="anchor" href="#configuring-nginx" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>Once again, we can install NGINX using homebrew. Just run the command
<code>brew install nginx</code>.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>brew <span class="nb">install </span>nginx
<span class="gp">==&gt;</span><span class="w"> </span>Downloading https://homebrew.bintray.com/bottles/nginx-1.17.10.catalina.bottle.tar.gz
<span class="go">Already downloaded: /Users/fnando/Library/Caches/Homebrew/downloads/d7118d9cc53ef3be545ac049f7e50aa30f2378f673aa925702deaa6117fb403c--nginx-1.17.10.catalina.bottle.tar.gz
</span><span class="gp">==&gt;</span><span class="w"> </span>Pouring nginx-1.17.10.catalina.bottle.tar.gz
<span class="gp">==&gt;</span><span class="w"> </span>Caveats
<span class="go">Docroot is: /usr/local/var/www

The default port has been set in /usr/local/etc/nginx/nginx.conf to 8080 so that
nginx can run without sudo.

nginx will load all files in /usr/local/etc/nginx/servers/.

To have launchd start nginx now and restart at login:
  brew services start nginx
Or, if you don't want/need a background service you can just run:
  nginx
</span><span class="gp">==&gt;</span><span class="w"> </span>Summary
<span class="go">🍺  /usr/local/Cellar/nginx/1.17.10: 25 files, 2.1MB
</span></code></pre></div>
<p>Given that I develop web applications all the time, I like starting NGINX on
boot. To do it so, run the command <code>sudo brew services start nginx</code>, and
homebrew will take care of copying the launch file to
<code>/Library/LaunchDaemons/homebrew.mxcl.nginx.plist</code>.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span><span class="nb">sudo </span>brew services start nginx
<span class="go">Warning: Taking root:admin ownership of some nginx paths:
  /usr/local/Cellar/nginx/1.17.10/bin
  /usr/local/Cellar/nginx/1.17.10/bin/nginx
  /usr/local/opt/nginx
  /usr/local/opt/nginx/bin
  /usr/local/var/homebrew/linked/nginx
This will require manual removal of these paths using `sudo rm` on
brew upgrade/reinstall/uninstall.
Warning: nginx must be run as non-root to start at user login!
</span><span class="gp">==&gt;</span><span class="w"> </span>Successfully started <span class="sb">`</span>nginx<span class="sb">`</span> <span class="o">(</span>label: homebrew.mxcl.nginx<span class="o">)</span>
</code></pre></div>
<p>Yeah, I know&hellip; <code>sudo</code>. But that&rsquo;s how you can hit <code>https://fnando.dev</code> instead
of having to specify a non-privileged port. Another thing you could do is
setting up <code>/usr/local</code> permission to the group <code>admin</code>, so it&rsquo;s up to you.</p>

<p>Your NGINX configuration must be added to <code>/usr/local/etc/nginx/servers/</code>. I
like to use the apex domain name (i.e. the domain name without subdomain) as the
file name, so I&rsquo;m going to create a
<code>/usr/local/etc/nginx/servers/fnando.dev.conf</code> file with the content below.</p>
<div class="highlight"><pre class="highlight nginx"><code><span class="k">upstream</span> <span class="s">fnando_dev</span> <span class="p">{</span>
  <span class="kn">server</span> <span class="nf">127.0.0.1</span><span class="p">:</span><span class="mi">3000</span> <span class="s">max_fails=0</span><span class="p">;</span>
  <span class="kn">server</span> <span class="nf">127.0.0.1</span><span class="p">:</span><span class="mi">4567</span> <span class="s">max_fails=0</span><span class="p">;</span>
  <span class="kn">server</span> <span class="nf">127.0.0.1</span><span class="p">:</span><span class="mi">5000</span> <span class="s">max_fails=0</span><span class="p">;</span>
  <span class="kn">server</span> <span class="nf">127.0.0.1</span><span class="p">:</span><span class="mi">5001</span> <span class="s">max_fails=0</span><span class="p">;</span>
  <span class="kn">server</span> <span class="nf">127.0.0.1</span><span class="p">:</span><span class="mi">9292</span> <span class="s">max_fails=0</span><span class="p">;</span>
  <span class="kn">server</span> <span class="nf">127.0.0.1</span><span class="p">:</span><span class="mi">9393</span> <span class="s">max_fails=0</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">server</span> <span class="p">{</span>
  <span class="kn">listen</span>              <span class="mi">80</span><span class="p">;</span>
  <span class="kn">listen</span>              <span class="mi">443</span> <span class="s">ssl</span><span class="p">;</span>
  <span class="kn">server_name</span>         <span class="s">fnando.dev</span><span class="p">;</span>

  <span class="kn">ssl_certificate</span>     <span class="n">/Users/fnando/local/letsencrypt/live/fnando.dev/fullchain.pem</span><span class="p">;</span>
  <span class="kn">ssl_certificate_key</span> <span class="n">/Users/fnando/local/letsencrypt/live/fnando.dev/privkey.pem</span><span class="p">;</span>

  <span class="kn">ssl_protocols</span>       <span class="s">TLSv1</span> <span class="s">TLSv1.1</span> <span class="s">TLSv1.2</span><span class="p">;</span>
  <span class="kn">ssl_ciphers</span>         <span class="s">HIGH:!aNULL:!MD5</span><span class="p">;</span>

  <span class="kn">location</span> <span class="n">/</span> <span class="p">{</span>
    <span class="kn">proxy_set_header</span> <span class="s">X-Forwarded-For</span> <span class="nv">$proxy_add_x_forwarded_for</span><span class="p">;</span>
    <span class="kn">proxy_set_header</span> <span class="s">X-Forwarded-Proto</span> <span class="nv">$scheme</span><span class="p">;</span>
    <span class="kn">proxy_set_header</span> <span class="s">Host</span> <span class="nv">$http_host</span><span class="p">;</span>
    <span class="kn">proxy_redirect</span> <span class="no">off</span><span class="p">;</span>
    <span class="kn">proxy_pass</span> <span class="s">http://fnando_dev</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="k">server</span> <span class="p">{</span>
  <span class="kn">listen</span>              <span class="mi">80</span><span class="p">;</span>
  <span class="kn">listen</span>              <span class="mi">443</span> <span class="s">ssl</span><span class="p">;</span>
  <span class="kn">server_name</span>         <span class="s">*.fnando.dev</span><span class="p">;</span>

  <span class="kn">ssl_certificate</span>     <span class="n">/Users/fnando/local/letsencrypt/live/fnando.dev/fullchain.pem</span><span class="p">;</span>
  <span class="kn">ssl_certificate_key</span> <span class="n">/Users/fnando/local/letsencrypt/live/fnando.dev/privkey.pem</span><span class="p">;</span>

  <span class="kn">ssl_protocols</span>       <span class="s">TLSv1</span> <span class="s">TLSv1.1</span> <span class="s">TLSv1.2</span><span class="p">;</span>
  <span class="kn">ssl_ciphers</span>         <span class="s">HIGH:!aNULL:!MD5</span><span class="p">;</span>

  <span class="kn">location</span> <span class="n">/</span> <span class="p">{</span>
    <span class="kn">proxy_set_header</span> <span class="s">X-Forwarded-For</span> <span class="nv">$proxy_add_x_forwarded_for</span><span class="p">;</span>
    <span class="kn">proxy_set_header</span> <span class="s">X-Forwarded-Proto</span> <span class="nv">$scheme</span><span class="p">;</span>
    <span class="kn">proxy_set_header</span> <span class="s">Host</span> <span class="nv">$http_host</span><span class="p">;</span>
    <span class="kn">proxy_redirect</span> <span class="no">off</span><span class="p">;</span>
    <span class="kn">proxy_pass</span> <span class="s">http://fnando_dev</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>Notice that you cannot use <code>~</code> to indicate your home directory, so use the full
path instead. Another thing is that you can specify any number of servers on the
<a href="https://nginx.org/en/docs/http/ngx_http_upstream_module.html#upstream">upstream</a> statement, so make sure your framework&rsquo;s port is listed there.</p>

<p>Now, restart the server with <code>sudo brew services restart nginx</code>.</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ brew services restart nginx
Stopping `nginx`... (might take a while)
==&gt; Successfully stopped `nginx` (label: homebrew.mxcl.nginx)
==&gt; Successfully started `nginx` (label: homebrew.mxcl.nginx)
</code></pre></div>
<p>If you&rsquo;re all set up, you can hit your application on your custom domain, like
<code>https://fnando.dev</code>. To quickly test it, start your web application and hit
that url.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/safari.png" alt="Sample app running on HTTPS"></p>

<p>As you can see, this is a 100% valid certificate.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/safari-certificate.png" alt="Certificate information on Safari"></p>

<p>And subdomains work just fine. 😎</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/letsencrypt/safari-subdomain.png" alt="Sample app running subdomains with HTTPS"></p>
<h2 tabindex="-1" id="wrapping-up">Wrapping up<a class="anchor" href="#wrapping-up" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>You may be wondering why would you develop using HTTPS, and the answer is that
many things require HTTPS, like <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API">webauthn</a>. You can check a full list of
features that require secure context on <a href="https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts/features_restricted_to_secure_contexts">Mozilla&rsquo;s website</a>.</p>

<p>I tried all sort of combinations in the past, but this is the best option by
far. No hacks, no crazy &ldquo;add certificate roots to keychain&rdquo; setups. Thanks,
Let&rsquo;s Encrypt.</p>
]]>
      </content:encoded>
      <link>https://nandovieira.com/using-lets-encrypt-in-development-with-nginx-and-aws-route53</link>
      <guid>https://nandovieira.com/using-lets-encrypt-in-development-with-nginx-and-aws-route53</guid>
      <pubDate>Mon, 25 May 2020 16:27:00 -0700</pubDate>
    </item>
    <item>
      <title>Supporting dark mode in web content</title>
      <description>
        <![CDATA[<p>Welcome to 2019, the year that the technology went dark mode. Both <a href="https://www.android.com/android-10/">Android
10</a> and <a href="https://www.apple.com/ios/ios-13/">iOS 13</a> were released last month, and the most
acclaimed feature was dark mode. Native apps can now implement specific themes
for each mode (i.e. dark, light), but can you do it on the web? This article
outlines how you can take advantage of this new trend and make everyone happy.</p>
<h2 tabindex="-1" id="are-there-any-benefits-on-using-dark-mode">Are there any benefits on using dark mode?<a class="anchor" href="#are-there-any-benefits-on-using-dark-mode" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>When you read about dark mode, you often see &ldquo;easy on the eyes&rdquo; or &ldquo;energy
efficiency&rdquo; being mentioned. Truth is, to be energy efficient you need
<a href="https://en.wikipedia.org/wiki/OLED">OLED</a>/<a href="https://en.wikipedia.org/wiki/AMOLED">AMOLED</a> screens which are only available on newer high-end
devices.</p>

<ul>
<li><a href="https://support.apple.com/kb/sp770">iPhone X</a> or newer.</li>
<li><a href="https://www.samsung.com/global/galaxy/galaxy-s10/specs/">Samsung Galaxy S10</a> or newer.</li>
<li><a href="https://store.google.com/product/pixel_3_specs">Google Pixel 3</a> or newer.</li>
<li><a href="https://consumer.huawei.com/en/phones/p30/specs/">Huawei P30</a><a href="https://consumer.huawei.com/en/phones/p30/specs/">huawei-p30</a> or newer.</li>
</ul>

<p>If you&rsquo;re using LCD or another type of screen, changing colors won’t do much for
your battery life. Most people won&rsquo;t benefit from the energy efficiency aspect
of dark modes at all.</p>

<p>But what about health? It certainly is better, right? Well… unfortunately, we
still don&rsquo;t have enough scientific data to assert that dark mode is better. What
we know for sure is that the time we spend looking at screens is the main factor
to eye strain. The less, the better.</p>

<p>Some doctors also mention that a higher contrast (either way) is more important
than using either mode. And some doctors mention that dark mode can be more
damaging for people with astigmatism.</p>

<p>I personally don&rsquo;t like dark mode at all when using dark background with light
text (i.e. black background with white text); I have that annoying feeling of
burnt image that takes a minute or so to disappear. And the opposite is also
true for some people. And without serious researches about this subject we can
only talk by experience.</p>

<p>Now, I think the biggest advantage of dark mode is when you&rsquo;re using your phone
in a low light room, but you would be better off not using your phone right
before going to sleep anyway, right?</p>
<h2 tabindex="-1" id="browse-support">Browse support<a class="anchor" href="#browse-support" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>How well supported is dark mode on today&rsquo;s browsers? To detect whether a browser
is running on a dark mode device or not, you&rsquo;ll need support for
<a href="https://drafts.csswg.org/mediaqueries-5/#prefers-color-scheme"><code>(prefers-color-scheme: dark)</code></a> media query,
available on the following devices.</p>

<ul>
<li>macOS Safari 13 or newer.</li>
<li>iOS Safari 13.2 or newer.</li>
<li>Android Browser 76 or newer.</li>
<li>Chrome for Android 78 or newer.</li>
<li>Firefox for Android 68 or newer.</li>
<li>Opera 62 or newer.</li>
<li>Chrome 76 or newer.</li>
<li>Firefox 67 or newer.</li>
<li>Microsoft Edge 76 or newer.</li>
</ul>

<p>Yeah, I know… this is stupid versioning in action and I shouldn&rsquo;t have added
them anyway. What you need to know is that Safari 13 is very, very new. And so
is the support for this media query on every other browser. From the business
perspective, it doesn&rsquo;t make sense to spend your designer&rsquo;s time on this task,
but you&rsquo;re going to do it anyway, aren&rsquo;t you? In this case, this is how you can
define the CSS.</p>
<div class="highlight"><pre class="highlight css"><code><span class="nt">body</span> <span class="p">{</span>
  <span class="nl">color</span><span class="p">:</span> <span class="nx">#333</span><span class="p">;</span>
  <span class="nl">background</span><span class="p">:</span> <span class="nx">#fff</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">@media</span> <span class="nb">screen</span> <span class="n">and</span> <span class="p">(</span><span class="n">prefers-color-scheme</span><span class="p">:</span> <span class="n">dark</span><span class="p">)</span> <span class="p">{</span>
  <span class="nt">body</span> <span class="p">{</span>
    <span class="nl">background</span><span class="p">:</span> <span class="nx">#2d3239</span><span class="p">;</span>
    <span class="nl">color</span><span class="p">:</span> <span class="nx">#75715e</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="nt">h1</span> <span class="p">{</span>
    <span class="nl">color</span><span class="p">:</span> <span class="nx">#e9d970</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>You can easily alternate between light and dark mode using Safari&rsquo;s Web
Inspector. I couldn&rsquo;t find such option on Chrome yet, but we&rsquo;ll have something
similar sooner or later. <a href="https://codepen.io/fnando/full/XWWzdbL">This example can be found here.</a></p>

<video loop controls width="978" height="754" poster="https://nandovieira.s3.amazonaws.com/media/dark-mode/safari-dark-mode-inspector.jpg">
  <source src="https://nandovieira.s3.amazonaws.com/media/dark-mode/safari-dark-mode-inspector.mp4" type="video/mp4">
  <source src="https://nandovieira.s3.amazonaws.com/media/dark-mode/safari-dark-mode-inspector.webm" type="video/webm">
</video>

<p>I know what you&rsquo;re thinking… the CSS can easily get out of hand. And you&rsquo;re
right! Unless you use variables. More specifically, <a href="https://drafts.csswg.org/css-variables/">CSS
variables</a>.</p>
<h2 tabindex="-1" id="creating-manageable-themes-with-css-variables">Creating manageable themes with CSS variables<a class="anchor" href="#creating-manageable-themes-with-css-variables" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>The trick to organize your CSS is to use variables. To define a variable, you
can use the <code>--variable-name: value</code> format.</p>
<div class="highlight"><pre class="highlight css"><code><span class="nd">:root</span> <span class="p">{</span>
  <span class="py">--page-background</span><span class="p">:</span> <span class="nx">#fff</span><span class="p">;</span>
<span class="p">}</span>

<span class="nt">body</span> <span class="p">{</span>
  <span class="nl">background</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="l">--page-background</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>
<p>That simple. Now, how can we define both of our themes using variables? First,
declare your light theme without any media query, like the following:</p>
<div class="highlight"><pre class="highlight css"><code><span class="nd">:root</span> <span class="p">{</span>
  <span class="py">--page-background</span><span class="p">:</span> <span class="nx">#fff</span><span class="p">;</span>
  <span class="py">--page-title</span><span class="p">:</span> <span class="nx">#333</span><span class="p">;</span>
  <span class="py">--page-text</span><span class="p">:</span> <span class="nx">#333</span><span class="p">;</span>
<span class="p">}</span>

<span class="nt">body</span> <span class="p">{</span>
  <span class="nl">background</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="l">--page-background</span><span class="p">);</span>
  <span class="nl">color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="l">--page-text</span><span class="p">);</span>
<span class="p">}</span>

<span class="nt">h1</span> <span class="p">{</span>
  <span class="nl">color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="l">--page-title</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>
<p>Now, adding the dark theme is as simple as wrapping the sample properties in a
media query. The whole CSS code looks like this:</p>
<div class="highlight"><pre class="highlight css"><code><span class="nd">:root</span> <span class="p">{</span>
  <span class="py">--page-background</span><span class="p">:</span> <span class="nx">#fff</span><span class="p">;</span>
  <span class="py">--page-title</span><span class="p">:</span> <span class="nx">#333</span><span class="p">;</span>
  <span class="py">--page-text</span><span class="p">:</span> <span class="nx">#333</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">@media</span> <span class="nb">screen</span> <span class="n">and</span> <span class="p">(</span><span class="n">prefers-color-scheme</span><span class="p">:</span> <span class="n">dark</span><span class="p">)</span> <span class="p">{</span>
  <span class="nd">:root</span> <span class="p">{</span>
    <span class="py">--page-background</span><span class="p">:</span> <span class="nx">#2d3239</span><span class="p">;</span>
    <span class="py">--page-title</span><span class="p">:</span> <span class="nx">#e9d970</span><span class="p">;</span>
    <span class="py">--page-text</span><span class="p">:</span> <span class="nx">#75715e</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="nt">body</span> <span class="p">{</span>
  <span class="nl">background</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="l">--page-background</span><span class="p">);</span>
  <span class="nl">color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="l">--page-text</span><span class="p">);</span>
<span class="p">}</span>

<span class="nt">h1</span> <span class="p">{</span>
  <span class="nl">color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="l">--page-title</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>
<p>You can <a href="https://codepen.io/fnando/pen/jOOarWO">see this example here</a>. You can define any properties to
tweak your theme including (but not limited to) background images, borders,
shadows, and filters.</p>
<h2 tabindex="-1" id="dark-mode-images-and-videos">Dark mode images and videos<a class="anchor" href="#dark-mode-images-and-videos" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>It&rsquo;s not uncommon to have bright images/videos all over your page. One nice
trick is using filters to reduce the brightness of <code>&lt;img&gt;</code> and <code>&lt;video&gt;</code>. You
can try a combination of <code>opacity</code> and <code>grayscale</code> filters. Add
<code>--image-grayscale</code> and <code>--image-opacity</code> variables and tweak it as you wish:</p>
<div class="highlight"><pre class="highlight css"><code><span class="nd">:root</span> <span class="p">{</span>
  <span class="py">--page-background</span><span class="p">:</span> <span class="nx">#fff</span><span class="p">;</span>
  <span class="py">--page-title</span><span class="p">:</span> <span class="nx">#333</span><span class="p">;</span>
  <span class="py">--page-text</span><span class="p">:</span> <span class="nx">#333</span><span class="p">;</span>
  <span class="py">--image-grayscale</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
  <span class="py">--image-opacity</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">@media</span> <span class="nb">screen</span> <span class="n">and</span> <span class="p">(</span><span class="n">prefers-color-scheme</span><span class="p">:</span> <span class="n">dark</span><span class="p">)</span> <span class="p">{</span>
  <span class="nd">:root</span> <span class="p">{</span>
    <span class="py">--page-background</span><span class="p">:</span> <span class="nx">#2d3239</span><span class="p">;</span>
    <span class="py">--page-title</span><span class="p">:</span> <span class="nx">#e9d970</span><span class="p">;</span>
    <span class="py">--page-text</span><span class="p">:</span> <span class="nx">#75715e</span><span class="p">;</span>
    <span class="py">--image-grayscale</span><span class="p">:</span> <span class="m">50%</span><span class="p">;</span>
    <span class="py">--image-opacity</span><span class="p">:</span> <span class="m">60%</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="nt">img</span><span class="o">,</span>
<span class="nt">video</span> <span class="p">{</span>
  <span class="nl">filter</span><span class="p">:</span> <span class="nf">grayscale</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="l">--image-grayscale</span><span class="p">))</span> <span class="nf">opacity</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="l">--image-opacity</span><span class="p">));</span>
<span class="p">}</span>
</code></pre></div>
<p><img src="https://nandovieira.s3.amazonaws.com/media/dark-mode/image-filters.jpg" alt="Image different in a light theme vs dark theme with filters"></p>

<p>You can <a href="https://codepen.io/fnando/full/WNNXxoN">see this example here</a>. This trick won&rsquo;t work everywhere and
may requiring wrapping images in a container. Eventually, you&rsquo;ll need a
different image. That&rsquo;s where <a href="https://html.spec.whatwg.org/multipage/embedded-content.html#the-picture-element"><code>&lt;picture&gt;</code></a> comes in.</p>

<p>The <code>&lt;picture&gt;</code> element supports media query matchers. So, in case you want to
specify a different logo for dark mode, you can use a different <code>&lt;source&gt;</code>. If
there are no suitable matches or if the browser doesn&rsquo;t support the <code>&lt;picture&gt;</code>
element, then the default <code>src</code> attribute is selected.</p>

<p>Let&rsquo;s say that instead of rendering that same image using filters, you wanted to
render a totally different darker image.</p>
<div class="highlight"><pre class="highlight html"><code><span class="nt">&lt;picture&gt;</span>
  <span class="nt">&lt;source</span> <span class="na">srcset=</span><span class="s">"beach.jpg"</span> <span class="na">media=</span><span class="s">"(prefers-color-scheme: dark)"</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;img</span> <span class="na">src=</span><span class="s">"pool.jpg"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;/picture&gt;</span>
</code></pre></div>
<p>As for the CSS, you can remove everything related to filters. I&rsquo;ll leave this as
an exercise for you. The <a href="https://codepen.io/fnando/full/YzzEeLQ">end result can be seen here</a>.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/dark-mode/picture-element.jpg" alt="&lt;picture&gt; element in action!"></p>

<p>In some cases, you may use an embedded embedded <a href="https://developer.mozilla.org/en-US/docs/Web/SVG">SVG</a> and change the
colors. It works great for things like flat icons, logos, and that sort of
thing, but sometimes you just have to render a different image. Let&rsquo;s add a logo
that can adapt to dark mode.</p>

<p>It&rsquo;s important to know is that, to style <code>&lt;svg&gt;</code> elements you have to actual
render it on your HTML markup. Referencing it through an <code>&lt;img&gt;</code> tag won&rsquo;t allow
any styles on the rendering SVG. With that said, the SVG we&rsquo;re using looks like
this:</p>
<div class="highlight"><pre class="highlight html"><code><span class="nt">&lt;svg</span>
  <span class="na">id=</span><span class="s">"logo"</span>
  <span class="na">width=</span><span class="s">"250px"</span>
  <span class="na">height=</span><span class="s">"55px"</span>
  <span class="na">viewBox=</span><span class="s">"0 0 250 55"</span>
  <span class="na">version=</span><span class="s">"1.1"</span>
  <span class="na">xmlns=</span><span class="s">"http://www.w3.org/2000/svg"</span>
  <span class="na">xmlns:xlink=</span><span class="s">"http://www.w3.org/1999/xlink"</span>
<span class="nt">&gt;</span>
  <span class="nt">&lt;g</span> <span class="na">stroke=</span><span class="s">"none"</span> <span class="na">stroke-width=</span><span class="s">"1"</span> <span class="na">fill=</span><span class="s">"none"</span> <span class="na">fill-rule=</span><span class="s">"evenodd"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;g</span> <span class="na">id=</span><span class="s">"logo"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;path</span> <span class="na">d=</span><span class="s">"..."</span> <span class="na">id=</span><span class="s">"background"</span> <span class="na">fill=</span><span class="s">"#0091FF"</span><span class="nt">&gt;&lt;/path&gt;</span>
      <span class="nt">&lt;path</span> <span class="na">d=</span><span class="s">"..."</span> <span class="na">id=</span><span class="s">"letter"</span> <span class="na">fill=</span><span class="s">"#FFD700"</span> <span class="na">fill-rule=</span><span class="s">"nonzero"</span><span class="nt">&gt;&lt;/path&gt;</span>
      <span class="nt">&lt;path</span> <span class="na">d=</span><span class="s">"..."</span> <span class="na">id=</span><span class="s">"words"</span> <span class="na">fill=</span><span class="s">"#45638B"</span> <span class="na">fill-rule=</span><span class="s">"nonzero"</span><span class="nt">&gt;&lt;/path&gt;</span>
    <span class="nt">&lt;/g&gt;</span>
  <span class="nt">&lt;/g&gt;</span>
<span class="nt">&lt;/svg&gt;</span>
</code></pre></div>
<p>The colors applied to this SVG are the light mode colors, and by doing that
we&rsquo;re only required to style the dark mode. This is the updated CSS with the SVG
styling changes.</p>
<div class="highlight"><pre class="highlight css"><code><span class="k">@media</span> <span class="nb">screen</span> <span class="n">and</span> <span class="p">(</span><span class="n">prefers-color-scheme</span><span class="p">:</span> <span class="n">dark</span><span class="p">)</span> <span class="p">{</span>
  <span class="nd">:root</span> <span class="p">{</span>
    <span class="py">--page-background</span><span class="p">:</span> <span class="nx">#2d3239</span><span class="p">;</span>
    <span class="py">--page-title</span><span class="p">:</span> <span class="nx">#e9d970</span><span class="p">;</span>
    <span class="py">--page-text</span><span class="p">:</span> <span class="nx">#75715e</span><span class="p">;</span>

    <span class="py">--logo-background</span><span class="p">:</span> <span class="nx">#4d5866</span><span class="p">;</span>
    <span class="py">--logo-words</span><span class="p">:</span> <span class="nx">#fff</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="nf">#logo--words</span> <span class="p">{</span>
    <span class="py">fill</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="l">--logo-words</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="nf">#logo--background</span> <span class="p">{</span>
    <span class="py">fill</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="l">--logo-background</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>You can <a href="https://codepen.io/fnando/full/abbVYJw">check this example here</a>.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/dark-mode/svg-element.jpg" alt="&lt;svg&gt; styling for dark mode"></p>

<p>Alternatively, you could have used <code>currentColor</code> as the value of <code>fill</code> and
<code>stroke</code> properties. This way, you can change all referenced colors by either
setting the SVG&rsquo;s <code>color</code> property or the inherited color. <a href="https://codepen.io/fnando/full/RwwjYKb">This approach works
extremely well with line icons</a>.</p>

<video loop controls width="1074" height="652" poster="https://nandovieira.s3.amazonaws.com/media/dark-mode/svg-current-color.jpg">
  <source src="https://nandovieira.s3.amazonaws.com/media/dark-mode/svg-current-color.mp4" type="video/mp4">
  <source src="https://nandovieira.s3.amazonaws.com/media/dark-mode/svg-current-color.webm" type="video/webm">
</video>

<p>The example above generates a new color every time the button is clicked and
sets the <code>&lt;body&gt;</code> element&rsquo;s <code>color</code> with <code>document.body.style.color = newColor</code>.</p>
<h2 tabindex="-1" id="dark-mode-javascript">Dark mode JavaScript<a class="anchor" href="#dark-mode-javascript" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>You may need to do operations that require detecting dark mode as well, like
rendering charts. For that you&rsquo;ll need to use
<a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia"><code>window.matchMedia</code></a>. The detection is fairly simple.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="kd">function</span> <span class="nf">isDarkMode</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">return </span><span class="p">(</span>
    <span class="nb">window</span><span class="p">.</span><span class="nx">matchMedia</span> <span class="o">&amp;&amp;</span>
    <span class="nb">window</span><span class="p">.</span><span class="nf">matchMedia</span><span class="p">(</span><span class="dl">"</span><span class="s2">(prefers-color-scheme: dark)</span><span class="dl">"</span><span class="p">).</span><span class="nx">matches</span>
  <span class="p">);</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nf">renderCanvas</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">theme</span> <span class="o">=</span> <span class="nf">isDarkMode</span><span class="p">()</span>
    <span class="p">?</span> <span class="p">{</span> <span class="na">background</span><span class="p">:</span> <span class="dl">"</span><span class="s2">#4d5866</span><span class="dl">"</span><span class="p">,</span> <span class="na">border</span><span class="p">:</span> <span class="dl">"</span><span class="s2">#7091ba</span><span class="dl">"</span> <span class="p">}</span>
    <span class="p">:</span> <span class="p">{</span> <span class="na">background</span><span class="p">:</span> <span class="dl">"</span><span class="s2">#ffffff</span><span class="dl">"</span><span class="p">,</span> <span class="na">border</span><span class="p">:</span> <span class="dl">"</span><span class="s2">#000</span><span class="dl">"</span> <span class="p">};</span>

  <span class="kd">const</span> <span class="nx">canvas</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nf">querySelector</span><span class="p">(</span><span class="dl">"</span><span class="s2">canvas</span><span class="dl">"</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">ctx</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nf">getContext</span><span class="p">(</span><span class="dl">"</span><span class="s2">2d</span><span class="dl">"</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">x</span> <span class="o">=</span> <span class="mi">15</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">y</span> <span class="o">=</span> <span class="mi">15</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">width</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span> <span class="o">-</span> <span class="nx">x</span> <span class="o">*</span> <span class="mi">2</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">height</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span> <span class="o">-</span> <span class="nx">y</span> <span class="o">*</span> <span class="mi">2</span><span class="p">;</span>

  <span class="nx">ctx</span><span class="p">.</span><span class="nf">clearRect</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span><span class="p">);</span>
  <span class="nx">ctx</span><span class="p">.</span><span class="nx">lineWidth</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
  <span class="nx">ctx</span><span class="p">.</span><span class="nx">strokeStyle</span> <span class="o">=</span> <span class="nx">theme</span><span class="p">.</span><span class="nx">border</span><span class="p">;</span>
  <span class="nx">ctx</span><span class="p">.</span><span class="nf">strokeRect</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">height</span><span class="p">);</span>
  <span class="nx">ctx</span><span class="p">.</span><span class="nx">fillStyle</span> <span class="o">=</span> <span class="nx">theme</span><span class="p">.</span><span class="nx">background</span><span class="p">;</span>
  <span class="nx">ctx</span><span class="p">.</span><span class="nf">fillRect</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">height</span><span class="p">);</span>
<span class="p">}</span>

<span class="nf">renderCanvas</span><span class="p">();</span>
</code></pre></div>
<p>As you can see, all you have to do is conditionally define your theme once your
media matcher detects the dark mode. <a href="https://codepen.io/fnando/full/VwwrxPJ">You can see this example here</a>.</p>

<p>Now, if we switch from one mode to the other (you may also have configured your
computer to automatically do it for you), you would see the wrong colors being
rendered. To re-render the canvas we can use
<a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList/addListener"><code>MediaQueryList.addListener</code></a> to respond to the change.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="kd">const</span> <span class="nx">darkModeMatcher</span> <span class="o">=</span>
  <span class="nb">window</span><span class="p">.</span><span class="nx">matchMedia</span> <span class="o">&amp;&amp;</span> <span class="nb">window</span><span class="p">.</span><span class="nf">matchMedia</span><span class="p">(</span><span class="dl">"</span><span class="s2">(prefers-color-scheme: dark)</span><span class="dl">"</span><span class="p">);</span>

<span class="kd">function</span> <span class="nf">isDarkMode</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">darkModeMatcher</span> <span class="o">&amp;&amp;</span> <span class="nx">darkModeMatcher</span><span class="p">.</span><span class="nx">matches</span><span class="p">;</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nf">onDarkModeChange</span><span class="p">(</span><span class="nx">callback</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if </span><span class="p">(</span><span class="o">!</span><span class="nx">darkModeMatcher</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="nx">darkModeMatcher</span><span class="p">.</span><span class="nf">addListener</span><span class="p">(({</span> <span class="nx">matches</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="nf">callback</span><span class="p">(</span><span class="nx">matches</span><span class="p">));</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nf">renderCanvas</span><span class="p">(</span><span class="nx">useDarkTheme</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">theme</span> <span class="o">=</span> <span class="nx">useDarkTheme</span>
    <span class="p">?</span> <span class="p">{</span> <span class="na">background</span><span class="p">:</span> <span class="dl">"</span><span class="s2">#4d5866</span><span class="dl">"</span><span class="p">,</span> <span class="na">border</span><span class="p">:</span> <span class="dl">"</span><span class="s2">#7091ba</span><span class="dl">"</span> <span class="p">}</span>
    <span class="p">:</span> <span class="p">{</span> <span class="na">background</span><span class="p">:</span> <span class="dl">"</span><span class="s2">#ffffff</span><span class="dl">"</span><span class="p">,</span> <span class="na">border</span><span class="p">:</span> <span class="dl">"</span><span class="s2">#000</span><span class="dl">"</span> <span class="p">};</span>

  <span class="kd">const</span> <span class="nx">canvas</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nf">querySelector</span><span class="p">(</span><span class="dl">"</span><span class="s2">canvas</span><span class="dl">"</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">ctx</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nf">getContext</span><span class="p">(</span><span class="dl">"</span><span class="s2">2d</span><span class="dl">"</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">x</span> <span class="o">=</span> <span class="mi">15</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">y</span> <span class="o">=</span> <span class="mi">15</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">width</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span> <span class="o">-</span> <span class="nx">x</span> <span class="o">*</span> <span class="mi">2</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">height</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span> <span class="o">-</span> <span class="nx">y</span> <span class="o">*</span> <span class="mi">2</span><span class="p">;</span>

  <span class="nx">ctx</span><span class="p">.</span><span class="nf">clearRect</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span><span class="p">);</span>
  <span class="nx">ctx</span><span class="p">.</span><span class="nx">lineWidth</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
  <span class="nx">ctx</span><span class="p">.</span><span class="nx">strokeStyle</span> <span class="o">=</span> <span class="nx">theme</span><span class="p">.</span><span class="nx">border</span><span class="p">;</span>
  <span class="nx">ctx</span><span class="p">.</span><span class="nf">strokeRect</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">height</span><span class="p">);</span>
  <span class="nx">ctx</span><span class="p">.</span><span class="nx">fillStyle</span> <span class="o">=</span> <span class="nx">theme</span><span class="p">.</span><span class="nx">background</span><span class="p">;</span>
  <span class="nx">ctx</span><span class="p">.</span><span class="nf">fillRect</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">height</span><span class="p">);</span>
<span class="p">}</span>

<span class="nf">renderCanvas</span><span class="p">(</span><span class="nf">isDarkMode</span><span class="p">());</span>
<span class="nf">onDarkModeChange</span><span class="p">(</span><span class="nx">renderCanvas</span><span class="p">);</span>
</code></pre></div>
<p>This will make sure the function <code>renderCanvas</code> is called whenever the mode
changes. <a href="https://codepen.io/fnando/full/BaamVXG">You can see this example here</a>.</p>

<video loop controls width="978" height="754" poster="https://nandovieira.s3.amazonaws.com/media/dark-mode/match-media-listener.jpg">
  <source src="https://nandovieira.s3.amazonaws.com/media/dark-mode/match-media-listener.mp4" type="video/mp4">
  <source src="https://nandovieira.s3.amazonaws.com/media/dark-mode/match-media-listener.webm" type="video/webm">
</video>

<p>And that pretty ends what you need to know about the technical aspects of
supporting dark mode in the web.</p>
<h2 tabindex="-1" id="wrapping-up">Wrapping up<a class="anchor" href="#wrapping-up" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>Dark mode is the latest trend in tech, no doubt about it. And even without
having any serious research backing its so-called &ldquo;dark mode benefits&rdquo;, you may
consider doing it for the sake of your customers&rsquo; happiness. The technical
aspects are quite simple, but don&rsquo;t fool yourself: creating dark themes is a
very challenging process, specially when it comes to art direction for all
assets (including images and video).</p>

<p>Another thing to consider is whether you should automatically switch to dark
mode or use a configurable setting on your site. The latter is very simple to
implement by setting a class on an element (e.g.
<code>&lt;html data-theme=&quot;dark-mode&quot;&gt;</code>), but you&rsquo;d have to manually execute scripts and
change images/videos in case you switched from one mode to the other. Either
that, or not bothering at all, which would be (probably) fine in most cases.</p>
]]>
      </description>
      <content:encoded>
        <![CDATA[<p>Welcome to 2019, the year that the technology went dark mode. Both <a href="https://www.android.com/android-10/">Android
10</a> and <a href="https://www.apple.com/ios/ios-13/">iOS 13</a> were released last month, and the most
acclaimed feature was dark mode. Native apps can now implement specific themes
for each mode (i.e. dark, light), but can you do it on the web? This article
outlines how you can take advantage of this new trend and make everyone happy.</p>
<h2 tabindex="-1" id="are-there-any-benefits-on-using-dark-mode">Are there any benefits on using dark mode?<a class="anchor" href="#are-there-any-benefits-on-using-dark-mode" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>When you read about dark mode, you often see &ldquo;easy on the eyes&rdquo; or &ldquo;energy
efficiency&rdquo; being mentioned. Truth is, to be energy efficient you need
<a href="https://en.wikipedia.org/wiki/OLED">OLED</a>/<a href="https://en.wikipedia.org/wiki/AMOLED">AMOLED</a> screens which are only available on newer high-end
devices.</p>

<ul>
<li><a href="https://support.apple.com/kb/sp770">iPhone X</a> or newer.</li>
<li><a href="https://www.samsung.com/global/galaxy/galaxy-s10/specs/">Samsung Galaxy S10</a> or newer.</li>
<li><a href="https://store.google.com/product/pixel_3_specs">Google Pixel 3</a> or newer.</li>
<li><a href="https://consumer.huawei.com/en/phones/p30/specs/">Huawei P30</a><a href="https://consumer.huawei.com/en/phones/p30/specs/">huawei-p30</a> or newer.</li>
</ul>

<p>If you&rsquo;re using LCD or another type of screen, changing colors won’t do much for
your battery life. Most people won&rsquo;t benefit from the energy efficiency aspect
of dark modes at all.</p>

<p>But what about health? It certainly is better, right? Well… unfortunately, we
still don&rsquo;t have enough scientific data to assert that dark mode is better. What
we know for sure is that the time we spend looking at screens is the main factor
to eye strain. The less, the better.</p>

<p>Some doctors also mention that a higher contrast (either way) is more important
than using either mode. And some doctors mention that dark mode can be more
damaging for people with astigmatism.</p>

<p>I personally don&rsquo;t like dark mode at all when using dark background with light
text (i.e. black background with white text); I have that annoying feeling of
burnt image that takes a minute or so to disappear. And the opposite is also
true for some people. And without serious researches about this subject we can
only talk by experience.</p>

<p>Now, I think the biggest advantage of dark mode is when you&rsquo;re using your phone
in a low light room, but you would be better off not using your phone right
before going to sleep anyway, right?</p>
<h2 tabindex="-1" id="browse-support">Browse support<a class="anchor" href="#browse-support" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>How well supported is dark mode on today&rsquo;s browsers? To detect whether a browser
is running on a dark mode device or not, you&rsquo;ll need support for
<a href="https://drafts.csswg.org/mediaqueries-5/#prefers-color-scheme"><code>(prefers-color-scheme: dark)</code></a> media query,
available on the following devices.</p>

<ul>
<li>macOS Safari 13 or newer.</li>
<li>iOS Safari 13.2 or newer.</li>
<li>Android Browser 76 or newer.</li>
<li>Chrome for Android 78 or newer.</li>
<li>Firefox for Android 68 or newer.</li>
<li>Opera 62 or newer.</li>
<li>Chrome 76 or newer.</li>
<li>Firefox 67 or newer.</li>
<li>Microsoft Edge 76 or newer.</li>
</ul>

<p>Yeah, I know… this is stupid versioning in action and I shouldn&rsquo;t have added
them anyway. What you need to know is that Safari 13 is very, very new. And so
is the support for this media query on every other browser. From the business
perspective, it doesn&rsquo;t make sense to spend your designer&rsquo;s time on this task,
but you&rsquo;re going to do it anyway, aren&rsquo;t you? In this case, this is how you can
define the CSS.</p>
<div class="highlight"><pre class="highlight css"><code><span class="nt">body</span> <span class="p">{</span>
  <span class="nl">color</span><span class="p">:</span> <span class="nx">#333</span><span class="p">;</span>
  <span class="nl">background</span><span class="p">:</span> <span class="nx">#fff</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">@media</span> <span class="nb">screen</span> <span class="n">and</span> <span class="p">(</span><span class="n">prefers-color-scheme</span><span class="p">:</span> <span class="n">dark</span><span class="p">)</span> <span class="p">{</span>
  <span class="nt">body</span> <span class="p">{</span>
    <span class="nl">background</span><span class="p">:</span> <span class="nx">#2d3239</span><span class="p">;</span>
    <span class="nl">color</span><span class="p">:</span> <span class="nx">#75715e</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="nt">h1</span> <span class="p">{</span>
    <span class="nl">color</span><span class="p">:</span> <span class="nx">#e9d970</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>You can easily alternate between light and dark mode using Safari&rsquo;s Web
Inspector. I couldn&rsquo;t find such option on Chrome yet, but we&rsquo;ll have something
similar sooner or later. <a href="https://codepen.io/fnando/full/XWWzdbL">This example can be found here.</a></p>

<video loop controls width="978" height="754" poster="https://nandovieira.s3.amazonaws.com/media/dark-mode/safari-dark-mode-inspector.jpg">
  <source src="https://nandovieira.s3.amazonaws.com/media/dark-mode/safari-dark-mode-inspector.mp4" type="video/mp4">
  <source src="https://nandovieira.s3.amazonaws.com/media/dark-mode/safari-dark-mode-inspector.webm" type="video/webm">
</video>

<p>I know what you&rsquo;re thinking… the CSS can easily get out of hand. And you&rsquo;re
right! Unless you use variables. More specifically, <a href="https://drafts.csswg.org/css-variables/">CSS
variables</a>.</p>
<h2 tabindex="-1" id="creating-manageable-themes-with-css-variables">Creating manageable themes with CSS variables<a class="anchor" href="#creating-manageable-themes-with-css-variables" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>The trick to organize your CSS is to use variables. To define a variable, you
can use the <code>--variable-name: value</code> format.</p>
<div class="highlight"><pre class="highlight css"><code><span class="nd">:root</span> <span class="p">{</span>
  <span class="py">--page-background</span><span class="p">:</span> <span class="nx">#fff</span><span class="p">;</span>
<span class="p">}</span>

<span class="nt">body</span> <span class="p">{</span>
  <span class="nl">background</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="l">--page-background</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>
<p>That simple. Now, how can we define both of our themes using variables? First,
declare your light theme without any media query, like the following:</p>
<div class="highlight"><pre class="highlight css"><code><span class="nd">:root</span> <span class="p">{</span>
  <span class="py">--page-background</span><span class="p">:</span> <span class="nx">#fff</span><span class="p">;</span>
  <span class="py">--page-title</span><span class="p">:</span> <span class="nx">#333</span><span class="p">;</span>
  <span class="py">--page-text</span><span class="p">:</span> <span class="nx">#333</span><span class="p">;</span>
<span class="p">}</span>

<span class="nt">body</span> <span class="p">{</span>
  <span class="nl">background</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="l">--page-background</span><span class="p">);</span>
  <span class="nl">color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="l">--page-text</span><span class="p">);</span>
<span class="p">}</span>

<span class="nt">h1</span> <span class="p">{</span>
  <span class="nl">color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="l">--page-title</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>
<p>Now, adding the dark theme is as simple as wrapping the sample properties in a
media query. The whole CSS code looks like this:</p>
<div class="highlight"><pre class="highlight css"><code><span class="nd">:root</span> <span class="p">{</span>
  <span class="py">--page-background</span><span class="p">:</span> <span class="nx">#fff</span><span class="p">;</span>
  <span class="py">--page-title</span><span class="p">:</span> <span class="nx">#333</span><span class="p">;</span>
  <span class="py">--page-text</span><span class="p">:</span> <span class="nx">#333</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">@media</span> <span class="nb">screen</span> <span class="n">and</span> <span class="p">(</span><span class="n">prefers-color-scheme</span><span class="p">:</span> <span class="n">dark</span><span class="p">)</span> <span class="p">{</span>
  <span class="nd">:root</span> <span class="p">{</span>
    <span class="py">--page-background</span><span class="p">:</span> <span class="nx">#2d3239</span><span class="p">;</span>
    <span class="py">--page-title</span><span class="p">:</span> <span class="nx">#e9d970</span><span class="p">;</span>
    <span class="py">--page-text</span><span class="p">:</span> <span class="nx">#75715e</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="nt">body</span> <span class="p">{</span>
  <span class="nl">background</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="l">--page-background</span><span class="p">);</span>
  <span class="nl">color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="l">--page-text</span><span class="p">);</span>
<span class="p">}</span>

<span class="nt">h1</span> <span class="p">{</span>
  <span class="nl">color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="l">--page-title</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>
<p>You can <a href="https://codepen.io/fnando/pen/jOOarWO">see this example here</a>. You can define any properties to
tweak your theme including (but not limited to) background images, borders,
shadows, and filters.</p>
<h2 tabindex="-1" id="dark-mode-images-and-videos">Dark mode images and videos<a class="anchor" href="#dark-mode-images-and-videos" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>It&rsquo;s not uncommon to have bright images/videos all over your page. One nice
trick is using filters to reduce the brightness of <code>&lt;img&gt;</code> and <code>&lt;video&gt;</code>. You
can try a combination of <code>opacity</code> and <code>grayscale</code> filters. Add
<code>--image-grayscale</code> and <code>--image-opacity</code> variables and tweak it as you wish:</p>
<div class="highlight"><pre class="highlight css"><code><span class="nd">:root</span> <span class="p">{</span>
  <span class="py">--page-background</span><span class="p">:</span> <span class="nx">#fff</span><span class="p">;</span>
  <span class="py">--page-title</span><span class="p">:</span> <span class="nx">#333</span><span class="p">;</span>
  <span class="py">--page-text</span><span class="p">:</span> <span class="nx">#333</span><span class="p">;</span>
  <span class="py">--image-grayscale</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
  <span class="py">--image-opacity</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">@media</span> <span class="nb">screen</span> <span class="n">and</span> <span class="p">(</span><span class="n">prefers-color-scheme</span><span class="p">:</span> <span class="n">dark</span><span class="p">)</span> <span class="p">{</span>
  <span class="nd">:root</span> <span class="p">{</span>
    <span class="py">--page-background</span><span class="p">:</span> <span class="nx">#2d3239</span><span class="p">;</span>
    <span class="py">--page-title</span><span class="p">:</span> <span class="nx">#e9d970</span><span class="p">;</span>
    <span class="py">--page-text</span><span class="p">:</span> <span class="nx">#75715e</span><span class="p">;</span>
    <span class="py">--image-grayscale</span><span class="p">:</span> <span class="m">50%</span><span class="p">;</span>
    <span class="py">--image-opacity</span><span class="p">:</span> <span class="m">60%</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="nt">img</span><span class="o">,</span>
<span class="nt">video</span> <span class="p">{</span>
  <span class="nl">filter</span><span class="p">:</span> <span class="nf">grayscale</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="l">--image-grayscale</span><span class="p">))</span> <span class="nf">opacity</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="l">--image-opacity</span><span class="p">));</span>
<span class="p">}</span>
</code></pre></div>
<p><img src="https://nandovieira.s3.amazonaws.com/media/dark-mode/image-filters.jpg" alt="Image different in a light theme vs dark theme with filters"></p>

<p>You can <a href="https://codepen.io/fnando/full/WNNXxoN">see this example here</a>. This trick won&rsquo;t work everywhere and
may requiring wrapping images in a container. Eventually, you&rsquo;ll need a
different image. That&rsquo;s where <a href="https://html.spec.whatwg.org/multipage/embedded-content.html#the-picture-element"><code>&lt;picture&gt;</code></a> comes in.</p>

<p>The <code>&lt;picture&gt;</code> element supports media query matchers. So, in case you want to
specify a different logo for dark mode, you can use a different <code>&lt;source&gt;</code>. If
there are no suitable matches or if the browser doesn&rsquo;t support the <code>&lt;picture&gt;</code>
element, then the default <code>src</code> attribute is selected.</p>

<p>Let&rsquo;s say that instead of rendering that same image using filters, you wanted to
render a totally different darker image.</p>
<div class="highlight"><pre class="highlight html"><code><span class="nt">&lt;picture&gt;</span>
  <span class="nt">&lt;source</span> <span class="na">srcset=</span><span class="s">"beach.jpg"</span> <span class="na">media=</span><span class="s">"(prefers-color-scheme: dark)"</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;img</span> <span class="na">src=</span><span class="s">"pool.jpg"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;/picture&gt;</span>
</code></pre></div>
<p>As for the CSS, you can remove everything related to filters. I&rsquo;ll leave this as
an exercise for you. The <a href="https://codepen.io/fnando/full/YzzEeLQ">end result can be seen here</a>.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/dark-mode/picture-element.jpg" alt="&lt;picture&gt; element in action!"></p>

<p>In some cases, you may use an embedded embedded <a href="https://developer.mozilla.org/en-US/docs/Web/SVG">SVG</a> and change the
colors. It works great for things like flat icons, logos, and that sort of
thing, but sometimes you just have to render a different image. Let&rsquo;s add a logo
that can adapt to dark mode.</p>

<p>It&rsquo;s important to know is that, to style <code>&lt;svg&gt;</code> elements you have to actual
render it on your HTML markup. Referencing it through an <code>&lt;img&gt;</code> tag won&rsquo;t allow
any styles on the rendering SVG. With that said, the SVG we&rsquo;re using looks like
this:</p>
<div class="highlight"><pre class="highlight html"><code><span class="nt">&lt;svg</span>
  <span class="na">id=</span><span class="s">"logo"</span>
  <span class="na">width=</span><span class="s">"250px"</span>
  <span class="na">height=</span><span class="s">"55px"</span>
  <span class="na">viewBox=</span><span class="s">"0 0 250 55"</span>
  <span class="na">version=</span><span class="s">"1.1"</span>
  <span class="na">xmlns=</span><span class="s">"http://www.w3.org/2000/svg"</span>
  <span class="na">xmlns:xlink=</span><span class="s">"http://www.w3.org/1999/xlink"</span>
<span class="nt">&gt;</span>
  <span class="nt">&lt;g</span> <span class="na">stroke=</span><span class="s">"none"</span> <span class="na">stroke-width=</span><span class="s">"1"</span> <span class="na">fill=</span><span class="s">"none"</span> <span class="na">fill-rule=</span><span class="s">"evenodd"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;g</span> <span class="na">id=</span><span class="s">"logo"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;path</span> <span class="na">d=</span><span class="s">"..."</span> <span class="na">id=</span><span class="s">"background"</span> <span class="na">fill=</span><span class="s">"#0091FF"</span><span class="nt">&gt;&lt;/path&gt;</span>
      <span class="nt">&lt;path</span> <span class="na">d=</span><span class="s">"..."</span> <span class="na">id=</span><span class="s">"letter"</span> <span class="na">fill=</span><span class="s">"#FFD700"</span> <span class="na">fill-rule=</span><span class="s">"nonzero"</span><span class="nt">&gt;&lt;/path&gt;</span>
      <span class="nt">&lt;path</span> <span class="na">d=</span><span class="s">"..."</span> <span class="na">id=</span><span class="s">"words"</span> <span class="na">fill=</span><span class="s">"#45638B"</span> <span class="na">fill-rule=</span><span class="s">"nonzero"</span><span class="nt">&gt;&lt;/path&gt;</span>
    <span class="nt">&lt;/g&gt;</span>
  <span class="nt">&lt;/g&gt;</span>
<span class="nt">&lt;/svg&gt;</span>
</code></pre></div>
<p>The colors applied to this SVG are the light mode colors, and by doing that
we&rsquo;re only required to style the dark mode. This is the updated CSS with the SVG
styling changes.</p>
<div class="highlight"><pre class="highlight css"><code><span class="k">@media</span> <span class="nb">screen</span> <span class="n">and</span> <span class="p">(</span><span class="n">prefers-color-scheme</span><span class="p">:</span> <span class="n">dark</span><span class="p">)</span> <span class="p">{</span>
  <span class="nd">:root</span> <span class="p">{</span>
    <span class="py">--page-background</span><span class="p">:</span> <span class="nx">#2d3239</span><span class="p">;</span>
    <span class="py">--page-title</span><span class="p">:</span> <span class="nx">#e9d970</span><span class="p">;</span>
    <span class="py">--page-text</span><span class="p">:</span> <span class="nx">#75715e</span><span class="p">;</span>

    <span class="py">--logo-background</span><span class="p">:</span> <span class="nx">#4d5866</span><span class="p">;</span>
    <span class="py">--logo-words</span><span class="p">:</span> <span class="nx">#fff</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="nf">#logo--words</span> <span class="p">{</span>
    <span class="py">fill</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="l">--logo-words</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="nf">#logo--background</span> <span class="p">{</span>
    <span class="py">fill</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="l">--logo-background</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>You can <a href="https://codepen.io/fnando/full/abbVYJw">check this example here</a>.</p>

<p><img src="https://nandovieira.s3.amazonaws.com/media/dark-mode/svg-element.jpg" alt="&lt;svg&gt; styling for dark mode"></p>

<p>Alternatively, you could have used <code>currentColor</code> as the value of <code>fill</code> and
<code>stroke</code> properties. This way, you can change all referenced colors by either
setting the SVG&rsquo;s <code>color</code> property or the inherited color. <a href="https://codepen.io/fnando/full/RwwjYKb">This approach works
extremely well with line icons</a>.</p>

<video loop controls width="1074" height="652" poster="https://nandovieira.s3.amazonaws.com/media/dark-mode/svg-current-color.jpg">
  <source src="https://nandovieira.s3.amazonaws.com/media/dark-mode/svg-current-color.mp4" type="video/mp4">
  <source src="https://nandovieira.s3.amazonaws.com/media/dark-mode/svg-current-color.webm" type="video/webm">
</video>

<p>The example above generates a new color every time the button is clicked and
sets the <code>&lt;body&gt;</code> element&rsquo;s <code>color</code> with <code>document.body.style.color = newColor</code>.</p>
<h2 tabindex="-1" id="dark-mode-javascript">Dark mode JavaScript<a class="anchor" href="#dark-mode-javascript" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>You may need to do operations that require detecting dark mode as well, like
rendering charts. For that you&rsquo;ll need to use
<a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia"><code>window.matchMedia</code></a>. The detection is fairly simple.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="kd">function</span> <span class="nf">isDarkMode</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">return </span><span class="p">(</span>
    <span class="nb">window</span><span class="p">.</span><span class="nx">matchMedia</span> <span class="o">&amp;&amp;</span>
    <span class="nb">window</span><span class="p">.</span><span class="nf">matchMedia</span><span class="p">(</span><span class="dl">"</span><span class="s2">(prefers-color-scheme: dark)</span><span class="dl">"</span><span class="p">).</span><span class="nx">matches</span>
  <span class="p">);</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nf">renderCanvas</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">theme</span> <span class="o">=</span> <span class="nf">isDarkMode</span><span class="p">()</span>
    <span class="p">?</span> <span class="p">{</span> <span class="na">background</span><span class="p">:</span> <span class="dl">"</span><span class="s2">#4d5866</span><span class="dl">"</span><span class="p">,</span> <span class="na">border</span><span class="p">:</span> <span class="dl">"</span><span class="s2">#7091ba</span><span class="dl">"</span> <span class="p">}</span>
    <span class="p">:</span> <span class="p">{</span> <span class="na">background</span><span class="p">:</span> <span class="dl">"</span><span class="s2">#ffffff</span><span class="dl">"</span><span class="p">,</span> <span class="na">border</span><span class="p">:</span> <span class="dl">"</span><span class="s2">#000</span><span class="dl">"</span> <span class="p">};</span>

  <span class="kd">const</span> <span class="nx">canvas</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nf">querySelector</span><span class="p">(</span><span class="dl">"</span><span class="s2">canvas</span><span class="dl">"</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">ctx</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nf">getContext</span><span class="p">(</span><span class="dl">"</span><span class="s2">2d</span><span class="dl">"</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">x</span> <span class="o">=</span> <span class="mi">15</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">y</span> <span class="o">=</span> <span class="mi">15</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">width</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span> <span class="o">-</span> <span class="nx">x</span> <span class="o">*</span> <span class="mi">2</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">height</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span> <span class="o">-</span> <span class="nx">y</span> <span class="o">*</span> <span class="mi">2</span><span class="p">;</span>

  <span class="nx">ctx</span><span class="p">.</span><span class="nf">clearRect</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span><span class="p">);</span>
  <span class="nx">ctx</span><span class="p">.</span><span class="nx">lineWidth</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
  <span class="nx">ctx</span><span class="p">.</span><span class="nx">strokeStyle</span> <span class="o">=</span> <span class="nx">theme</span><span class="p">.</span><span class="nx">border</span><span class="p">;</span>
  <span class="nx">ctx</span><span class="p">.</span><span class="nf">strokeRect</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">height</span><span class="p">);</span>
  <span class="nx">ctx</span><span class="p">.</span><span class="nx">fillStyle</span> <span class="o">=</span> <span class="nx">theme</span><span class="p">.</span><span class="nx">background</span><span class="p">;</span>
  <span class="nx">ctx</span><span class="p">.</span><span class="nf">fillRect</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">height</span><span class="p">);</span>
<span class="p">}</span>

<span class="nf">renderCanvas</span><span class="p">();</span>
</code></pre></div>
<p>As you can see, all you have to do is conditionally define your theme once your
media matcher detects the dark mode. <a href="https://codepen.io/fnando/full/VwwrxPJ">You can see this example here</a>.</p>

<p>Now, if we switch from one mode to the other (you may also have configured your
computer to automatically do it for you), you would see the wrong colors being
rendered. To re-render the canvas we can use
<a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList/addListener"><code>MediaQueryList.addListener</code></a> to respond to the change.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="kd">const</span> <span class="nx">darkModeMatcher</span> <span class="o">=</span>
  <span class="nb">window</span><span class="p">.</span><span class="nx">matchMedia</span> <span class="o">&amp;&amp;</span> <span class="nb">window</span><span class="p">.</span><span class="nf">matchMedia</span><span class="p">(</span><span class="dl">"</span><span class="s2">(prefers-color-scheme: dark)</span><span class="dl">"</span><span class="p">);</span>

<span class="kd">function</span> <span class="nf">isDarkMode</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">darkModeMatcher</span> <span class="o">&amp;&amp;</span> <span class="nx">darkModeMatcher</span><span class="p">.</span><span class="nx">matches</span><span class="p">;</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nf">onDarkModeChange</span><span class="p">(</span><span class="nx">callback</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if </span><span class="p">(</span><span class="o">!</span><span class="nx">darkModeMatcher</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="nx">darkModeMatcher</span><span class="p">.</span><span class="nf">addListener</span><span class="p">(({</span> <span class="nx">matches</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="nf">callback</span><span class="p">(</span><span class="nx">matches</span><span class="p">));</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nf">renderCanvas</span><span class="p">(</span><span class="nx">useDarkTheme</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">theme</span> <span class="o">=</span> <span class="nx">useDarkTheme</span>
    <span class="p">?</span> <span class="p">{</span> <span class="na">background</span><span class="p">:</span> <span class="dl">"</span><span class="s2">#4d5866</span><span class="dl">"</span><span class="p">,</span> <span class="na">border</span><span class="p">:</span> <span class="dl">"</span><span class="s2">#7091ba</span><span class="dl">"</span> <span class="p">}</span>
    <span class="p">:</span> <span class="p">{</span> <span class="na">background</span><span class="p">:</span> <span class="dl">"</span><span class="s2">#ffffff</span><span class="dl">"</span><span class="p">,</span> <span class="na">border</span><span class="p">:</span> <span class="dl">"</span><span class="s2">#000</span><span class="dl">"</span> <span class="p">};</span>

  <span class="kd">const</span> <span class="nx">canvas</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nf">querySelector</span><span class="p">(</span><span class="dl">"</span><span class="s2">canvas</span><span class="dl">"</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">ctx</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nf">getContext</span><span class="p">(</span><span class="dl">"</span><span class="s2">2d</span><span class="dl">"</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">x</span> <span class="o">=</span> <span class="mi">15</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">y</span> <span class="o">=</span> <span class="mi">15</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">width</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span> <span class="o">-</span> <span class="nx">x</span> <span class="o">*</span> <span class="mi">2</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">height</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span> <span class="o">-</span> <span class="nx">y</span> <span class="o">*</span> <span class="mi">2</span><span class="p">;</span>

  <span class="nx">ctx</span><span class="p">.</span><span class="nf">clearRect</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span><span class="p">);</span>
  <span class="nx">ctx</span><span class="p">.</span><span class="nx">lineWidth</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
  <span class="nx">ctx</span><span class="p">.</span><span class="nx">strokeStyle</span> <span class="o">=</span> <span class="nx">theme</span><span class="p">.</span><span class="nx">border</span><span class="p">;</span>
  <span class="nx">ctx</span><span class="p">.</span><span class="nf">strokeRect</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">height</span><span class="p">);</span>
  <span class="nx">ctx</span><span class="p">.</span><span class="nx">fillStyle</span> <span class="o">=</span> <span class="nx">theme</span><span class="p">.</span><span class="nx">background</span><span class="p">;</span>
  <span class="nx">ctx</span><span class="p">.</span><span class="nf">fillRect</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">height</span><span class="p">);</span>
<span class="p">}</span>

<span class="nf">renderCanvas</span><span class="p">(</span><span class="nf">isDarkMode</span><span class="p">());</span>
<span class="nf">onDarkModeChange</span><span class="p">(</span><span class="nx">renderCanvas</span><span class="p">);</span>
</code></pre></div>
<p>This will make sure the function <code>renderCanvas</code> is called whenever the mode
changes. <a href="https://codepen.io/fnando/full/BaamVXG">You can see this example here</a>.</p>

<video loop controls width="978" height="754" poster="https://nandovieira.s3.amazonaws.com/media/dark-mode/match-media-listener.jpg">
  <source src="https://nandovieira.s3.amazonaws.com/media/dark-mode/match-media-listener.mp4" type="video/mp4">
  <source src="https://nandovieira.s3.amazonaws.com/media/dark-mode/match-media-listener.webm" type="video/webm">
</video>

<p>And that pretty ends what you need to know about the technical aspects of
supporting dark mode in the web.</p>
<h2 tabindex="-1" id="wrapping-up">Wrapping up<a class="anchor" href="#wrapping-up" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>Dark mode is the latest trend in tech, no doubt about it. And even without
having any serious research backing its so-called &ldquo;dark mode benefits&rdquo;, you may
consider doing it for the sake of your customers&rsquo; happiness. The technical
aspects are quite simple, but don&rsquo;t fool yourself: creating dark themes is a
very challenging process, specially when it comes to art direction for all
assets (including images and video).</p>

<p>Another thing to consider is whether you should automatically switch to dark
mode or use a configurable setting on your site. The latter is very simple to
implement by setting a class on an element (e.g.
<code>&lt;html data-theme=&quot;dark-mode&quot;&gt;</code>), but you&rsquo;d have to manually execute scripts and
change images/videos in case you switched from one mode to the other. Either
that, or not bothering at all, which would be (probably) fine in most cases.</p>
]]>
      </content:encoded>
      <link>https://nandovieira.com/supporting-dark-mode-in-web-content</link>
      <guid>https://nandovieira.com/supporting-dark-mode-in-web-content</guid>
      <pubDate>Thu, 31 Oct 2019 23:47:00 -0700</pubDate>
    </item>
    <item>
      <title>Setting up React Native on macOS Mojave</title>
      <description>
        <![CDATA[<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/react.svg" alt="React Native" class="align-right transparent" /></p>

<p>React Native is the go-to platform if you&rsquo;re versed in <a href="https://reactjs.org">React</a> and need to develop mobile applications. Instead of using a hybrid approach like several projects out there, React Native aims to develop native applications with the tooling we already use in web development.</p>

<p>For the most part, this approach works perfectly, specially if you&rsquo;re part of small team and lack the resources to develop full native code for both Apple and Android devices, but you need to be aware of its pros and cons, as in any other decision you have to make. Fortunately, <a href="https://medium.com/airbnb-engineering/react-native-at-airbnb-f95aa460be1c">Airbnb</a> published a very nice article covering this subject, so make sure you read it.</p>

<p>And after doing your own reserch, you decided that React Native will work just fine for your needs, so now what? Well, this article will show how to get your app up and running on simulators for both iOS and Android devices, as well as how to set up your project.</p>
<h2 tabindex="-1" id="installing-xcode">Installing Xcode<a class="anchor" href="#installing-xcode" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>To use iOS simulators, you&rsquo;ll need a Mac. You may be able to run your app using services like <a href="https://appetize.io">Appetize</a> (which I haven&rsquo;t tested), but given that my main development environment is macOS, this is what I&rsquo;ll focus on this article.</p>

<p>Many web developers out there are used to installing only the command-line tools, without the Xcode IDE, so they can build extensions or even use <a href="https://brew.sh">homebrew</a>. To use iOS simulators, you&rsquo;ll need the full thing, so go to the App Store and <a href="https://itunes.apple.com/us/app/xcode/id497799835?mt=12">install Xcode</a>.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/install-xcode.png" alt="App Store: Xcode app page"></p>

<p>This may take a while. When is done, open Xcode and install the extra components.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/install-xcode-addons.png" alt="Xcode: Installing additional components"></p>

<p>Now, you can install the iOS simulators. Go to &ldquo;Preferences &gt; Components&rdquo; and download as many simulators as you want. I usually install only the latest version, but that&rsquo;s on you and how far back you want to support.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/install-ios-simulators.png" alt="Xcode: Installing iOS simulators"></p>

<p>As far as Xcode goes, you&rsquo;re done! Now, let&rsquo;s set up the Android emulator.</p>
<h2 tabindex="-1" id="installing-android-studio">Installing Android Studio<a class="anchor" href="#installing-android-studio" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>Android&rsquo;s ecosystem is different than Apple&rsquo;s. First, you can install several third party emulators, some free, some paid. If you don&rsquo;t want to choose, just use <a href="https://developer.android.com/studio">Android Studio</a>, the official IDE.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/android-studio-site.png" alt="Android Studio website"></p>

<p>After downloading Android Studio, move the app to your <code>/Applications</code> folder.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/android-studio-move-to-applications.png" alt="Android Studio: Moving app to /Applications folder"></p>

<p>Now, open the app and follow the instructions. If you don&rsquo;t have an Android SDK available, you&rsquo;ll see a screen like the following:</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/install-android-sdk-01.png" alt="Android Studio Setup Wizard: No SDK detected"></p>

<p>Just click on &ldquo;Next&rdquo; when Android Studio will install it for you.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/install-android-sdk-02.png" alt="Android Studio Setup Wizard: Components Setup"></p>

<p>When is done, you be presented with a welcome screen.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/android-studio-welcome-screen.png" alt="Android Studio: Welcome Screen"></p>

<p>Go to &ldquo;Configure &gt; SDK Manager&rdquo;, then head to &ldquo;SDK Tools&rdquo;. Click on the checkbox to install the Build Tools, which will be used by React Native command-line tools. Click in &ldquo;Apply&rdquo; to install it.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/android-sdk-manager.png" alt="Android Studio: SDK Manager"></p>

<p>Now, notice the Android SDK Location available on the image above. You&rsquo;ll need this value to define an environment variable on the terminal. This is where things get tricky because you may have configured your terminal different than mine, but in general lines, you&rsquo;ll have to do one of the following:</p>

<ul>
<li>If you use bash, add the following lines to <code>~/.bashrc</code>.</li>
<li>If you use zsh, add the following lines to <code>~/.zshrc</code>.</li>
</ul>
<div class="highlight"><pre class="highlight shell"><code><span class="nb">export </span><span class="nv">JAVA_HOME</span><span class="o">=</span><span class="s2">"/Applications/Android Studio.app/Contents/jre/jdk/Contents/Home"</span>
<span class="nb">export </span><span class="nv">ANDROID_HOME</span><span class="o">=</span><span class="nv">$HOME</span>/Library/Android/sdk
<span class="nb">export </span><span class="nv">PATH</span><span class="o">=</span><span class="s2">"</span><span class="nv">$JAVA_HOME</span><span class="s2">/bin:</span><span class="nv">$ANDROID_HOME</span><span class="s2">/platform-tools:</span><span class="nv">$ANDROID_HOME</span><span class="s2">/emulator:</span><span class="nv">$PATH</span><span class="s2">"</span>
</code></pre></div>
<p>Restart your terminal to reload your configuration. You can check your configurations like the following:</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span><span class="nb">echo</span> <span class="nv">$JAVA_HOME</span>
<span class="go">/Applications/Android Studio.app/Contents/jre/jdk/Contents/Home

</span><span class="gp">$</span><span class="w"> </span><span class="nb">echo</span> <span class="nv">$ANDROID_HOME</span>
<span class="go">/Users/fnando/Library/Android/sdk

</span><span class="gp">$</span><span class="w"> </span>which java
<span class="go">/Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java

</span><span class="gp">$</span><span class="w"> </span>which adb
<span class="go">/Users/fnando/Library/Android/sdk/platform-tools/adb

</span><span class="gp">$</span><span class="w"> </span>which emulator
<span class="go">/Users/fnando/Library/Android/sdk/emulator/emulator
</span></code></pre></div>
<p>If you see anything too far from the above output, make sure you added the <code>export</code> lines to the correct files and restarted your terminal.</p>

<p>Finally, you can create a virtual device. Back to the welcome screen, go to &ldquo;Configure &gt; AVD Manager&rdquo;.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/android-virtual-device-manager.png" alt="Android Studio: Virtual Device Manager"></p>

<p>Click on &ldquo;Create Virtual Device&rdquo; and select a device definition. In this example I&rsquo;m selecting Pixel 3.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/android-avd-profile-chooser.png" alt="Android Studio: Virtual Device Profile Chooser"></p>

<p>When you&rsquo;re done, click on &ldquo;Next&rdquo;. Now you have to choose which Android version you&rsquo;re going to use. You can go with the latest stable version available, which right now is <a href="https://www.android.com/versions/pie-9-0/">Android Pie</a>. Make sure you click the &ldquo;Download&rdquo; link.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/android-pie.png" alt="Android Studio: System Image Selection"></p>

<p>After downloading the system image, click on &ldquo;Next&rdquo; once more. You&rsquo;ll be presented with the device profile you&rsquo;re creating. Just click &ldquo;Finish&rdquo;.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/android-pie-virtual-device.png" alt="Android Studio: Pie System Image"></p>

<p>Now you&rsquo;re back to the list of virtual devices available on your computer and you can always create more.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/android-avd-list.png" alt="Android Studio: Virtual Device List"></p>

<p>To start the emulator, click the play button available under the &ldquo;Actions&rdquo; column. Always remember to start the emulator by clicking the play button; otherwise, you&rsquo;ll see a message like <code>No connected devices!</code> when trying to run React Native on the Android emulator.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/android-emulator-running.png" alt="Android Studio: Android Emulator Running"></p>

<p>You can also start the emulator from the command-line. All you have to do is using the <code>emulator</code> command.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>emulator <span class="nt">-list-avds</span>
<span class="go">Pixel_3_API_28

</span><span class="gp">$</span><span class="w"> </span>emulator <span class="nt">-avd</span> <span class="s1">'Pixel_3_API_28'</span>
<span class="go">emulator: INFO: boot completed
emulator: INFO: boot time 34488 ms
emulator: Increasing screen off timeout, logcat buffer size to 2M.
</span></code></pre></div>
<p>Congrats! You&rsquo;re done with configuring emulators. Now, let&rsquo;s spin up a sample app and run it on both simulators.</p>
<h2 tabindex="-1" id="configuring-react-native">Configuring React Native<a class="anchor" href="#configuring-react-native" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>The official documentation recommends installing react-native-cli globally, but I avoid installing global packages. We&rsquo;re going to use <code>npx</code> to generate the app skeleton.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>npx react-native-cli init sample
<span class="c">...
</span><span class="go">
✨  Done in 5.90s.

  Run instructions for iOS:
    • cd /Users/fnando/Projects/sample &amp;&amp; react-native run-ios
    - or -
    • Open ios/sample.xcodeproj in Xcode
    • Hit the Run button

  Run instructions for Android:
    • Have an Android emulator running (quickest way to get started), or a device connected.
    • cd /Users/fnando/Projects/sample &amp;&amp; react-native run-android
</span></code></pre></div>
<p>Go to the projects directory with <code>cd sample</code> and run <code>react-native run-ios</code>. This will compile the app, install it on the emulator and run a separate tab with <a href="https://github.com/facebook/metro">Metro</a>, the JavaScript bundler for React Native developed by Facebook.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>react-native run-ios
<span class="c">...
</span><span class="go">info + exit 0

info

info ** BUILD SUCCEEDED **


info Installing build/sample/Build/Products/Debug-iphonesimulator/sample.app
info Launching org.reactjs.native.example.sample
org.reactjs.native.example.sample: 5043
</span></code></pre></div>
<p>This is the tab you should keep an eye on because any errors while running your app will be outputted to it. After everything is up and running, you&rsquo;ll be able to see the sample app running on the iOS simulator.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/sample-ios-simulator.png" alt="Sample app running on iOS simulator"></p>

<p>Great! What about Android simulator? Make sure you have the virtual device running (remember, from the Android Studio&rsquo;s welcome screen, choose &ldquo;Configure &gt; AVD Manager&rdquo;, then press the play button). You can check for running devices by executing <code>adb devices</code>.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>adb devices
<span class="go">List of devices attached
emulator-5554 device
</span></code></pre></div>
<p>Now, you can run <code>react-native run-android</code>. This will also compile a bunch of stuff and initialize the emulator.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>react-native run-android
<span class="c">...
</span><span class="go">
BUILD SUCCESSFUL in 14s
26 actionable tasks: 26 executed
info Running /Users/fnando/Library/Android/sdk/platform-tools/adb -s emulator-5554 reverse tcp:8081 tcp:8081
info Starting the app on emulator-5554 (/Users/fnando/Library/Android/sdk/platform-tools/adb -s emulator-5554 shell am start -n com.sample/com.sample.MainActivity)...
Starting: Intent { cmp=com.sample/.MainActivity }
</span></code></pre></div>
<p>You&rsquo;ll be able to see the app running on the Android simulator if everything went smoothly.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/sample-android-simulator.png" alt="Sample app running on Android simulator"></p>
<h2 tabindex="-1" id="running-physical-devices">Running physical devices<a class="anchor" href="#running-physical-devices" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>At some point you&rsquo;re better off testing your apps on physical devices. This can help you fix bad user experience that wouldn&rsquo;t bother on simulators.</p>
<h3 tabindex="-1" id="running-the-app-on-your-iphone">Running the app on your iPhone<a class="anchor" href="#running-the-app-on-your-iphone" aria-hidden="true" tabindex="-1"></a>
</h3>
<p>To run your app on the iPhone, open <code>ios/sample.xcodeproj</code> on Xcode. You can use <code>open ios/sample.xcodeproj</code> to open this file from your terminal.</p>

<p>First, you have to select a developer profile. Click on the project name and go to the target you&rsquo;re building (in this case, <code>sample</code>). Change the bundle identifier to your own domain, otherwise you won&rsquo;t be able to build the project. Then click &ldquo;Add Account&rdquo;, enter your Apple ID and password, and select your name under the dropdown. You may also have to select your developer account for the <code>sampleTests</code> target.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/xcode-developer-account.png" alt="Xcode developer account"></p>

<p>Connect your iPhone to the computer and click the simulator selector.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/xcode-simulator-dropdown.png" alt="Xcode&#39;s simulator selector"></p>

<p>Your device will be available on the top of the list.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/xcode-simulator-physical-device.png" alt="Physical iOS device selected"></p>

<p>Finally, click the &ldquo;Build and Run&rdquo; button, or press <kbd>cmd-R</kbd>. This will install and open the app on your iPhone.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/app-running-on-iphone-8.jpg" alt="Running app on physical iPhone 8"></p>
<h3 tabindex="-1" id="running-the-app-on-your-android">Running the app on your Android<a class="anchor" href="#running-the-app-on-your-android" aria-hidden="true" tabindex="-1"></a>
</h3>
<p>I didn&rsquo;t have an Android device, but decided to buy one to test the app on the real thing. After some research, I decided to buy a <a href="https://www.mi.com/global/mi-a2/">Xiaomi Mi A2</a> which costed me around $170 on Amazon. It&rsquo;s a very nice device, even for daily usage. The good thing about it is that it comes with stock Android, and not the shitty modified version that some companies ship (looking at you, Samsung).</p>

<p>First, make sure Developer Options is enabled. Here, things can get tricky. Different devices can be activated differently. In my case, I had to go to &ldquo;Settings &gt; About phone&rdquo; and tap the Build number 7 times in order to activate the developer mode. Then go to &ldquo;Settings &gt; System &gt; Advanced &gt; Developer Options&rdquo; and activate &ldquo;USB Debugging&rdquo;.</p>

<p>To verify that your device is ready, run <code>adb devices</code> on your terminal.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>adb devices
<span class="go">List of devices attached
bbb5cc28  device
</span></code></pre></div>
<p>Now, run <code>react-native run-android</code>. This command will install and open the app on your Android device.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/app-running-on-android-device.jpg" alt="Running app on physical Android device"></p>
<h2 tabindex="-1" id="using-typescript">Using TypeScript<a class="anchor" href="#using-typescript" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>This step is optional, but if you&rsquo;re planning to release your app as an open-source project you may consider using <a href="https://www.typescriptlang.org">TypeScript</a>, the typed language that compiles down to JavaScript, created by Microsoft.</p>

<p>For new projects, all you have to do is running the generator with <code>--template typescript</code>.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>npx react-native-cli init sample <span class="nt">--template</span> typescript
<span class="c">...
</span><span class="go">✨  Done in 4.96s.

  Run instructions for iOS:
    • cd /Users/fnando/Projects/sample_ts &amp;&amp; react-native run-ios
    - or -
    • Open ios/sample_ts.xcodeproj in Xcode
    • Hit the Run button

  Run instructions for Android:
    • Have an Android emulator running (quickest way to get started), or a device connected.
    • cd /Users/fnando/Projects/sample_ts &amp;&amp; react-native run-android
</span></code></pre></div>
<p>For existing projects, you&rsquo;ll need to manually configure everything that the TypeScript template provides. I won&rsquo;t cover this migration process here, so make sure you <a href="https://facebook.github.io/react-native/blog/2018/05/07/using-typescript-with-react-native">read the article</a> published on the official blog.</p>
<h2 tabindex="-1" id="wrapping-up">Wrapping up<a class="anchor" href="#wrapping-up" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>Despite all the efforts on developing the ecosystem, React Native is still immature and you&rsquo;ll find that developing apps can be challenging. But even with all these difficulties, I consider React Native the best solution for small companies/teams that need to develop native apps.</p>

<p>Before deep diving into React Native, ask yourself if a <a href="https://developers.google.com/web/progressive-web-apps/"><abbr title="Progressive Web App">PWA</abbr></a> is a viable solution. Unfortunaly, <abbr title="Progressive Web App">PWA</abbr> comes with its own challenges, like a different mindset for installing apps and inconsistencies between Android and iOS, as well as several device limitations, but it may be a good first step towards mobiles apps when responsive web is not enough, but React Native is too much.</p>
]]>
      </description>
      <content:encoded>
        <![CDATA[<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/react.svg" alt="React Native" class="align-right transparent" /></p>

<p>React Native is the go-to platform if you&rsquo;re versed in <a href="https://reactjs.org">React</a> and need to develop mobile applications. Instead of using a hybrid approach like several projects out there, React Native aims to develop native applications with the tooling we already use in web development.</p>

<p>For the most part, this approach works perfectly, specially if you&rsquo;re part of small team and lack the resources to develop full native code for both Apple and Android devices, but you need to be aware of its pros and cons, as in any other decision you have to make. Fortunately, <a href="https://medium.com/airbnb-engineering/react-native-at-airbnb-f95aa460be1c">Airbnb</a> published a very nice article covering this subject, so make sure you read it.</p>

<p>And after doing your own reserch, you decided that React Native will work just fine for your needs, so now what? Well, this article will show how to get your app up and running on simulators for both iOS and Android devices, as well as how to set up your project.</p>
<h2 tabindex="-1" id="installing-xcode">Installing Xcode<a class="anchor" href="#installing-xcode" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>To use iOS simulators, you&rsquo;ll need a Mac. You may be able to run your app using services like <a href="https://appetize.io">Appetize</a> (which I haven&rsquo;t tested), but given that my main development environment is macOS, this is what I&rsquo;ll focus on this article.</p>

<p>Many web developers out there are used to installing only the command-line tools, without the Xcode IDE, so they can build extensions or even use <a href="https://brew.sh">homebrew</a>. To use iOS simulators, you&rsquo;ll need the full thing, so go to the App Store and <a href="https://itunes.apple.com/us/app/xcode/id497799835?mt=12">install Xcode</a>.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/install-xcode.png" alt="App Store: Xcode app page"></p>

<p>This may take a while. When is done, open Xcode and install the extra components.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/install-xcode-addons.png" alt="Xcode: Installing additional components"></p>

<p>Now, you can install the iOS simulators. Go to &ldquo;Preferences &gt; Components&rdquo; and download as many simulators as you want. I usually install only the latest version, but that&rsquo;s on you and how far back you want to support.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/install-ios-simulators.png" alt="Xcode: Installing iOS simulators"></p>

<p>As far as Xcode goes, you&rsquo;re done! Now, let&rsquo;s set up the Android emulator.</p>
<h2 tabindex="-1" id="installing-android-studio">Installing Android Studio<a class="anchor" href="#installing-android-studio" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>Android&rsquo;s ecosystem is different than Apple&rsquo;s. First, you can install several third party emulators, some free, some paid. If you don&rsquo;t want to choose, just use <a href="https://developer.android.com/studio">Android Studio</a>, the official IDE.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/android-studio-site.png" alt="Android Studio website"></p>

<p>After downloading Android Studio, move the app to your <code>/Applications</code> folder.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/android-studio-move-to-applications.png" alt="Android Studio: Moving app to /Applications folder"></p>

<p>Now, open the app and follow the instructions. If you don&rsquo;t have an Android SDK available, you&rsquo;ll see a screen like the following:</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/install-android-sdk-01.png" alt="Android Studio Setup Wizard: No SDK detected"></p>

<p>Just click on &ldquo;Next&rdquo; when Android Studio will install it for you.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/install-android-sdk-02.png" alt="Android Studio Setup Wizard: Components Setup"></p>

<p>When is done, you be presented with a welcome screen.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/android-studio-welcome-screen.png" alt="Android Studio: Welcome Screen"></p>

<p>Go to &ldquo;Configure &gt; SDK Manager&rdquo;, then head to &ldquo;SDK Tools&rdquo;. Click on the checkbox to install the Build Tools, which will be used by React Native command-line tools. Click in &ldquo;Apply&rdquo; to install it.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/android-sdk-manager.png" alt="Android Studio: SDK Manager"></p>

<p>Now, notice the Android SDK Location available on the image above. You&rsquo;ll need this value to define an environment variable on the terminal. This is where things get tricky because you may have configured your terminal different than mine, but in general lines, you&rsquo;ll have to do one of the following:</p>

<ul>
<li>If you use bash, add the following lines to <code>~/.bashrc</code>.</li>
<li>If you use zsh, add the following lines to <code>~/.zshrc</code>.</li>
</ul>
<div class="highlight"><pre class="highlight shell"><code><span class="nb">export </span><span class="nv">JAVA_HOME</span><span class="o">=</span><span class="s2">"/Applications/Android Studio.app/Contents/jre/jdk/Contents/Home"</span>
<span class="nb">export </span><span class="nv">ANDROID_HOME</span><span class="o">=</span><span class="nv">$HOME</span>/Library/Android/sdk
<span class="nb">export </span><span class="nv">PATH</span><span class="o">=</span><span class="s2">"</span><span class="nv">$JAVA_HOME</span><span class="s2">/bin:</span><span class="nv">$ANDROID_HOME</span><span class="s2">/platform-tools:</span><span class="nv">$ANDROID_HOME</span><span class="s2">/emulator:</span><span class="nv">$PATH</span><span class="s2">"</span>
</code></pre></div>
<p>Restart your terminal to reload your configuration. You can check your configurations like the following:</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span><span class="nb">echo</span> <span class="nv">$JAVA_HOME</span>
<span class="go">/Applications/Android Studio.app/Contents/jre/jdk/Contents/Home

</span><span class="gp">$</span><span class="w"> </span><span class="nb">echo</span> <span class="nv">$ANDROID_HOME</span>
<span class="go">/Users/fnando/Library/Android/sdk

</span><span class="gp">$</span><span class="w"> </span>which java
<span class="go">/Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java

</span><span class="gp">$</span><span class="w"> </span>which adb
<span class="go">/Users/fnando/Library/Android/sdk/platform-tools/adb

</span><span class="gp">$</span><span class="w"> </span>which emulator
<span class="go">/Users/fnando/Library/Android/sdk/emulator/emulator
</span></code></pre></div>
<p>If you see anything too far from the above output, make sure you added the <code>export</code> lines to the correct files and restarted your terminal.</p>

<p>Finally, you can create a virtual device. Back to the welcome screen, go to &ldquo;Configure &gt; AVD Manager&rdquo;.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/android-virtual-device-manager.png" alt="Android Studio: Virtual Device Manager"></p>

<p>Click on &ldquo;Create Virtual Device&rdquo; and select a device definition. In this example I&rsquo;m selecting Pixel 3.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/android-avd-profile-chooser.png" alt="Android Studio: Virtual Device Profile Chooser"></p>

<p>When you&rsquo;re done, click on &ldquo;Next&rdquo;. Now you have to choose which Android version you&rsquo;re going to use. You can go with the latest stable version available, which right now is <a href="https://www.android.com/versions/pie-9-0/">Android Pie</a>. Make sure you click the &ldquo;Download&rdquo; link.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/android-pie.png" alt="Android Studio: System Image Selection"></p>

<p>After downloading the system image, click on &ldquo;Next&rdquo; once more. You&rsquo;ll be presented with the device profile you&rsquo;re creating. Just click &ldquo;Finish&rdquo;.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/android-pie-virtual-device.png" alt="Android Studio: Pie System Image"></p>

<p>Now you&rsquo;re back to the list of virtual devices available on your computer and you can always create more.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/android-avd-list.png" alt="Android Studio: Virtual Device List"></p>

<p>To start the emulator, click the play button available under the &ldquo;Actions&rdquo; column. Always remember to start the emulator by clicking the play button; otherwise, you&rsquo;ll see a message like <code>No connected devices!</code> when trying to run React Native on the Android emulator.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/android-emulator-running.png" alt="Android Studio: Android Emulator Running"></p>

<p>You can also start the emulator from the command-line. All you have to do is using the <code>emulator</code> command.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>emulator <span class="nt">-list-avds</span>
<span class="go">Pixel_3_API_28

</span><span class="gp">$</span><span class="w"> </span>emulator <span class="nt">-avd</span> <span class="s1">'Pixel_3_API_28'</span>
<span class="go">emulator: INFO: boot completed
emulator: INFO: boot time 34488 ms
emulator: Increasing screen off timeout, logcat buffer size to 2M.
</span></code></pre></div>
<p>Congrats! You&rsquo;re done with configuring emulators. Now, let&rsquo;s spin up a sample app and run it on both simulators.</p>
<h2 tabindex="-1" id="configuring-react-native">Configuring React Native<a class="anchor" href="#configuring-react-native" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>The official documentation recommends installing react-native-cli globally, but I avoid installing global packages. We&rsquo;re going to use <code>npx</code> to generate the app skeleton.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>npx react-native-cli init sample
<span class="c">...
</span><span class="go">
✨  Done in 5.90s.

  Run instructions for iOS:
    • cd /Users/fnando/Projects/sample &amp;&amp; react-native run-ios
    - or -
    • Open ios/sample.xcodeproj in Xcode
    • Hit the Run button

  Run instructions for Android:
    • Have an Android emulator running (quickest way to get started), or a device connected.
    • cd /Users/fnando/Projects/sample &amp;&amp; react-native run-android
</span></code></pre></div>
<p>Go to the projects directory with <code>cd sample</code> and run <code>react-native run-ios</code>. This will compile the app, install it on the emulator and run a separate tab with <a href="https://github.com/facebook/metro">Metro</a>, the JavaScript bundler for React Native developed by Facebook.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>react-native run-ios
<span class="c">...
</span><span class="go">info + exit 0

info

info ** BUILD SUCCEEDED **


info Installing build/sample/Build/Products/Debug-iphonesimulator/sample.app
info Launching org.reactjs.native.example.sample
org.reactjs.native.example.sample: 5043
</span></code></pre></div>
<p>This is the tab you should keep an eye on because any errors while running your app will be outputted to it. After everything is up and running, you&rsquo;ll be able to see the sample app running on the iOS simulator.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/sample-ios-simulator.png" alt="Sample app running on iOS simulator"></p>

<p>Great! What about Android simulator? Make sure you have the virtual device running (remember, from the Android Studio&rsquo;s welcome screen, choose &ldquo;Configure &gt; AVD Manager&rdquo;, then press the play button). You can check for running devices by executing <code>adb devices</code>.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>adb devices
<span class="go">List of devices attached
emulator-5554 device
</span></code></pre></div>
<p>Now, you can run <code>react-native run-android</code>. This will also compile a bunch of stuff and initialize the emulator.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>react-native run-android
<span class="c">...
</span><span class="go">
BUILD SUCCESSFUL in 14s
26 actionable tasks: 26 executed
info Running /Users/fnando/Library/Android/sdk/platform-tools/adb -s emulator-5554 reverse tcp:8081 tcp:8081
info Starting the app on emulator-5554 (/Users/fnando/Library/Android/sdk/platform-tools/adb -s emulator-5554 shell am start -n com.sample/com.sample.MainActivity)...
Starting: Intent { cmp=com.sample/.MainActivity }
</span></code></pre></div>
<p>You&rsquo;ll be able to see the app running on the Android simulator if everything went smoothly.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/sample-android-simulator.png" alt="Sample app running on Android simulator"></p>
<h2 tabindex="-1" id="running-physical-devices">Running physical devices<a class="anchor" href="#running-physical-devices" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>At some point you&rsquo;re better off testing your apps on physical devices. This can help you fix bad user experience that wouldn&rsquo;t bother on simulators.</p>
<h3 tabindex="-1" id="running-the-app-on-your-iphone">Running the app on your iPhone<a class="anchor" href="#running-the-app-on-your-iphone" aria-hidden="true" tabindex="-1"></a>
</h3>
<p>To run your app on the iPhone, open <code>ios/sample.xcodeproj</code> on Xcode. You can use <code>open ios/sample.xcodeproj</code> to open this file from your terminal.</p>

<p>First, you have to select a developer profile. Click on the project name and go to the target you&rsquo;re building (in this case, <code>sample</code>). Change the bundle identifier to your own domain, otherwise you won&rsquo;t be able to build the project. Then click &ldquo;Add Account&rdquo;, enter your Apple ID and password, and select your name under the dropdown. You may also have to select your developer account for the <code>sampleTests</code> target.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/xcode-developer-account.png" alt="Xcode developer account"></p>

<p>Connect your iPhone to the computer and click the simulator selector.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/xcode-simulator-dropdown.png" alt="Xcode&#39;s simulator selector"></p>

<p>Your device will be available on the top of the list.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/xcode-simulator-physical-device.png" alt="Physical iOS device selected"></p>

<p>Finally, click the &ldquo;Build and Run&rdquo; button, or press <kbd>cmd-R</kbd>. This will install and open the app on your iPhone.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/app-running-on-iphone-8.jpg" alt="Running app on physical iPhone 8"></p>
<h3 tabindex="-1" id="running-the-app-on-your-android">Running the app on your Android<a class="anchor" href="#running-the-app-on-your-android" aria-hidden="true" tabindex="-1"></a>
</h3>
<p>I didn&rsquo;t have an Android device, but decided to buy one to test the app on the real thing. After some research, I decided to buy a <a href="https://www.mi.com/global/mi-a2/">Xiaomi Mi A2</a> which costed me around $170 on Amazon. It&rsquo;s a very nice device, even for daily usage. The good thing about it is that it comes with stock Android, and not the shitty modified version that some companies ship (looking at you, Samsung).</p>

<p>First, make sure Developer Options is enabled. Here, things can get tricky. Different devices can be activated differently. In my case, I had to go to &ldquo;Settings &gt; About phone&rdquo; and tap the Build number 7 times in order to activate the developer mode. Then go to &ldquo;Settings &gt; System &gt; Advanced &gt; Developer Options&rdquo; and activate &ldquo;USB Debugging&rdquo;.</p>

<p>To verify that your device is ready, run <code>adb devices</code> on your terminal.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>adb devices
<span class="go">List of devices attached
bbb5cc28  device
</span></code></pre></div>
<p>Now, run <code>react-native run-android</code>. This command will install and open the app on your Android device.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/react-native-setup/app-running-on-android-device.jpg" alt="Running app on physical Android device"></p>
<h2 tabindex="-1" id="using-typescript">Using TypeScript<a class="anchor" href="#using-typescript" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>This step is optional, but if you&rsquo;re planning to release your app as an open-source project you may consider using <a href="https://www.typescriptlang.org">TypeScript</a>, the typed language that compiles down to JavaScript, created by Microsoft.</p>

<p>For new projects, all you have to do is running the generator with <code>--template typescript</code>.</p>
<div class="highlight"><pre class="highlight console"><code><span class="gp">$</span><span class="w"> </span>npx react-native-cli init sample <span class="nt">--template</span> typescript
<span class="c">...
</span><span class="go">✨  Done in 4.96s.

  Run instructions for iOS:
    • cd /Users/fnando/Projects/sample_ts &amp;&amp; react-native run-ios
    - or -
    • Open ios/sample_ts.xcodeproj in Xcode
    • Hit the Run button

  Run instructions for Android:
    • Have an Android emulator running (quickest way to get started), or a device connected.
    • cd /Users/fnando/Projects/sample_ts &amp;&amp; react-native run-android
</span></code></pre></div>
<p>For existing projects, you&rsquo;ll need to manually configure everything that the TypeScript template provides. I won&rsquo;t cover this migration process here, so make sure you <a href="https://facebook.github.io/react-native/blog/2018/05/07/using-typescript-with-react-native">read the article</a> published on the official blog.</p>
<h2 tabindex="-1" id="wrapping-up">Wrapping up<a class="anchor" href="#wrapping-up" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>Despite all the efforts on developing the ecosystem, React Native is still immature and you&rsquo;ll find that developing apps can be challenging. But even with all these difficulties, I consider React Native the best solution for small companies/teams that need to develop native apps.</p>

<p>Before deep diving into React Native, ask yourself if a <a href="https://developers.google.com/web/progressive-web-apps/"><abbr title="Progressive Web App">PWA</abbr></a> is a viable solution. Unfortunaly, <abbr title="Progressive Web App">PWA</abbr> comes with its own challenges, like a different mindset for installing apps and inconsistencies between Android and iOS, as well as several device limitations, but it may be a good first step towards mobiles apps when responsive web is not enough, but React Native is too much.</p>
]]>
      </content:encoded>
      <link>https://nandovieira.com/setting-up-react-native-on-macos-mojave</link>
      <guid>https://nandovieira.com/setting-up-react-native-on-macos-mojave</guid>
      <pubDate>Mon, 06 May 2019 14:38:00 -0700</pubDate>
    </item>
    <item>
      <title>Using ES2015 with Asset Pipeline on Ruby on Rails</title>
      <description>
        <![CDATA[<p>JavaScript is all new. Until recently, we had no new features. The last significant update was back in 2009, with <abbr title="ECMAScript 5">ES5</abbr>&lsquo;s release. And you couldn&rsquo;t use all features due to browser incompatibility.</p>

<p>To increase the compatibility level, we had to use things like <a href="https://github.com/es-shims/es5-shim/">es5-shim</a>, which conditionally checked if a feature was available, adding a <em>polyfill</em> if the browser didn&rsquo;t implement it.</p>

<p>And then the first pre-processors came in, like <a href="http://coffeescript.org">CoffeeScript</a>. You could write different constructions, that were compiled to code that the browsers could actually understand.</p>

<p>Interestingly, <a href="https://brendaneich.com">Brendan Eich</a> announced in 2009 a new JavaScript version called <em>Harmony</em>, which is now called <abbr title="ECMAScript 2015">ES2015</abbr><sup id="fnref1"><a href="#fn1">1</a></sup>. The first drafts were published in 2011, but the final specification was released in June of 2015.</p>

<p><abbr title="ECMAScript 2015">ES2015</abbr> has so many new features:</p>

<ul>
<li>Class definition</li>
<li>String interpolation</li>
<li>Fat arrow functions</li>
<li><a href="http://babeljs.io/docs/learn-es2015/">More!</a></li>
</ul>

<p><a href="https://kangax.github.io/compat-table/es6/">Browsers are implementing</a> <abbr title="ECMAScript 2015">ES2015</abbr> in a fast pace, but it&rsquo;ll take some time until we can actually use it. Maybe years. Fortunately, we have <a href="https://babeljs.io">Babel.js</a>. We can use all these new features today without worrying with browser compatibility.</p>

<p>Babel.js<sup id="fnref2"><a href="#fn2">2</a></sup> is just a pre-processor. You write code that uses these new features (and more), which will be exported as code that browsers can understand, even those that don&rsquo;t fully understand <abbr title="ECMAScript 2015">ES2015</abbr>.</p>
<h2 tabindex="-1" id="using-babel-js-with-asset-pipeline">Using Babel.js with Asset Pipeline<a class="anchor" href="#using-babel-js-with-asset-pipeline" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>There are several ways you can use to precompile your JavaScript code. Some people use Babel&rsquo;s <abbr title="Command-Line Interface">CLI</abbr>. Some people prefer <em>builders</em> like <a href="http://gruntjs.com">Grunt</a> or <a href="http://gulpjs.com">Gulp</a> to automate the compilation process. But if you&rsquo;re using Ruby on Rails you&rsquo;re more likely to use <a href="http://guides.rubyonrails.org/asset_pipeline.html">Asset Pipeline</a> for front-end assets compilation. And this is, in my opinion, the easiest way of using Babel, believe it or not.</p>

<p>Unfortunately there&rsquo;s no built-in support on the stable release of <a href="https://github.com/rails/sprockets">Sprockets</a>, so you&rsquo;ll have to use a pre-release version.</p>

<p>The transpilation is performed by <a href="https://rubygems.org/gems/babel-schmooze-sprockets">babel-schmooze-sprockets</a>. It vendors several Babel extensions so that you can use Babel without having to deal with NPM on your application (you&rsquo;ll still need Node.js though).</p>

<p>Update your <code>Gemfile</code> to include these dependencies.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="n">source</span> <span class="s2">"https://rubygems.org"</span>

<span class="n">gem</span> <span class="s2">"rails"</span><span class="p">,</span> <span class="s2">"4.2.6"</span>
<span class="n">gem</span> <span class="s2">"sqlite3"</span>
<span class="n">gem</span> <span class="s2">"uglifier"</span><span class="p">,</span> <span class="s2">"&gt;= 1.3.0"</span>

<span class="n">gem</span> <span class="s2">"sprockets"</span><span class="p">,</span> <span class="s2">"~&gt; 4.x"</span>
<span class="n">gem</span> <span class="s2">"babel-schmooze-sprockets"</span>

<span class="n">gem</span> <span class="s2">"turbolinks"</span><span class="p">,</span> <span class="s2">"~&gt; 5.x"</span>
<span class="n">gem</span> <span class="s2">"jquery-rails"</span>
</code></pre></div>
<p>That&rsquo;s it! Now all <code>.es6</code> files will be compiled using Babel. Create a file at <code>app/assets/javascripts/hello.es6</code> with the following code:</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="kd">class</span> <span class="nc">Hello</span> <span class="p">{</span>
  <span class="nf">constructor</span><span class="p">()</span> <span class="p">{</span>
    <span class="nf">alert</span><span class="p">(</span><span class="dl">"</span><span class="s2">Hello!</span><span class="dl">"</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="k">new</span> <span class="nc">Hello</span><span class="p">();</span>
</code></pre></div>
<p>Make sure you&rsquo;re loading <code>hello.es6</code> at <code>app/assets/javascripts/application.js</code>. You also need to load Babel helpers; they&rsquo;ll be used to reduce the amount of generated code.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="c1">//= require babel</span>
<span class="c1">//= require_tree .</span>
<span class="c1">//= require_self</span>
</code></pre></div>
<p>Now if you access the page on your browser, you&rsquo;ll see an <code>alert</code> box like this:</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/es6-alert-hello.png" alt="Alert box - Hello"></p>

<p>If you want the new <a href="https://babeljs.io/docs/usage/modules/">module system</a>, you still have some things to configure.</p>
<h3 tabindex="-1" id="using-es2015-modules">Using <abbr title="ECMAScript 2015">ES2015</abbr> modules<a class="anchor" href="#using-es2015-modules" aria-hidden="true" tabindex="-1"></a>
</h3>
<p><abbr title="ECMAScript 2015">ES2015</abbr> introduced module support; instead of defining your code in the global scope, you can use a scope per file. Importing modules is pretty straightforward:</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="k">import</span> <span class="nx">Foo</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">foo</span><span class="dl">"</span><span class="p">;</span>
</code></pre></div>
<p>This will load <code>Foo</code> from <code>foo.js</code>. One single file can export several units (functions, objects, anything you want), and you don&rsquo;t have to load everything at once, pretty much like <a href="http://python.org">Python</a>.</p>

<p>Babel has no idea of how these modules should be exported, and by default, will use the <a href="http://www.commonjs.org/specs/modules/1.0/">CommonJS</a> format, which can&rsquo;t be used by the browser, so we&rsquo;ll use another approach.</p>

<p>The easiest way is using <a href="http://requirejs.org/docs/whyamd.html">AMD</a>, and for this we&rsquo;ll use <a href="https://github.com/jrburke/almond">almond</a>. You can install almond with whatever you&rsquo;re using for managing packages; in this article I&rsquo;ll use <a href="http://rails-assets.org">http://rails-assets.org</a>, a bower-to-rubygems converter. Update your <code>Gemfile</code> like the following:</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="n">source</span> <span class="s2">"https://rubygems.org"</span>

<span class="n">gem</span> <span class="s2">"rails"</span><span class="p">,</span> <span class="s2">"4.2.6"</span>
<span class="n">gem</span> <span class="s2">"sqlite3"</span>
<span class="n">gem</span> <span class="s2">"uglifier"</span><span class="p">,</span> <span class="s2">"&gt;= 1.3.0"</span>

<span class="n">gem</span> <span class="s2">"sprockets"</span><span class="p">,</span> <span class="s2">"~&gt; 4.x"</span>
<span class="n">gem</span> <span class="s2">"babel-schmooze-sprockets"</span>

<span class="n">gem</span> <span class="s2">"turbolinks"</span><span class="p">,</span> <span class="s2">"~&gt; 5.x"</span>
<span class="n">gem</span> <span class="s2">"jquery-rails"</span>

<span class="n">source</span> <span class="s2">"https://rails-assets.org"</span> <span class="k">do</span>
  <span class="n">gem</span> <span class="s2">"rails-assets-almond"</span>
<span class="k">end</span>
</code></pre></div>
<p>Finally, update <code>app/assets/javascripts/application.js</code> so it loads almond.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="c1">//= require turbolinks</span>
<span class="c1">//= require almond</span>
<span class="c1">//= require jquery</span>
<span class="c1">//= require_tree .</span>
<span class="c1">//= require_self</span>
</code></pre></div>
<p>Notice that Turbolinks must be loaded before Almond; this happens because Turbolinks is an anonymous AMD module. For more information, check the section &ldquo;Almond.js gotcha&rdquo;, later on this article.</p>

<p>Now you have to think about the execution process. Are you going to use some code dispatcher? Or execute code based on the view that is being rendered? Are you going to create your own execution mechanism? The answer depends on your own workflow, so I&rsquo;m not going to give you too many alternatives here.</p>

<p>We&rsquo;re going to create a boot script that uses the controller and action names to execute the JavaScript you need for that specific page. Just add the <code>require</code> call to your <code>app/assets/javascripts/application.js</code> file.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="c1">//= require turbolinks</span>
<span class="c1">//= require almond</span>
<span class="c1">//= require jquery</span>
<span class="c1">//= require_tree .</span>
<span class="c1">//= require_self</span>

<span class="nf">require</span><span class="p">([</span><span class="dl">"</span><span class="s2">application/boot</span><span class="dl">"</span><span class="p">]);</span>
</code></pre></div>
<p>You have to create <code>app/assets/javascripts/application/boot.es6</code>. I&rsquo;ll listen to some events, like DOM&rsquo;s <code>ready</code> and Turbolinks&rsquo; <code>turbolinks:load</code>.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="k">import</span> <span class="nx">$</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">jquery</span><span class="dl">"</span><span class="p">;</span>

<span class="kd">function</span> <span class="nf">runner</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">var</span> <span class="nx">path</span> <span class="o">=</span> <span class="nf">$</span><span class="p">(</span><span class="dl">"</span><span class="s2">body</span><span class="dl">"</span><span class="p">).</span><span class="nf">data</span><span class="p">(</span><span class="dl">"</span><span class="s2">route</span><span class="dl">"</span><span class="p">);</span>

  <span class="c1">// Load script for this page.</span>
  <span class="c1">// We should use System.import, but it's not worth the trouble, so</span>
  <span class="c1">// let's use almond's require instead.</span>
  <span class="k">try</span> <span class="p">{</span>
    <span class="nf">require</span><span class="p">([</span><span class="nx">path</span><span class="p">],</span> <span class="nx">onload</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">catch </span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
    <span class="nf">handleError</span><span class="p">(</span><span class="nx">error</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nf">onload</span><span class="p">(</span><span class="nx">mod</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// Assign the default module.</span>
  <span class="kd">var</span> <span class="nx">Page</span> <span class="o">=</span> <span class="nx">mod</span><span class="p">.</span><span class="k">default</span><span class="p">;</span>

  <span class="c1">// Instantiate the page, passing &lt;body&gt; as the root element.</span>
  <span class="kd">var</span> <span class="nx">page</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Page</span><span class="p">(</span><span class="nf">$</span><span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">));</span>

  <span class="c1">// Set up page and run scripts for it.</span>
  <span class="k">if </span><span class="p">(</span><span class="nx">page</span><span class="p">.</span><span class="nx">setup</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">page</span><span class="p">.</span><span class="nf">setup</span><span class="p">();</span>
  <span class="p">}</span>

  <span class="nx">page</span><span class="p">.</span><span class="nf">run</span><span class="p">();</span>
<span class="p">}</span>

<span class="c1">// Handles exception.</span>
<span class="kd">function</span> <span class="nf">handleError</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if </span><span class="p">(</span><span class="nx">error</span><span class="p">.</span><span class="nx">message</span><span class="p">.</span><span class="nf">match</span><span class="p">(</span><span class="sr">/undefined missing/</span><span class="p">))</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nf">warn</span><span class="p">(</span><span class="dl">"</span><span class="s2">missing module:</span><span class="dl">"</span><span class="p">,</span> <span class="nx">error</span><span class="p">.</span><span class="nx">message</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="dl">"</span><span class="s2"> </span><span class="dl">"</span><span class="p">).</span><span class="nf">pop</span><span class="p">());</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="k">throw</span> <span class="nx">error</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="nf">$</span><span class="p">(</span><span class="nb">window</span><span class="p">)</span>
  <span class="p">.</span><span class="nf">ready</span><span class="p">(</span><span class="nx">runner</span><span class="p">)</span>
  <span class="p">.</span><span class="nf">on</span><span class="p">(</span><span class="dl">"</span><span class="s2">turbolinks:load</span><span class="dl">"</span><span class="p">,</span> <span class="nx">runner</span><span class="p">);</span>
</code></pre></div>
<p>This script needs a <code>data-route</code> property property on your <code>&lt;body&gt;</code> element. You can add something like the following to your layout file (e.g. <code>app/views/layouts/application.html.erb</code>). You can use the following helper method:</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="c1"># app/helpers/application_helper.rb</span>
<span class="k">module</span> <span class="nn">ApplicationHelper</span>
  <span class="no">ACTION_ALIASES</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s2">"update"</span> <span class="o">=&gt;</span> <span class="s2">"edit"</span><span class="p">,</span>
    <span class="s2">"create"</span> <span class="o">=&gt;</span> <span class="s2">"new"</span>
  <span class="p">}</span>

  <span class="k">def</span> <span class="nf">js_route</span>
    <span class="n">action_name</span> <span class="o">=</span> <span class="no">ACTION_ALIASES</span><span class="p">[</span><span class="n">controller</span><span class="p">.</span><span class="nf">action_name</span><span class="p">]</span> <span class="o">||</span> <span class="n">controller</span><span class="p">.</span><span class="nf">action_name</span>
    <span class="n">controller_name</span> <span class="o">=</span> <span class="n">controller</span><span class="p">.</span><span class="nf">class</span><span class="p">.</span><span class="nf">name</span><span class="p">.</span><span class="nf">underscore</span><span class="p">.</span><span class="nf">gsub</span><span class="p">(</span><span class="s2">"_controller"</span><span class="p">,</span> <span class="s2">""</span><span class="p">)</span>

    <span class="s2">"</span><span class="si">#{</span><span class="n">controller_name</span><span class="si">}</span><span class="s2">/</span><span class="si">#{</span><span class="n">action_name</span><span class="si">}</span><span class="s2">"</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div>
<p>And then:</p>
<div class="highlight"><pre class="highlight erb"><code><span class="nt">&lt;body</span> <span class="na">data-route=</span><span class="s">"application/pages/</span><span class="cp">&lt;%=</span> <span class="n">js_route</span> <span class="cp">%&gt;</span><span class="s">"</span><span class="nt">&gt;</span>
</code></pre></div>
<p>Now let&rsquo;s create a class that will be executed when the template is rendered. We&rsquo;ll use the <code>site</code> controller and <code>home</code> action as example. For this you&rsquo;ll need to create the <code>app/assets/javascripts/application/pages/site/home.es6</code> file.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="k">export</span> <span class="k">default</span> <span class="kd">class</span> <span class="nc">Home</span> <span class="p">{</span>
  <span class="nf">constructor</span><span class="p">(</span><span class="nx">root</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">root</span> <span class="o">=</span> <span class="nx">root</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
    <span class="c1">// add event listeners</span>
    <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">-&gt; setting up listeners and whatnot</span><span class="dl">"</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="nf">run</span><span class="p">()</span> <span class="p">{</span>
    <span class="c1">// trigger initial action (e.g. perform http requests)</span>
    <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">-&gt; perform initial actions</span><span class="dl">"</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>Note that we&rsquo;re exporting the <code>Home</code> class as the default module. This is the module that will be used when you have something like <code>import Home from &#39;application/pages/home&#39;;</code>.</p>

<p>Also note that we&rsquo;re defining the <code>Home#constructor</code> method; this is the method that is executed when the class is instantiated.</p>

<p>I said this before, but class definition is one of the things I like the most. Compare it with the constructor function form:</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="kd">function</span> <span class="nf">Home</span><span class="p">(</span><span class="nx">root</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">this</span><span class="p">.</span><span class="nx">root</span> <span class="o">=</span> <span class="nx">root</span><span class="p">;</span>
<span class="p">}</span>

<span class="nx">Home</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">setup</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  <span class="c1">// add listeners</span>
<span class="p">};</span>

<span class="nx">Home</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">run</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  <span class="c1">// initial execution</span>
<span class="p">};</span>
</code></pre></div>
<p>They&rsquo;re are similar, but using the <code>class</code> keyword makes closer to what we use in other languages. Here&rsquo;s how you would write the same class in Ruby:</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">Home</span>
  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">root</span><span class="p">)</span>
    <span class="vi">@root</span> <span class="o">=</span> <span class="n">root</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">setup</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">run</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div>
<p><abbr title="ECMAScript 2015">ES2015</abbr> has other niceties. To discover what&rsquo;s new, I recommend the <a href="http://exploringjs.com/">Exploring <abbr title="ECMAScript 6">ES6</abbr></a> book, which <a href="http://exploringjs.com/es6/">you can read for free</a>.</p>
<h3 tabindex="-1" id="almond-js-gotcha">Almond.js gotcha<a class="anchor" href="#almond-js-gotcha" aria-hidden="true" tabindex="-1"></a>
</h3>
<p>There is one gotcha when using Almond; all modules must be explicitly named. This means that libraries like <a href="https://qunitjs.com">qunit</a> won&rsquo;t work out of the box because they&rsquo;re anonymous modules. To solve this problem, you should load the library before loading Almond and then exporting the module yourself.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="c1">//= require qunit</span>
<span class="c1">//= require almond</span>

<span class="nf">define</span><span class="p">(</span><span class="dl">"</span><span class="s2">qunit</span><span class="dl">"</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">QUnit</span><span class="p">;</span>
<span class="p">});</span>
</code></pre></div>
<p>The problem of doing this is that the library won&rsquo;t detect AMD support and will export global variables, but I still prefer this behavior over compiling code with an optimizer like <a href="http://requirejs.org/docs/download.html#rjs">r.js</a> or a library that integrates that into Rails.</p>
<h2 tabindex="-1" id="wrapping-up">Wrapping up<a class="anchor" href="#wrapping-up" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>Using <abbr title="ECMAScript 2015">ES2015</abbr> today is a viable option. With Babel you can use all these new features without worrying with browser compatibility. The integration with Asset Pipeline make things easier, even for those that don&rsquo;t fully grasp the Node.js ecosystem.</p>

<p>There&rsquo;s a working repository <a href="https://github.com/fnando/using-es6-with-asset-pipeline-on-ruby-on-rails">available at Github</a>.</p>

<div class="footnotes">
<hr>
<ol>

<li id="fn1">
<p><em>ES2015</em> is also known as <em>ES.Next</em> or <em>ES6</em>.&nbsp;<a href="#fnref1">&#8617;</a></p>
</li>

<li id="fn2">
<p>This project used to be called <em>6to5</em>.js. <a href="http://babeljs.io/blog/2015/02/15/not-born-to-die/">Read more.</a>&nbsp;<a href="#fnref2">&#8617;</a></p>
</li>

</ol>
</div>
]]>
      </description>
      <content:encoded>
        <![CDATA[<p>JavaScript is all new. Until recently, we had no new features. The last significant update was back in 2009, with <abbr title="ECMAScript 5">ES5</abbr>&lsquo;s release. And you couldn&rsquo;t use all features due to browser incompatibility.</p>

<p>To increase the compatibility level, we had to use things like <a href="https://github.com/es-shims/es5-shim/">es5-shim</a>, which conditionally checked if a feature was available, adding a <em>polyfill</em> if the browser didn&rsquo;t implement it.</p>

<p>And then the first pre-processors came in, like <a href="http://coffeescript.org">CoffeeScript</a>. You could write different constructions, that were compiled to code that the browsers could actually understand.</p>

<p>Interestingly, <a href="https://brendaneich.com">Brendan Eich</a> announced in 2009 a new JavaScript version called <em>Harmony</em>, which is now called <abbr title="ECMAScript 2015">ES2015</abbr><sup id="fnref1"><a href="#fn1">1</a></sup>. The first drafts were published in 2011, but the final specification was released in June of 2015.</p>

<p><abbr title="ECMAScript 2015">ES2015</abbr> has so many new features:</p>

<ul>
<li>Class definition</li>
<li>String interpolation</li>
<li>Fat arrow functions</li>
<li><a href="http://babeljs.io/docs/learn-es2015/">More!</a></li>
</ul>

<p><a href="https://kangax.github.io/compat-table/es6/">Browsers are implementing</a> <abbr title="ECMAScript 2015">ES2015</abbr> in a fast pace, but it&rsquo;ll take some time until we can actually use it. Maybe years. Fortunately, we have <a href="https://babeljs.io">Babel.js</a>. We can use all these new features today without worrying with browser compatibility.</p>

<p>Babel.js<sup id="fnref2"><a href="#fn2">2</a></sup> is just a pre-processor. You write code that uses these new features (and more), which will be exported as code that browsers can understand, even those that don&rsquo;t fully understand <abbr title="ECMAScript 2015">ES2015</abbr>.</p>
<h2 tabindex="-1" id="using-babel-js-with-asset-pipeline">Using Babel.js with Asset Pipeline<a class="anchor" href="#using-babel-js-with-asset-pipeline" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>There are several ways you can use to precompile your JavaScript code. Some people use Babel&rsquo;s <abbr title="Command-Line Interface">CLI</abbr>. Some people prefer <em>builders</em> like <a href="http://gruntjs.com">Grunt</a> or <a href="http://gulpjs.com">Gulp</a> to automate the compilation process. But if you&rsquo;re using Ruby on Rails you&rsquo;re more likely to use <a href="http://guides.rubyonrails.org/asset_pipeline.html">Asset Pipeline</a> for front-end assets compilation. And this is, in my opinion, the easiest way of using Babel, believe it or not.</p>

<p>Unfortunately there&rsquo;s no built-in support on the stable release of <a href="https://github.com/rails/sprockets">Sprockets</a>, so you&rsquo;ll have to use a pre-release version.</p>

<p>The transpilation is performed by <a href="https://rubygems.org/gems/babel-schmooze-sprockets">babel-schmooze-sprockets</a>. It vendors several Babel extensions so that you can use Babel without having to deal with NPM on your application (you&rsquo;ll still need Node.js though).</p>

<p>Update your <code>Gemfile</code> to include these dependencies.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="n">source</span> <span class="s2">"https://rubygems.org"</span>

<span class="n">gem</span> <span class="s2">"rails"</span><span class="p">,</span> <span class="s2">"4.2.6"</span>
<span class="n">gem</span> <span class="s2">"sqlite3"</span>
<span class="n">gem</span> <span class="s2">"uglifier"</span><span class="p">,</span> <span class="s2">"&gt;= 1.3.0"</span>

<span class="n">gem</span> <span class="s2">"sprockets"</span><span class="p">,</span> <span class="s2">"~&gt; 4.x"</span>
<span class="n">gem</span> <span class="s2">"babel-schmooze-sprockets"</span>

<span class="n">gem</span> <span class="s2">"turbolinks"</span><span class="p">,</span> <span class="s2">"~&gt; 5.x"</span>
<span class="n">gem</span> <span class="s2">"jquery-rails"</span>
</code></pre></div>
<p>That&rsquo;s it! Now all <code>.es6</code> files will be compiled using Babel. Create a file at <code>app/assets/javascripts/hello.es6</code> with the following code:</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="kd">class</span> <span class="nc">Hello</span> <span class="p">{</span>
  <span class="nf">constructor</span><span class="p">()</span> <span class="p">{</span>
    <span class="nf">alert</span><span class="p">(</span><span class="dl">"</span><span class="s2">Hello!</span><span class="dl">"</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="k">new</span> <span class="nc">Hello</span><span class="p">();</span>
</code></pre></div>
<p>Make sure you&rsquo;re loading <code>hello.es6</code> at <code>app/assets/javascripts/application.js</code>. You also need to load Babel helpers; they&rsquo;ll be used to reduce the amount of generated code.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="c1">//= require babel</span>
<span class="c1">//= require_tree .</span>
<span class="c1">//= require_self</span>
</code></pre></div>
<p>Now if you access the page on your browser, you&rsquo;ll see an <code>alert</code> box like this:</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/es6-alert-hello.png" alt="Alert box - Hello"></p>

<p>If you want the new <a href="https://babeljs.io/docs/usage/modules/">module system</a>, you still have some things to configure.</p>
<h3 tabindex="-1" id="using-es2015-modules">Using <abbr title="ECMAScript 2015">ES2015</abbr> modules<a class="anchor" href="#using-es2015-modules" aria-hidden="true" tabindex="-1"></a>
</h3>
<p><abbr title="ECMAScript 2015">ES2015</abbr> introduced module support; instead of defining your code in the global scope, you can use a scope per file. Importing modules is pretty straightforward:</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="k">import</span> <span class="nx">Foo</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">foo</span><span class="dl">"</span><span class="p">;</span>
</code></pre></div>
<p>This will load <code>Foo</code> from <code>foo.js</code>. One single file can export several units (functions, objects, anything you want), and you don&rsquo;t have to load everything at once, pretty much like <a href="http://python.org">Python</a>.</p>

<p>Babel has no idea of how these modules should be exported, and by default, will use the <a href="http://www.commonjs.org/specs/modules/1.0/">CommonJS</a> format, which can&rsquo;t be used by the browser, so we&rsquo;ll use another approach.</p>

<p>The easiest way is using <a href="http://requirejs.org/docs/whyamd.html">AMD</a>, and for this we&rsquo;ll use <a href="https://github.com/jrburke/almond">almond</a>. You can install almond with whatever you&rsquo;re using for managing packages; in this article I&rsquo;ll use <a href="http://rails-assets.org">http://rails-assets.org</a>, a bower-to-rubygems converter. Update your <code>Gemfile</code> like the following:</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="n">source</span> <span class="s2">"https://rubygems.org"</span>

<span class="n">gem</span> <span class="s2">"rails"</span><span class="p">,</span> <span class="s2">"4.2.6"</span>
<span class="n">gem</span> <span class="s2">"sqlite3"</span>
<span class="n">gem</span> <span class="s2">"uglifier"</span><span class="p">,</span> <span class="s2">"&gt;= 1.3.0"</span>

<span class="n">gem</span> <span class="s2">"sprockets"</span><span class="p">,</span> <span class="s2">"~&gt; 4.x"</span>
<span class="n">gem</span> <span class="s2">"babel-schmooze-sprockets"</span>

<span class="n">gem</span> <span class="s2">"turbolinks"</span><span class="p">,</span> <span class="s2">"~&gt; 5.x"</span>
<span class="n">gem</span> <span class="s2">"jquery-rails"</span>

<span class="n">source</span> <span class="s2">"https://rails-assets.org"</span> <span class="k">do</span>
  <span class="n">gem</span> <span class="s2">"rails-assets-almond"</span>
<span class="k">end</span>
</code></pre></div>
<p>Finally, update <code>app/assets/javascripts/application.js</code> so it loads almond.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="c1">//= require turbolinks</span>
<span class="c1">//= require almond</span>
<span class="c1">//= require jquery</span>
<span class="c1">//= require_tree .</span>
<span class="c1">//= require_self</span>
</code></pre></div>
<p>Notice that Turbolinks must be loaded before Almond; this happens because Turbolinks is an anonymous AMD module. For more information, check the section &ldquo;Almond.js gotcha&rdquo;, later on this article.</p>

<p>Now you have to think about the execution process. Are you going to use some code dispatcher? Or execute code based on the view that is being rendered? Are you going to create your own execution mechanism? The answer depends on your own workflow, so I&rsquo;m not going to give you too many alternatives here.</p>

<p>We&rsquo;re going to create a boot script that uses the controller and action names to execute the JavaScript you need for that specific page. Just add the <code>require</code> call to your <code>app/assets/javascripts/application.js</code> file.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="c1">//= require turbolinks</span>
<span class="c1">//= require almond</span>
<span class="c1">//= require jquery</span>
<span class="c1">//= require_tree .</span>
<span class="c1">//= require_self</span>

<span class="nf">require</span><span class="p">([</span><span class="dl">"</span><span class="s2">application/boot</span><span class="dl">"</span><span class="p">]);</span>
</code></pre></div>
<p>You have to create <code>app/assets/javascripts/application/boot.es6</code>. I&rsquo;ll listen to some events, like DOM&rsquo;s <code>ready</code> and Turbolinks&rsquo; <code>turbolinks:load</code>.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="k">import</span> <span class="nx">$</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">jquery</span><span class="dl">"</span><span class="p">;</span>

<span class="kd">function</span> <span class="nf">runner</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">var</span> <span class="nx">path</span> <span class="o">=</span> <span class="nf">$</span><span class="p">(</span><span class="dl">"</span><span class="s2">body</span><span class="dl">"</span><span class="p">).</span><span class="nf">data</span><span class="p">(</span><span class="dl">"</span><span class="s2">route</span><span class="dl">"</span><span class="p">);</span>

  <span class="c1">// Load script for this page.</span>
  <span class="c1">// We should use System.import, but it's not worth the trouble, so</span>
  <span class="c1">// let's use almond's require instead.</span>
  <span class="k">try</span> <span class="p">{</span>
    <span class="nf">require</span><span class="p">([</span><span class="nx">path</span><span class="p">],</span> <span class="nx">onload</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">catch </span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
    <span class="nf">handleError</span><span class="p">(</span><span class="nx">error</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nf">onload</span><span class="p">(</span><span class="nx">mod</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// Assign the default module.</span>
  <span class="kd">var</span> <span class="nx">Page</span> <span class="o">=</span> <span class="nx">mod</span><span class="p">.</span><span class="k">default</span><span class="p">;</span>

  <span class="c1">// Instantiate the page, passing &lt;body&gt; as the root element.</span>
  <span class="kd">var</span> <span class="nx">page</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Page</span><span class="p">(</span><span class="nf">$</span><span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">));</span>

  <span class="c1">// Set up page and run scripts for it.</span>
  <span class="k">if </span><span class="p">(</span><span class="nx">page</span><span class="p">.</span><span class="nx">setup</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">page</span><span class="p">.</span><span class="nf">setup</span><span class="p">();</span>
  <span class="p">}</span>

  <span class="nx">page</span><span class="p">.</span><span class="nf">run</span><span class="p">();</span>
<span class="p">}</span>

<span class="c1">// Handles exception.</span>
<span class="kd">function</span> <span class="nf">handleError</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if </span><span class="p">(</span><span class="nx">error</span><span class="p">.</span><span class="nx">message</span><span class="p">.</span><span class="nf">match</span><span class="p">(</span><span class="sr">/undefined missing/</span><span class="p">))</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nf">warn</span><span class="p">(</span><span class="dl">"</span><span class="s2">missing module:</span><span class="dl">"</span><span class="p">,</span> <span class="nx">error</span><span class="p">.</span><span class="nx">message</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="dl">"</span><span class="s2"> </span><span class="dl">"</span><span class="p">).</span><span class="nf">pop</span><span class="p">());</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="k">throw</span> <span class="nx">error</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="nf">$</span><span class="p">(</span><span class="nb">window</span><span class="p">)</span>
  <span class="p">.</span><span class="nf">ready</span><span class="p">(</span><span class="nx">runner</span><span class="p">)</span>
  <span class="p">.</span><span class="nf">on</span><span class="p">(</span><span class="dl">"</span><span class="s2">turbolinks:load</span><span class="dl">"</span><span class="p">,</span> <span class="nx">runner</span><span class="p">);</span>
</code></pre></div>
<p>This script needs a <code>data-route</code> property property on your <code>&lt;body&gt;</code> element. You can add something like the following to your layout file (e.g. <code>app/views/layouts/application.html.erb</code>). You can use the following helper method:</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="c1"># app/helpers/application_helper.rb</span>
<span class="k">module</span> <span class="nn">ApplicationHelper</span>
  <span class="no">ACTION_ALIASES</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s2">"update"</span> <span class="o">=&gt;</span> <span class="s2">"edit"</span><span class="p">,</span>
    <span class="s2">"create"</span> <span class="o">=&gt;</span> <span class="s2">"new"</span>
  <span class="p">}</span>

  <span class="k">def</span> <span class="nf">js_route</span>
    <span class="n">action_name</span> <span class="o">=</span> <span class="no">ACTION_ALIASES</span><span class="p">[</span><span class="n">controller</span><span class="p">.</span><span class="nf">action_name</span><span class="p">]</span> <span class="o">||</span> <span class="n">controller</span><span class="p">.</span><span class="nf">action_name</span>
    <span class="n">controller_name</span> <span class="o">=</span> <span class="n">controller</span><span class="p">.</span><span class="nf">class</span><span class="p">.</span><span class="nf">name</span><span class="p">.</span><span class="nf">underscore</span><span class="p">.</span><span class="nf">gsub</span><span class="p">(</span><span class="s2">"_controller"</span><span class="p">,</span> <span class="s2">""</span><span class="p">)</span>

    <span class="s2">"</span><span class="si">#{</span><span class="n">controller_name</span><span class="si">}</span><span class="s2">/</span><span class="si">#{</span><span class="n">action_name</span><span class="si">}</span><span class="s2">"</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div>
<p>And then:</p>
<div class="highlight"><pre class="highlight erb"><code><span class="nt">&lt;body</span> <span class="na">data-route=</span><span class="s">"application/pages/</span><span class="cp">&lt;%=</span> <span class="n">js_route</span> <span class="cp">%&gt;</span><span class="s">"</span><span class="nt">&gt;</span>
</code></pre></div>
<p>Now let&rsquo;s create a class that will be executed when the template is rendered. We&rsquo;ll use the <code>site</code> controller and <code>home</code> action as example. For this you&rsquo;ll need to create the <code>app/assets/javascripts/application/pages/site/home.es6</code> file.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="k">export</span> <span class="k">default</span> <span class="kd">class</span> <span class="nc">Home</span> <span class="p">{</span>
  <span class="nf">constructor</span><span class="p">(</span><span class="nx">root</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">root</span> <span class="o">=</span> <span class="nx">root</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
    <span class="c1">// add event listeners</span>
    <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">-&gt; setting up listeners and whatnot</span><span class="dl">"</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="nf">run</span><span class="p">()</span> <span class="p">{</span>
    <span class="c1">// trigger initial action (e.g. perform http requests)</span>
    <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">-&gt; perform initial actions</span><span class="dl">"</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>Note that we&rsquo;re exporting the <code>Home</code> class as the default module. This is the module that will be used when you have something like <code>import Home from &#39;application/pages/home&#39;;</code>.</p>

<p>Also note that we&rsquo;re defining the <code>Home#constructor</code> method; this is the method that is executed when the class is instantiated.</p>

<p>I said this before, but class definition is one of the things I like the most. Compare it with the constructor function form:</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="kd">function</span> <span class="nf">Home</span><span class="p">(</span><span class="nx">root</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">this</span><span class="p">.</span><span class="nx">root</span> <span class="o">=</span> <span class="nx">root</span><span class="p">;</span>
<span class="p">}</span>

<span class="nx">Home</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">setup</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  <span class="c1">// add listeners</span>
<span class="p">};</span>

<span class="nx">Home</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">run</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  <span class="c1">// initial execution</span>
<span class="p">};</span>
</code></pre></div>
<p>They&rsquo;re are similar, but using the <code>class</code> keyword makes closer to what we use in other languages. Here&rsquo;s how you would write the same class in Ruby:</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">Home</span>
  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">root</span><span class="p">)</span>
    <span class="vi">@root</span> <span class="o">=</span> <span class="n">root</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">setup</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">run</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div>
<p><abbr title="ECMAScript 2015">ES2015</abbr> has other niceties. To discover what&rsquo;s new, I recommend the <a href="http://exploringjs.com/">Exploring <abbr title="ECMAScript 6">ES6</abbr></a> book, which <a href="http://exploringjs.com/es6/">you can read for free</a>.</p>
<h3 tabindex="-1" id="almond-js-gotcha">Almond.js gotcha<a class="anchor" href="#almond-js-gotcha" aria-hidden="true" tabindex="-1"></a>
</h3>
<p>There is one gotcha when using Almond; all modules must be explicitly named. This means that libraries like <a href="https://qunitjs.com">qunit</a> won&rsquo;t work out of the box because they&rsquo;re anonymous modules. To solve this problem, you should load the library before loading Almond and then exporting the module yourself.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="c1">//= require qunit</span>
<span class="c1">//= require almond</span>

<span class="nf">define</span><span class="p">(</span><span class="dl">"</span><span class="s2">qunit</span><span class="dl">"</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">QUnit</span><span class="p">;</span>
<span class="p">});</span>
</code></pre></div>
<p>The problem of doing this is that the library won&rsquo;t detect AMD support and will export global variables, but I still prefer this behavior over compiling code with an optimizer like <a href="http://requirejs.org/docs/download.html#rjs">r.js</a> or a library that integrates that into Rails.</p>
<h2 tabindex="-1" id="wrapping-up">Wrapping up<a class="anchor" href="#wrapping-up" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>Using <abbr title="ECMAScript 2015">ES2015</abbr> today is a viable option. With Babel you can use all these new features without worrying with browser compatibility. The integration with Asset Pipeline make things easier, even for those that don&rsquo;t fully grasp the Node.js ecosystem.</p>

<p>There&rsquo;s a working repository <a href="https://github.com/fnando/using-es6-with-asset-pipeline-on-ruby-on-rails">available at Github</a>.</p>

<div class="footnotes">
<hr>
<ol>

<li id="fn1">
<p><em>ES2015</em> is also known as <em>ES.Next</em> or <em>ES6</em>.&nbsp;<a href="#fnref1">&#8617;</a></p>
</li>

<li id="fn2">
<p>This project used to be called <em>6to5</em>.js. <a href="http://babeljs.io/blog/2015/02/15/not-born-to-die/">Read more.</a>&nbsp;<a href="#fnref2">&#8617;</a></p>
</li>

</ol>
</div>
]]>
      </content:encoded>
      <link>https://nandovieira.com/using-es2015-with-asset-pipeline-on-ruby-on-rails</link>
      <guid>https://nandovieira.com/using-es2015-with-asset-pipeline-on-ruby-on-rails</guid>
      <pubDate>Mon, 30 May 2016 08:00:00 -0300</pubDate>
    </item>
    <item>
      <title>Replacing Pingdom with Ruby and Heroku</title>
      <description>
        <![CDATA[<p>Pingdom recently increased the price of the starting plan from $10 to $14. Since they didn&rsquo;t keep the price for older customers, I decided to create something to replace it, because the new pricing is just too much for my personal sites.</p>

<p>This article shows how you can set up your own uptime checker and run it on Heroku for $7/month.</p>
<h2 tabindex="-1" id="about-uptime_checker">About uptime_checker<a class="anchor" href="#about-uptime_checker" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>I created <a href="https://github.com/fnando/uptime_checker">uptime_checker</a>, which has less than 500 lines. The main features are:</p>

<ul>
<li>Several notification mechanisms (Telegram, Twitter&rsquo;s Direct Message, E-mail, Slack, Hipchat, and more)</li>
<li>Easy to set up on Heroku</li>
<li>Easy to define the sites that will be monitored</li>
</ul>

<p>The idea is pretty simple: hit the urls you define every 30s (configurable) and see if the site returns the expected status code. You can also check if the response contains a string. If the status/body is not what you expected, it notifies you.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/uptime-checker-slack-notification.png" alt="Slack Notification"></p>

<p>Running uptime_check on Heroku is really easy and you won&rsquo;t need more than a few minutes, so let&rsquo;s do it.</p>
<h2 tabindex="-1" id="setting-up-uptime_checker">Setting up uptime_checker<a class="anchor" href="#setting-up-uptime_checker" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>First, you have to fork the repository, since you have to create your own branch with a configuration file. Go to <a href="https://github.com/fnando/uptime_checker">Github</a> and hit the &ldquo;Fork&rdquo; button.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/uptime-checker-fork-button.png" alt="Fork button"></p>

<p>Now, clone the repository and create a new branch: I&rsquo;ll call it <code>personal</code> in this article.</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ git clone git@github.com:&lt;your username&gt;/uptime_checker.yml
$ cd uptime_checker
$ git checkout -B personal
</code></pre></div>
<p>Copy the sample configuration file.</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ cp checkers.yml.sample checkers.yml
</code></pre></div>
<p>Open the <code>checkers.yml</code> file. This file is where you define all the sites that must be monitored. It supports ERB, so you can render dynamic variables. Here&rsquo;s is my own configuration file.</p>
<div class="highlight"><pre class="highlight yaml"><code><span class="na">notify</span><span class="pi">:</span> <span class="nl">&amp;notify</span>
  <span class="pi">-</span> <span class="na">twitter</span><span class="pi">:</span> <span class="s">fnando</span>
  <span class="pi">-</span> <span class="na">email</span><span class="pi">:</span> <span class="s">fnando.vieira@gmail.com</span>
  <span class="pi">-</span> <span class="na">stdout</span><span class="pi">:</span> <span class="s">~</span>
  <span class="pi">-</span> <span class="na">telegram</span><span class="pi">:</span> <span class="s">-97517324</span>
  <span class="pi">-</span> <span class="na">noti</span><span class="pi">:</span> <span class="s">&lt;%= ENV["NOTI_FNANDO"] %&gt;</span>

<span class="na">checkers</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Codeplane</span>
    <span class="na">url</span><span class="pi">:</span> <span class="s">https://codeplane.com/</span>
    <span class="na">notify</span><span class="pi">:</span> <span class="nv">*notify</span>
    <span class="na">status</span><span class="pi">:</span> <span class="m">200</span>

  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Codeplane</span>
    <span class="na">url</span><span class="pi">:</span> <span class="s">https://codeplane.com.br/</span>
    <span class="na">notify</span><span class="pi">:</span> <span class="nv">*notify</span>
    <span class="na">status</span><span class="pi">:</span> <span class="m">200</span>

  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Hellobits</span>
    <span class="na">url</span><span class="pi">:</span> <span class="s">http://hellobits.com/</span>
    <span class="na">notify</span><span class="pi">:</span> <span class="nv">*notify</span>
    <span class="na">status</span><span class="pi">:</span> <span class="m">200</span>

  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">HOWTO</span>
    <span class="na">url</span><span class="pi">:</span> <span class="s">http://howtocode.com.br/</span>
    <span class="na">notify</span><span class="pi">:</span> <span class="nv">*notify</span>
    <span class="na">status</span><span class="pi">:</span> <span class="m">200</span>

  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Nando Vieira</span>
    <span class="na">url</span><span class="pi">:</span> <span class="s">http://nandovieira.com/</span>
    <span class="na">notify</span><span class="pi">:</span> <span class="nv">*notify</span>
    <span class="na">status</span><span class="pi">:</span> <span class="m">200</span>

  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Nando Vieira</span>
    <span class="na">url</span><span class="pi">:</span> <span class="s">https://nandovieira.com.br/</span>
    <span class="na">notify</span><span class="pi">:</span> <span class="nv">*notify</span>
    <span class="na">status</span><span class="pi">:</span> <span class="m">200</span>

  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Presentta</span>
    <span class="na">url</span><span class="pi">:</span> <span class="s">https://presentta.com.br/</span>
    <span class="na">notify</span><span class="pi">:</span> <span class="nv">*notify</span>
    <span class="na">status</span><span class="pi">:</span> <span class="m">200</span>
</code></pre></div>
<p>To avoid repeating the notifiers, I&rsquo;m using the shared block trick. You can have different notifications for each site you&rsquo;re monitoring if you need.</p>

<p>Notice that I&rsquo;m expecting those sites to respond with a 200 status code, but you can also check if the response has some given text.</p>
<div class="highlight"><pre class="highlight yaml"><code><span class="na">checkers</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Example</span>
    <span class="na">url</span><span class="pi">:</span> <span class="s">http://example.com</span>
    <span class="na">notify</span><span class="pi">:</span> <span class="nv">*notify</span>
    <span class="na">status</span><span class="pi">:</span> <span class="m">200</span>
    <span class="na">body</span><span class="pi">:</span> <span class="s">Some string</span>
</code></pre></div>
<p>After you&rsquo;re done, commit your changes.</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ git add .
$ git commit -m "Add checkers."
$ git push origin personal
</code></pre></div><h2 tabindex="-1" id="deploying-to-heroku">Deploying to Heroku<a class="anchor" href="#deploying-to-heroku" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>Deploying uptime_checker to <a href="https://heroku.com">Heroku</a> is the easiest way you can have this running. Assuming you already Heroku configured, create a new application.</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ heroku create
</code></pre></div>
<p>Redis is required, so we need to add the add-on.</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ heroku addons:create heroku-redis
</code></pre></div>
<p>To receive e-mail notifications, you&rsquo;ll also need Sendgrid.</p>
<div class="highlight"><pre class="highlight plaintext"><code>heroku addons:create sendgrid:starter
</code></pre></div>
<p>I use <a href="http://notiapp.com">Noti</a>. If you check the checkers example above, you&rsquo;ll see that I&rsquo;m using a <code>NOTI_FNANDO</code> environment variable. Let&rsquo;s set that on Heroku:</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ heroku config:set NOTI_FNANDO=fc17d63d-5d3e-4532-98ed-b8047f0d5bc9
</code></pre></div>
<p>Now it&rsquo;s time to deploy! Since we&rsquo;ll deploy the <code>personal</code> branch, you have to use the following command:</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ git push heroku personal:master
</code></pre></div>
<p>This means that the <code>personal</code> branch will be pushed as Heroku&rsquo;s <code>master</code>.</p>

<p>To run this 24/7 ($7/mo), we have to scale up our worker.</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ heroku ps:scale worker=1
$ heroku dyno:type worker=hobby
</code></pre></div>
<p>And we&rsquo;re done! You can check the logs with <code>heroku logs --tail</code>.</p>
<div class="highlight"><pre class="highlight plaintext"><code>app[worker.1]: enabled_notifiers="email, noti, stdout, telegram, twitter" time="2016-01-29T12:40:28Z"
app[worker.1]: message="starting check" url="https://presentta.com.br/" time="2016-01-29T12:40:28Z"
app[worker.1]: message="starting check" url="https://nandovieira.com.br/" time="2016-01-29T12:40:28Z"
app[worker.1]: message="starting check" url="http://nandovieira.com/" time="2016-01-29T12:40:28Z"
app[worker.1]: message="starting check" url="http://howtocode.com.br/" time="2016-01-29T12:40:28Z"
app[worker.1]: message="starting check" url="http://hellobits.com/" time="2016-01-29T12:40:28Z"
app[worker.1]: url="https://codeplane.com/" message="check finished" status="no changes" time="2016-01-29T12:40:28Z"
app[worker.1]: url="https://codeplane.com.br/" message="check finished" status="no changes" time="2016-01-29T12:40:28Z"
app[worker.1]: url="http://howtocode.com.br/" message="check finished" status="no changes" time="2016-01-29T12:40:28Z"
app[worker.1]: url="https://nandovieira.com.br/" message="check finished" status="no changes" time="2016-01-29T12:40:28Z"
app[worker.1]: url="https://presentta.com.br/" message="check finished" status="no changes" time="2016-01-29T12:40:28Z"
</code></pre></div>
<p>If you&rsquo;re not into Heroku, you can also deploy it to a VPS. If you already know how to deploy a Rails application, you won&rsquo;t have any problem. Since this involves too much things, I leave this as an exercise to the reader.</p>
<h3 tabindex="-1" id="keeping-up-with-master">Keeping up with master<a class="anchor" href="#keeping-up-with-master" aria-hidden="true" tabindex="-1"></a>
</h3>
<p>You may want to keep up with <code>master</code>, getting all the fixes and new features (if any). The workflow for updating your branch is pretty simple.</p>

<p>First, switch off to <code>master</code> and pull all the changes.</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ git checkout master
$ git pull
</code></pre></div>
<p>Then switch back to <code>personal</code> and apply <code>master</code>&lsquo;s onto it.</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ git checkout personal
$ git rebase master
</code></pre></div>
<p>Now, deploy the repository.</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ git push heroku personal:master -f
</code></pre></div><h2 tabindex="-1" id="wrapping-up">Wrapping up<a class="anchor" href="#wrapping-up" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>That&rsquo;s it! If you have any suggestions or need help, just <a href="https://github.com/fnando/uptime_checker/issues/new">open an issue</a>.</p>
]]>
      </description>
      <content:encoded>
        <![CDATA[<p>Pingdom recently increased the price of the starting plan from $10 to $14. Since they didn&rsquo;t keep the price for older customers, I decided to create something to replace it, because the new pricing is just too much for my personal sites.</p>

<p>This article shows how you can set up your own uptime checker and run it on Heroku for $7/month.</p>
<h2 tabindex="-1" id="about-uptime_checker">About uptime_checker<a class="anchor" href="#about-uptime_checker" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>I created <a href="https://github.com/fnando/uptime_checker">uptime_checker</a>, which has less than 500 lines. The main features are:</p>

<ul>
<li>Several notification mechanisms (Telegram, Twitter&rsquo;s Direct Message, E-mail, Slack, Hipchat, and more)</li>
<li>Easy to set up on Heroku</li>
<li>Easy to define the sites that will be monitored</li>
</ul>

<p>The idea is pretty simple: hit the urls you define every 30s (configurable) and see if the site returns the expected status code. You can also check if the response contains a string. If the status/body is not what you expected, it notifies you.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/uptime-checker-slack-notification.png" alt="Slack Notification"></p>

<p>Running uptime_check on Heroku is really easy and you won&rsquo;t need more than a few minutes, so let&rsquo;s do it.</p>
<h2 tabindex="-1" id="setting-up-uptime_checker">Setting up uptime_checker<a class="anchor" href="#setting-up-uptime_checker" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>First, you have to fork the repository, since you have to create your own branch with a configuration file. Go to <a href="https://github.com/fnando/uptime_checker">Github</a> and hit the &ldquo;Fork&rdquo; button.</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/uptime-checker-fork-button.png" alt="Fork button"></p>

<p>Now, clone the repository and create a new branch: I&rsquo;ll call it <code>personal</code> in this article.</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ git clone git@github.com:&lt;your username&gt;/uptime_checker.yml
$ cd uptime_checker
$ git checkout -B personal
</code></pre></div>
<p>Copy the sample configuration file.</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ cp checkers.yml.sample checkers.yml
</code></pre></div>
<p>Open the <code>checkers.yml</code> file. This file is where you define all the sites that must be monitored. It supports ERB, so you can render dynamic variables. Here&rsquo;s is my own configuration file.</p>
<div class="highlight"><pre class="highlight yaml"><code><span class="na">notify</span><span class="pi">:</span> <span class="nl">&amp;notify</span>
  <span class="pi">-</span> <span class="na">twitter</span><span class="pi">:</span> <span class="s">fnando</span>
  <span class="pi">-</span> <span class="na">email</span><span class="pi">:</span> <span class="s">fnando.vieira@gmail.com</span>
  <span class="pi">-</span> <span class="na">stdout</span><span class="pi">:</span> <span class="s">~</span>
  <span class="pi">-</span> <span class="na">telegram</span><span class="pi">:</span> <span class="s">-97517324</span>
  <span class="pi">-</span> <span class="na">noti</span><span class="pi">:</span> <span class="s">&lt;%= ENV["NOTI_FNANDO"] %&gt;</span>

<span class="na">checkers</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Codeplane</span>
    <span class="na">url</span><span class="pi">:</span> <span class="s">https://codeplane.com/</span>
    <span class="na">notify</span><span class="pi">:</span> <span class="nv">*notify</span>
    <span class="na">status</span><span class="pi">:</span> <span class="m">200</span>

  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Codeplane</span>
    <span class="na">url</span><span class="pi">:</span> <span class="s">https://codeplane.com.br/</span>
    <span class="na">notify</span><span class="pi">:</span> <span class="nv">*notify</span>
    <span class="na">status</span><span class="pi">:</span> <span class="m">200</span>

  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Hellobits</span>
    <span class="na">url</span><span class="pi">:</span> <span class="s">http://hellobits.com/</span>
    <span class="na">notify</span><span class="pi">:</span> <span class="nv">*notify</span>
    <span class="na">status</span><span class="pi">:</span> <span class="m">200</span>

  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">HOWTO</span>
    <span class="na">url</span><span class="pi">:</span> <span class="s">http://howtocode.com.br/</span>
    <span class="na">notify</span><span class="pi">:</span> <span class="nv">*notify</span>
    <span class="na">status</span><span class="pi">:</span> <span class="m">200</span>

  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Nando Vieira</span>
    <span class="na">url</span><span class="pi">:</span> <span class="s">http://nandovieira.com/</span>
    <span class="na">notify</span><span class="pi">:</span> <span class="nv">*notify</span>
    <span class="na">status</span><span class="pi">:</span> <span class="m">200</span>

  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Nando Vieira</span>
    <span class="na">url</span><span class="pi">:</span> <span class="s">https://nandovieira.com.br/</span>
    <span class="na">notify</span><span class="pi">:</span> <span class="nv">*notify</span>
    <span class="na">status</span><span class="pi">:</span> <span class="m">200</span>

  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Presentta</span>
    <span class="na">url</span><span class="pi">:</span> <span class="s">https://presentta.com.br/</span>
    <span class="na">notify</span><span class="pi">:</span> <span class="nv">*notify</span>
    <span class="na">status</span><span class="pi">:</span> <span class="m">200</span>
</code></pre></div>
<p>To avoid repeating the notifiers, I&rsquo;m using the shared block trick. You can have different notifications for each site you&rsquo;re monitoring if you need.</p>

<p>Notice that I&rsquo;m expecting those sites to respond with a 200 status code, but you can also check if the response has some given text.</p>
<div class="highlight"><pre class="highlight yaml"><code><span class="na">checkers</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Example</span>
    <span class="na">url</span><span class="pi">:</span> <span class="s">http://example.com</span>
    <span class="na">notify</span><span class="pi">:</span> <span class="nv">*notify</span>
    <span class="na">status</span><span class="pi">:</span> <span class="m">200</span>
    <span class="na">body</span><span class="pi">:</span> <span class="s">Some string</span>
</code></pre></div>
<p>After you&rsquo;re done, commit your changes.</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ git add .
$ git commit -m "Add checkers."
$ git push origin personal
</code></pre></div><h2 tabindex="-1" id="deploying-to-heroku">Deploying to Heroku<a class="anchor" href="#deploying-to-heroku" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>Deploying uptime_checker to <a href="https://heroku.com">Heroku</a> is the easiest way you can have this running. Assuming you already Heroku configured, create a new application.</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ heroku create
</code></pre></div>
<p>Redis is required, so we need to add the add-on.</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ heroku addons:create heroku-redis
</code></pre></div>
<p>To receive e-mail notifications, you&rsquo;ll also need Sendgrid.</p>
<div class="highlight"><pre class="highlight plaintext"><code>heroku addons:create sendgrid:starter
</code></pre></div>
<p>I use <a href="http://notiapp.com">Noti</a>. If you check the checkers example above, you&rsquo;ll see that I&rsquo;m using a <code>NOTI_FNANDO</code> environment variable. Let&rsquo;s set that on Heroku:</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ heroku config:set NOTI_FNANDO=fc17d63d-5d3e-4532-98ed-b8047f0d5bc9
</code></pre></div>
<p>Now it&rsquo;s time to deploy! Since we&rsquo;ll deploy the <code>personal</code> branch, you have to use the following command:</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ git push heroku personal:master
</code></pre></div>
<p>This means that the <code>personal</code> branch will be pushed as Heroku&rsquo;s <code>master</code>.</p>

<p>To run this 24/7 ($7/mo), we have to scale up our worker.</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ heroku ps:scale worker=1
$ heroku dyno:type worker=hobby
</code></pre></div>
<p>And we&rsquo;re done! You can check the logs with <code>heroku logs --tail</code>.</p>
<div class="highlight"><pre class="highlight plaintext"><code>app[worker.1]: enabled_notifiers="email, noti, stdout, telegram, twitter" time="2016-01-29T12:40:28Z"
app[worker.1]: message="starting check" url="https://presentta.com.br/" time="2016-01-29T12:40:28Z"
app[worker.1]: message="starting check" url="https://nandovieira.com.br/" time="2016-01-29T12:40:28Z"
app[worker.1]: message="starting check" url="http://nandovieira.com/" time="2016-01-29T12:40:28Z"
app[worker.1]: message="starting check" url="http://howtocode.com.br/" time="2016-01-29T12:40:28Z"
app[worker.1]: message="starting check" url="http://hellobits.com/" time="2016-01-29T12:40:28Z"
app[worker.1]: url="https://codeplane.com/" message="check finished" status="no changes" time="2016-01-29T12:40:28Z"
app[worker.1]: url="https://codeplane.com.br/" message="check finished" status="no changes" time="2016-01-29T12:40:28Z"
app[worker.1]: url="http://howtocode.com.br/" message="check finished" status="no changes" time="2016-01-29T12:40:28Z"
app[worker.1]: url="https://nandovieira.com.br/" message="check finished" status="no changes" time="2016-01-29T12:40:28Z"
app[worker.1]: url="https://presentta.com.br/" message="check finished" status="no changes" time="2016-01-29T12:40:28Z"
</code></pre></div>
<p>If you&rsquo;re not into Heroku, you can also deploy it to a VPS. If you already know how to deploy a Rails application, you won&rsquo;t have any problem. Since this involves too much things, I leave this as an exercise to the reader.</p>
<h3 tabindex="-1" id="keeping-up-with-master">Keeping up with master<a class="anchor" href="#keeping-up-with-master" aria-hidden="true" tabindex="-1"></a>
</h3>
<p>You may want to keep up with <code>master</code>, getting all the fixes and new features (if any). The workflow for updating your branch is pretty simple.</p>

<p>First, switch off to <code>master</code> and pull all the changes.</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ git checkout master
$ git pull
</code></pre></div>
<p>Then switch back to <code>personal</code> and apply <code>master</code>&lsquo;s onto it.</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ git checkout personal
$ git rebase master
</code></pre></div>
<p>Now, deploy the repository.</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ git push heroku personal:master -f
</code></pre></div><h2 tabindex="-1" id="wrapping-up">Wrapping up<a class="anchor" href="#wrapping-up" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>That&rsquo;s it! If you have any suggestions or need help, just <a href="https://github.com/fnando/uptime_checker/issues/new">open an issue</a>.</p>
]]>
      </content:encoded>
      <link>https://nandovieira.com/replacing-pingdom-with-ruby-and-heroku</link>
      <guid>https://nandovieira.com/replacing-pingdom-with-ruby-and-heroku</guid>
      <pubDate>Fri, 29 Jan 2016 09:47:00 -0200</pubDate>
    </item>
    <item>
      <title>Working with dates on Ruby on Rails</title>
      <description>
        <![CDATA[<p>Working with dates can be hard. You need to consider time zones, understand how to store dates in your database, parse strings into dates or even format dates and display them to the user. And there&rsquo;s the daylight saving time.</p>

<p>In this article we&rsquo;ll see how to use the utilities provided by Rails, so that your system can handle dates correctly.</p>
<h2 tabindex="-1" id="how-ruby-works">How Ruby works<a class="anchor" href="#how-ruby-works" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>Ruby has basically two different classes for handling dates: <code>Date</code> and <code>Time</code><sup id="fnref1"><a href="#fn1">1</a></sup>. You can generate dates by parsing strings or using individual attributes that represent the year, hour, month, and so on.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="nb">require</span> <span class="s2">"time"</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s2">"Dec 8 2015 10:19"</span><span class="p">)</span>
<span class="c1">#=&gt; 2015-12-08 10:19:00 -0200</span>

<span class="no">Date</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s2">"Dec 8 2015"</span><span class="p">)</span>
<span class="c1">#=&gt; #&lt;Date: 2015-12-08&gt;</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">19</span><span class="p">)</span>
<span class="c1">#=&gt; 2015-12-08 10:19:00 -0200</span>

<span class="no">Date</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">8</span><span class="p">)</span>
<span class="c1">#=&gt; #&lt;Date: 2015-12-08&gt;</span>
</code></pre></div>
<p>Every date calculation must be performed manually. Let&rsquo;s say you want to advance one hour; all you have to do is add 3600 seconds to the date.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="n">time</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">now</span>
<span class="c1">#=&gt; 2015-12-08 10:26:40 -0200</span>

<span class="n">time</span> <span class="o">+</span> <span class="mi">3600</span>
<span class="c1">#=&gt; 2015-12-08 11:26:40 -0200</span>
</code></pre></div>
<p>Most calculations are easy to implement, but things get complex when you have to consider time zones.</p>
<h3 tabindex="-1" id="working-with-time-zones">Working with time zones<a class="anchor" href="#working-with-time-zones" aria-hidden="true" tabindex="-1"></a>
</h3>
<p>To set the time zone in a Ruby script, you have to set the <code>TZ</code> environment variable. This variable is not defined in most systems, but that will depend on how your server is configured. Older POSIX systems used this variable but this done by <code>/etc/localtime</code> now.</p>

<p>This is how Ruby behaves with and without the <code>TZ</code> variable.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="no">ENV</span><span class="p">[</span><span class="s2">"TZ"</span><span class="p">]</span>
<span class="c1">#=&gt; nil</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">now</span>
<span class="c1">#=&gt; 2015-12-08 10:30:00 -0200</span>

<span class="no">ENV</span><span class="p">[</span><span class="s2">"TZ"</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"America/Los_Angeles"</span>
<span class="c1">#=&gt; "America/Los_Angeles"</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">now</span>
<span class="c1">#=&gt; 2015-12-08 04:30:14 -0800</span>
</code></pre></div>
<p>The <code>date</code> command, available in <em>*nix</em> systems, also use the <code>TZ</code> variable, changing how the date is presented in a user session.</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ date
Tue Dec  8 09:37:12 BRST 2015

$ export TZ=America/Los_Angeles

$ date
Tue Dec  8 03:37:27 PST 2015
</code></pre></div>
<p>The problem is that not every software out there will use this variable automatically (or use it at all). PostgreSQL won&rsquo;t use the <code>TZ</code> variable, preferring the <code>timezone</code> configuration<sup id="fnref2"><a href="#fn2">2</a></sup>.</p>
<div class="highlight"><pre class="highlight sql"><code><span class="k">SELECT</span> <span class="n">current_setting</span><span class="p">(</span><span class="s1">'timezone'</span><span class="p">);</span>
<span class="o">#=&gt;</span> <span class="n">Brazil</span><span class="o">/</span><span class="n">East</span>
</code></pre></div>
<p>You&rsquo;re better off avoiding the time zone in all pieces of your infrastructure; instead of using a specific time zone, just go with <a href="http://yellerapp.com/posts/2015-01-12-the-worst-server-setup-you-can-make.html">Etc/UTC</a>. This will free you from problems related to <abbr title="Daylight Saving Time">DST</abbr> and cron jobs. It will also be easier to define how different systems will work with dates and communicate. The time zone presentation should be the application&rsquo;s responsibility.</p>
<h2 tabindex="-1" id="how-ruby-on-rails-works">How Ruby on Rails works<a class="anchor" href="#how-ruby-on-rails-works" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>To define the time zone in Ruby on Rails application use the environment configuration file or create an initializer.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="c1"># config/initializers/time_zone.rb</span>
<span class="no">Time</span><span class="p">.</span><span class="nf">zone</span> <span class="o">=</span> <span class="s2">"America/Sao_Paulo"</span>
</code></pre></div>
<p>Ruby on Rails can have a time zone configuration per application, totally ignoring the <code>TZ</code> environment variable, but this behavior has some implications.</p>

<p>First, Ruby won&rsquo;t consider Rails&rsquo; time zone configuration. This means that if your system is using the <code>America/Sao_Paulo</code> time zone and your application is using <code>America/Los_Angeles</code>, Ruby will still consider the former configuration.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="no">Time</span><span class="p">.</span><span class="nf">now</span><span class="p">.</span><span class="nf">zone</span>
<span class="c1">#=&gt; "BRST"</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">zone</span> <span class="o">=</span> <span class="s2">"America/Los_Angeles"</span>
<span class="c1">#=&gt; "America/Los_Angeles"</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">now</span><span class="p">.</span><span class="nf">zone</span>
<span class="c1">#=&gt; "BRST"</span>
</code></pre></div>
<p>To generate dates that knows about the time zone, you should use methods defined by the ActiveSupport library. These methods will generate objects from the <code>ActiveSupport::TimeWithZone</code><sup id="fnref3"><a href="#fn3">3</a></sup> class. There are a large number of methods and their usage will depend on what you want to accomplish.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="no">Time</span><span class="p">.</span><span class="nf">zone</span><span class="p">.</span><span class="nf">now</span>
<span class="c1">#=&gt; Tue, 08 Dec 2015 03:37:57 PST -08:00</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">zone</span><span class="p">.</span><span class="nf">today</span>
<span class="c1">#=&gt; Tue, 08 Dec 2015</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">current</span>
<span class="c1">#=&gt; Tue, 08 Dec 2015 03:38:17 PST -08:00</span>

<span class="mi">1</span><span class="p">.</span><span class="nf">hour</span><span class="p">.</span><span class="nf">ago</span>
<span class="c1">#=&gt; Tue, 08 Dec 2015 02:38:28 PST -08:00</span>

<span class="mi">1</span><span class="p">.</span><span class="nf">day</span><span class="p">.</span><span class="nf">from_now</span>
<span class="c1">#=&gt; Wed, 09 Dec 2015 03:38:36 PST -08:00</span>

<span class="no">Date</span><span class="p">.</span><span class="nf">yesterday</span>
<span class="c1">#=&gt; Mon, 07 Dec 2015</span>

<span class="no">Date</span><span class="p">.</span><span class="nf">tomorrow</span>
<span class="c1">#=&gt; Wed, 09 Dec 2015</span>
</code></pre></div>
<p>The general guidelines are:</p>

<ul>
<li>Use <code>Time.current</code> instead of <code>Time.now</code>.</li>
<li>Use <code>Date.current</code> instead of <code>Date.today</code>.</li>
</ul>

<p>Notice that <code>Time.current</code> will ignore the time zone information if your application doesn&rsquo;t set it; the same will happen to <code>Date.current</code>, as you can see in ActiveSupport&rsquo;s implementation:</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="c1"># activesupport-4.2.5/lib/active_support/core_ext/time/calculations.rb</span>
<span class="c1"># line 30</span>
<span class="k">class</span> <span class="nc">Time</span>
  <span class="k">def</span> <span class="nf">current</span>
    <span class="o">::</span><span class="no">Time</span><span class="p">.</span><span class="nf">zone</span> <span class="p">?</span> <span class="o">::</span><span class="no">Time</span><span class="p">.</span><span class="nf">zone</span><span class="p">.</span><span class="nf">now</span> <span class="p">:</span> <span class="o">::</span><span class="no">Time</span><span class="p">.</span><span class="nf">now</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="c1"># activesupport-4.2.5/lib/active_support/core_ext/date/calculations.rb</span>
<span class="c1"># line 46</span>
<span class="k">class</span> <span class="nc">Date</span>
  <span class="k">def</span> <span class="nf">current</span>
    <span class="o">::</span><span class="no">Time</span><span class="p">.</span><span class="nf">zone</span> <span class="p">?</span> <span class="o">::</span><span class="no">Time</span><span class="p">.</span><span class="nf">zone</span><span class="p">.</span><span class="nf">today</span> <span class="p">:</span> <span class="o">::</span><span class="no">Date</span><span class="p">.</span><span class="nf">today</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div>
<p>You can always use <code>Time.zone.today</code> and <code>Time.zone.now</code> for a deterministic result.</p>
<h3 tabindex="-1" id="dealing-with-daylight-saving-time">Dealing with Daylight Saving Time<a class="anchor" href="#dealing-with-daylight-saving-time" aria-hidden="true" tabindex="-1"></a>
</h3>
<p>ActiveSupport uses the <a href="https://rubygems.org/gems/tzinfo">TZInfo</a> gem so it can know about time zones. This gem will load all this information from your operating system; in Ubuntu, this information comes from the <code>tzdata</code> package.</p>

<p>This means that keeping your server up-to-date will provide fresh information about <abbr title="Daylight Saving Time">DST</abbr> and all time zones from all around the world.</p>

<p>The code below shows how the dates know about <abbr title="Daylight Saving Time">DST</abbr>, considering Brazil&rsquo;s <abbr title="Daylight Saving Time">DST</abbr> for 2016.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="no">Time</span><span class="p">.</span><span class="nf">zone</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s1">'2015-10-17'</span><span class="p">).</span><span class="nf">dst?</span>
<span class="c1">#=&gt; false</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">zone</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s1">'2015-10-18'</span><span class="p">).</span><span class="nf">dst?</span>
<span class="c1">#=&gt; true</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">zone</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s1">'2016-02-20'</span><span class="p">).</span><span class="nf">dst?</span>
<span class="c1">#=&gt; true</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">zone</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s1">'2016-02-21'</span><span class="p">).</span><span class="nf">dst?</span>
<span class="c1">#=&gt; false</span>
</code></pre></div>
<p>Sometimes you have to parse dates and <abbr title="Daylight Saving Time">DST</abbr> can bring unexpected consequences. Recently I had to integrate some user-provided dates in calendar and the biggest challenge was displaying future dates, considering time zones and <abbr title="Daylight Saving Time">DST</abbr>.</p>

<p>Turns out the solution is simple; when parsing dates, ignore the time zone information from the string and use the <code>Time.use_zone</code>.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="no">Time</span><span class="p">.</span><span class="nf">current</span>
<span class="c1">#=&gt; Mon, 07 Dec 2015 18:57:51 BRST -02:00</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">use_zone</span><span class="p">(</span><span class="s2">"America/Sao_Paulo"</span><span class="p">)</span> <span class="k">do</span>
  <span class="n">starts_at</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">zone</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s2">"2016-03-05 10:00"</span><span class="p">)</span>
  <span class="c1">#=&gt; Sat, 05 Mar 2016 10:00:00 BRT -03:00</span>

  <span class="n">ends_at</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">zone</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s2">"2016-03-05 17:00"</span><span class="p">)</span>
  <span class="c1">#=&gt; Sat, 05 Mar 2016 17:00:00 BRT -03:00</span>
<span class="k">end</span>
</code></pre></div><h3 tabindex="-1" id="how-dates-are-persisted">How dates are persisted<a class="anchor" href="#how-dates-are-persisted" aria-hidden="true" tabindex="-1"></a>
</h3>
<p>ActiveRecord will persist dates in <a href="https://en.wikipedia.org/wiki/Coordinated_Universal_Time">UTC</a> (Coordinated Universal Time). This behavior is defined by the <code>ActiveRecord::Base.default_timezone</code> and <code>ActiveRecord::Base.time_zone_aware_attributes</code> properties.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span><span class="p">.</span><span class="nf">default_timezone</span>
<span class="c1">#=&gt; :utc</span>

<span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span><span class="p">.</span><span class="nf">time_zone_aware_attributes</span>
<span class="c1">#=&gt; true</span>
</code></pre></div>
<p>Every time you persist an date attribute to the database, ActiveRecord will convert it to UTC. And after loading a record, ActiveRecord will do the other way around, converting the date back to the time zone defined in your application<sup id="fnref4"><a href="#fn4">4</a></sup>.</p>

<p>Notice that dates can be off by some hours if you use <code>Date.today</code> or <code>Time.now</code>.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="no">Time</span><span class="p">.</span><span class="nf">zone</span> <span class="o">=</span> <span class="s2">"America/Los_Angeles"</span>
<span class="c1">#=&gt; "America/Los_Angeles"</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">now</span><span class="p">.</span><span class="nf">beginning_of_day</span><span class="p">.</span><span class="nf">utc</span>
<span class="c1">#=&gt; 2015-12-08 02:00:00 UTC</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">current</span><span class="p">.</span><span class="nf">beginning_of_day</span><span class="p">.</span><span class="nf">utc</span>
<span class="c1">#=&gt; 2015-12-08 08:00:00 UTC</span>
</code></pre></div>
<p>This happens because <code>Time.now</code> ignores the time zone defined by the application and will use the system&rsquo;s one (in this case <code>BRST-0200</code>). That&rsquo;s why is extremely important for you to use ActiveSupport methods, like I said before.</p>

<p>Since ActiveRecord assumes that all dates are persisted as UTC, you can also end up with wrongs dates if you update database records from outside the application. PostgreSQL has a column type that knows about time zones and will convert dates before persisting it. Unfortunately Rails doesn&rsquo;t support <a href="http://www.postgresql.org/docs/current/static/datatype-datetime.html"><code>time with time zone</code></a>, but you can easily add it to your application by creating an initializer with the following code:</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="c1"># config/initializers/active_record.rb</span>
<span class="no">ActiveRecord</span><span class="o">::</span><span class="no">ConnectionAdapters</span><span class="o">::</span><span class="no">PostgreSQLAdapter</span><span class="o">::</span>
  <span class="no">NATIVE_DATABASE_TYPES</span><span class="p">[</span><span class="ss">:datetime</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="ss">name: </span><span class="s2">"timestamp with time zone"</span><span class="p">}</span>
</code></pre></div><h3 tabindex="-1" id="querying-your-database">Querying your database<a class="anchor" href="#querying-your-database" aria-hidden="true" tabindex="-1"></a>
</h3>
<p>We already know that ActiveRecord assumes your dates are stored as UTC. To make your queries work as expected, all you have to do is use dates that have this time zone knowledge (like <code>Time.current</code>) and the library will do the right thing.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="no">Article</span><span class="p">.</span><span class="nf">where</span><span class="p">(</span><span class="s2">"published_at &gt;= ?"</span><span class="p">,</span> <span class="no">Time</span><span class="p">.</span><span class="nf">current</span><span class="p">)</span>
</code></pre></div>
<p>Remember that dates generated by <code>1.day.ago</code> and <code>Date.current.beginning_of_month</code> will consider the time zone, so feel free to use them.</p>
<h3 tabindex="-1" id="parsing-dates-sent-by-users">Parsing dates sent by users<a class="anchor" href="#parsing-dates-sent-by-users" aria-hidden="true" tabindex="-1"></a>
</h3>
<p>When parsing dates you must also consider time zones. Ruby has the methods <code>Time.parse</code> and <code>Date.parse</code>, but they ignore this information. In this case you should use <code>Time.zone.parse</code> or your dates can be off by a few hours.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="no">Time</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s2">"8:47am Dec 7th, 2015"</span><span class="p">).</span><span class="nf">utc</span>
<span class="c1">#=&gt; 2015-12-07 10:47:00 UTC</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">zone</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s2">"8:47am Dec 7th, 2015"</span><span class="p">).</span><span class="nf">utc</span>
<span class="c1">#=&gt; 2015-12-07 16:47:00 UTC</span>
</code></pre></div>
<p>If you have numbers that represent the date, use <code>Time.zone.local</code> instead of <code>Time.new</code>.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="no">Time</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">47</span><span class="p">,</span> <span class="mi">0</span><span class="p">).</span><span class="nf">utc</span>
<span class="c1">#=&gt; 2015-12-07 10:47:00 UTC</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">zone</span><span class="p">.</span><span class="nf">local</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">47</span><span class="p">,</span> <span class="mi">0</span><span class="p">).</span><span class="nf">utc</span>
<span class="c1">#=&gt; 2015-12-07 16:47:00 UTC</span>
</code></pre></div>
<p>Finally, use <code>Time.zone.at</code> if you have a Unix timestamp.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="no">Time</span><span class="p">.</span><span class="nf">zone</span><span class="p">.</span><span class="nf">at</span><span class="p">(</span><span class="mi">1449506820</span><span class="p">)</span>
<span class="c1">#=&gt; 2015-12-07 16:47:00 UTC</span>
</code></pre></div>
<p>To set the time zone for an execution context, use the method <code>Time.use_zone</code>, which sets the specified time zone only while running the block.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="no">Time</span><span class="p">.</span><span class="nf">zone</span> <span class="o">=</span> <span class="s2">"America/Sao_Paulo"</span>
<span class="c1">#=&gt; "America/Sao_Paulo"</span>

<span class="n">now_in_sao_paulo</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">current</span>
<span class="c1">#=&gt; Tue, 08 Dec 2015 09:40:31 BRST -02:00</span>

<span class="n">now_in_los_angeles</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">use_zone</span><span class="p">(</span><span class="s2">"America/Los_Angeles"</span><span class="p">)</span> <span class="k">do</span>
  <span class="no">Time</span><span class="p">.</span><span class="nf">current</span>
<span class="k">end</span>
<span class="c1">#=&gt; Tue, 08 Dec 2015 03:40:31 PST -08:00</span>

<span class="n">now_in_sao_paulo</span><span class="p">.</span><span class="nf">utc</span>
<span class="c1">#=&gt; 2015-12-08 11:40:31 UTC</span>

<span class="n">now_in_los_angeles</span><span class="p">.</span><span class="nf">utc</span>
<span class="c1">#=&gt; 2015-12-08 11:40:31 UTC</span>

<span class="n">now_in_sao_paulo</span><span class="p">.</span><span class="nf">to_i</span> <span class="o">==</span> <span class="n">now_in_los_angeles</span><span class="p">.</span><span class="nf">to_i</span>
<span class="c1">#=&gt; true</span>
</code></pre></div>
<p>You can use this method on your controller so you can set the user&rsquo;s time zone.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">ApplicationController</span> <span class="o">&lt;</span> <span class="no">ActionController</span><span class="o">::</span><span class="no">Base</span>
  <span class="n">around_action</span> <span class="ss">:set_timezone</span><span class="p">,</span> <span class="ss">if: :logged_in?</span>

  <span class="kp">private</span>

  <span class="k">def</span> <span class="nf">set_timezone</span><span class="p">(</span><span class="o">&amp;</span><span class="n">action</span><span class="p">)</span>
    <span class="no">Time</span><span class="p">.</span><span class="nf">use_zone</span><span class="p">(</span><span class="n">current_user</span><span class="p">.</span><span class="nf">time_zone</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">action</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div><h3 tabindex="-1" id="working-with-dates-in-javascript">Working with dates in JavaScript<a class="anchor" href="#working-with-dates-in-javascript" aria-hidden="true" tabindex="-1"></a>
</h3>
<p>You may need to communicate with your Rails application from JavaScript. The best way of dealing with dates is by using <a href="http://momentjs.com">moment.js</a>.</p>

<p>Use the <code>Time#iso8601</code> method to send formatted dates to JavaScript; this will generate something like <code>2015-12-07T19:25:45Z</code>. You can then use moment.js to convert this string into a JavaScript <code>Date</code> object.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="kd">var</span> <span class="nx">date</span> <span class="o">=</span> <span class="nf">moment</span><span class="p">(</span><span class="dl">"</span><span class="s2">2015-12-07T19:25:45Z</span><span class="dl">"</span><span class="p">);</span>
<span class="c1">//=&gt; Mon Dec 07 2015 17:25:45 GMT-0200 (BRST)</span>

<span class="nx">date</span><span class="p">.</span><span class="nf">format</span><span class="p">(</span><span class="dl">"</span><span class="s2">MMM DD, YYYY - h:mma</span><span class="dl">"</span><span class="p">);</span>
<span class="c1">//=&gt; Dec 07, 2015 - 5:25pm</span>
</code></pre></div>
<p>Notice that moment.js will use the browser&rsquo;s time zone (<code>BRST-0200</code> in my case); if you need to convert the date to a different time zone, use <a href="http://momentjs.com/timezone">momentjs-timezone</a>.</p>
<h2 tabindex="-1" id="wrapping-up">Wrapping up<a class="anchor" href="#wrapping-up" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>If you application deals with dates, most notably date calculations, understand how Rails&rsquo; date handling works is essential. To make your life easier, just follow these rules:</p>

<ul>
<li>Use <code>Etc/UTC</code> as your server&rsquo;s time zone.</li>
<li>Always define the time zone in your Rails application, even if it&rsquo;s <code>Etc/UTC</code>.</li>
<li>Deal with time zone presentation on your application&rsquo;s layer.</li>
<li>Use <code>Time.current</code> and <code>Date.current</code> to retrieve the current date.</li>
<li>Use <code>Time.zone.parse</code> to convert string into dates.</li>
<li>Convert <code>Date</code> em <code>ActiveSupport::TimeWithZone</code> for comparison, like <code>date.in_time_zone == Time.current.beginning_of_day</code>.</li>
</ul>
<h2 tabindex="-1" id="resources">Resources<a class="anchor" href="#resources" aria-hidden="true" tabindex="-1"></a>
</h2>
<ul>
<li><a href="http://brendankemp.com/essays/dealing-with-time-zones-using-rails-and-postgres/">Dealing With Time Zones Using Rails and Postgres</a></li>
<li><a href="https://www.reinteractive.net/posts/168-dealing-with-timezones-effectively-in-rails">Dealing with timezones effectively in Rails</a></li>
<li><a href="http://alwayscoding.ca/momentos/2013/08/22/handling-dates-and-timezones-in-ruby-and-rails/">Handling Dates &amp; Timezones in Ruby &amp; Rails</a></li>
<li><a href="http://brendankemp.com/essays/handling-time-zones-in-rails/">Handling Time Zones in Rails</a></li>
<li><a href="http://makandracards.com/makandra/646-how-rails-and-mysql-are-handling-time-zones">How Rails and MySQL are handling time zones</a></li>
<li><a href="http://danilenko.org/2012/7/6/rails_timezones/">The Exhaustive Guide to Rails Time Zones</a></li>
<li><a href="http://yellerapp.com/posts/2015-01-12-the-worst-server-setup-you-can-make.html">The Worst Server Setup Mistake You Can Make</a></li>
<li><a href="http://www.elabs.se/blog/36-working-with-time-zones-in-ruby-on-rails">Working with time zones in Ruby on Rails</a></li>
</ul>

<div class="footnotes">
<hr>
<ol>

<li id="fn1">
<p>There&rsquo;s another class called <code>DateTime</code>, which is just a <code>Date</code> subclass that knows about the time part. The <code>Time</code> class had limitations on older Ruby versions running in 32-bit systems; in this case you should use <code>DateTime</code> instead. Ruby 1.9.2 fixed this problem and you can just use <code>Date</code> and <code>Time</code> classes, which now have <a href="https://gist.github.com/fnando/54ddc21c4640d09c30ce">similar performance</a>.&nbsp;<a href="#fnref1">&#8617;</a></p>
</li>

<li id="fn2">
<p>PostgreSQL will only use the <code>TZ</code> environment variable if the <code>timezone</code> configuration is not specified on your <code>postgresql.conf</code> file. Since this configuration always have a default value, is unlikely that PostgreSQL will use <code>TZ</code> unless you really want it to.&nbsp;<a href="#fnref2">&#8617;</a></p>
</li>

<li id="fn3">
<p>Methods like <code>Time.zone.today</code>, <code>Date.yesterday</code> and <code>Date.tomorrow</code> return objects from the <code>Date</code> class and don&rsquo;t know about time zone. Convert the <code>Date</code> object into a <code>ActiveSupport::TimeWithZone</code> instance with <code>date.in_time_zone</code>.&nbsp;<a href="#fnref3">&#8617;</a></p>
</li>

<li id="fn4">
<p>The <code>ActiveRecord::Base.time_zone_aware_attributes</code> configuration is disabled if you&rsquo;re using ActiveRecord outside Rails.&nbsp;<a href="#fnref4">&#8617;</a></p>
</li>

</ol>
</div>
]]>
      </description>
      <content:encoded>
        <![CDATA[<p>Working with dates can be hard. You need to consider time zones, understand how to store dates in your database, parse strings into dates or even format dates and display them to the user. And there&rsquo;s the daylight saving time.</p>

<p>In this article we&rsquo;ll see how to use the utilities provided by Rails, so that your system can handle dates correctly.</p>
<h2 tabindex="-1" id="how-ruby-works">How Ruby works<a class="anchor" href="#how-ruby-works" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>Ruby has basically two different classes for handling dates: <code>Date</code> and <code>Time</code><sup id="fnref1"><a href="#fn1">1</a></sup>. You can generate dates by parsing strings or using individual attributes that represent the year, hour, month, and so on.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="nb">require</span> <span class="s2">"time"</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s2">"Dec 8 2015 10:19"</span><span class="p">)</span>
<span class="c1">#=&gt; 2015-12-08 10:19:00 -0200</span>

<span class="no">Date</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s2">"Dec 8 2015"</span><span class="p">)</span>
<span class="c1">#=&gt; #&lt;Date: 2015-12-08&gt;</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">19</span><span class="p">)</span>
<span class="c1">#=&gt; 2015-12-08 10:19:00 -0200</span>

<span class="no">Date</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">8</span><span class="p">)</span>
<span class="c1">#=&gt; #&lt;Date: 2015-12-08&gt;</span>
</code></pre></div>
<p>Every date calculation must be performed manually. Let&rsquo;s say you want to advance one hour; all you have to do is add 3600 seconds to the date.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="n">time</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">now</span>
<span class="c1">#=&gt; 2015-12-08 10:26:40 -0200</span>

<span class="n">time</span> <span class="o">+</span> <span class="mi">3600</span>
<span class="c1">#=&gt; 2015-12-08 11:26:40 -0200</span>
</code></pre></div>
<p>Most calculations are easy to implement, but things get complex when you have to consider time zones.</p>
<h3 tabindex="-1" id="working-with-time-zones">Working with time zones<a class="anchor" href="#working-with-time-zones" aria-hidden="true" tabindex="-1"></a>
</h3>
<p>To set the time zone in a Ruby script, you have to set the <code>TZ</code> environment variable. This variable is not defined in most systems, but that will depend on how your server is configured. Older POSIX systems used this variable but this done by <code>/etc/localtime</code> now.</p>

<p>This is how Ruby behaves with and without the <code>TZ</code> variable.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="no">ENV</span><span class="p">[</span><span class="s2">"TZ"</span><span class="p">]</span>
<span class="c1">#=&gt; nil</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">now</span>
<span class="c1">#=&gt; 2015-12-08 10:30:00 -0200</span>

<span class="no">ENV</span><span class="p">[</span><span class="s2">"TZ"</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"America/Los_Angeles"</span>
<span class="c1">#=&gt; "America/Los_Angeles"</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">now</span>
<span class="c1">#=&gt; 2015-12-08 04:30:14 -0800</span>
</code></pre></div>
<p>The <code>date</code> command, available in <em>*nix</em> systems, also use the <code>TZ</code> variable, changing how the date is presented in a user session.</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ date
Tue Dec  8 09:37:12 BRST 2015

$ export TZ=America/Los_Angeles

$ date
Tue Dec  8 03:37:27 PST 2015
</code></pre></div>
<p>The problem is that not every software out there will use this variable automatically (or use it at all). PostgreSQL won&rsquo;t use the <code>TZ</code> variable, preferring the <code>timezone</code> configuration<sup id="fnref2"><a href="#fn2">2</a></sup>.</p>
<div class="highlight"><pre class="highlight sql"><code><span class="k">SELECT</span> <span class="n">current_setting</span><span class="p">(</span><span class="s1">'timezone'</span><span class="p">);</span>
<span class="o">#=&gt;</span> <span class="n">Brazil</span><span class="o">/</span><span class="n">East</span>
</code></pre></div>
<p>You&rsquo;re better off avoiding the time zone in all pieces of your infrastructure; instead of using a specific time zone, just go with <a href="http://yellerapp.com/posts/2015-01-12-the-worst-server-setup-you-can-make.html">Etc/UTC</a>. This will free you from problems related to <abbr title="Daylight Saving Time">DST</abbr> and cron jobs. It will also be easier to define how different systems will work with dates and communicate. The time zone presentation should be the application&rsquo;s responsibility.</p>
<h2 tabindex="-1" id="how-ruby-on-rails-works">How Ruby on Rails works<a class="anchor" href="#how-ruby-on-rails-works" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>To define the time zone in Ruby on Rails application use the environment configuration file or create an initializer.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="c1"># config/initializers/time_zone.rb</span>
<span class="no">Time</span><span class="p">.</span><span class="nf">zone</span> <span class="o">=</span> <span class="s2">"America/Sao_Paulo"</span>
</code></pre></div>
<p>Ruby on Rails can have a time zone configuration per application, totally ignoring the <code>TZ</code> environment variable, but this behavior has some implications.</p>

<p>First, Ruby won&rsquo;t consider Rails&rsquo; time zone configuration. This means that if your system is using the <code>America/Sao_Paulo</code> time zone and your application is using <code>America/Los_Angeles</code>, Ruby will still consider the former configuration.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="no">Time</span><span class="p">.</span><span class="nf">now</span><span class="p">.</span><span class="nf">zone</span>
<span class="c1">#=&gt; "BRST"</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">zone</span> <span class="o">=</span> <span class="s2">"America/Los_Angeles"</span>
<span class="c1">#=&gt; "America/Los_Angeles"</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">now</span><span class="p">.</span><span class="nf">zone</span>
<span class="c1">#=&gt; "BRST"</span>
</code></pre></div>
<p>To generate dates that knows about the time zone, you should use methods defined by the ActiveSupport library. These methods will generate objects from the <code>ActiveSupport::TimeWithZone</code><sup id="fnref3"><a href="#fn3">3</a></sup> class. There are a large number of methods and their usage will depend on what you want to accomplish.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="no">Time</span><span class="p">.</span><span class="nf">zone</span><span class="p">.</span><span class="nf">now</span>
<span class="c1">#=&gt; Tue, 08 Dec 2015 03:37:57 PST -08:00</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">zone</span><span class="p">.</span><span class="nf">today</span>
<span class="c1">#=&gt; Tue, 08 Dec 2015</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">current</span>
<span class="c1">#=&gt; Tue, 08 Dec 2015 03:38:17 PST -08:00</span>

<span class="mi">1</span><span class="p">.</span><span class="nf">hour</span><span class="p">.</span><span class="nf">ago</span>
<span class="c1">#=&gt; Tue, 08 Dec 2015 02:38:28 PST -08:00</span>

<span class="mi">1</span><span class="p">.</span><span class="nf">day</span><span class="p">.</span><span class="nf">from_now</span>
<span class="c1">#=&gt; Wed, 09 Dec 2015 03:38:36 PST -08:00</span>

<span class="no">Date</span><span class="p">.</span><span class="nf">yesterday</span>
<span class="c1">#=&gt; Mon, 07 Dec 2015</span>

<span class="no">Date</span><span class="p">.</span><span class="nf">tomorrow</span>
<span class="c1">#=&gt; Wed, 09 Dec 2015</span>
</code></pre></div>
<p>The general guidelines are:</p>

<ul>
<li>Use <code>Time.current</code> instead of <code>Time.now</code>.</li>
<li>Use <code>Date.current</code> instead of <code>Date.today</code>.</li>
</ul>

<p>Notice that <code>Time.current</code> will ignore the time zone information if your application doesn&rsquo;t set it; the same will happen to <code>Date.current</code>, as you can see in ActiveSupport&rsquo;s implementation:</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="c1"># activesupport-4.2.5/lib/active_support/core_ext/time/calculations.rb</span>
<span class="c1"># line 30</span>
<span class="k">class</span> <span class="nc">Time</span>
  <span class="k">def</span> <span class="nf">current</span>
    <span class="o">::</span><span class="no">Time</span><span class="p">.</span><span class="nf">zone</span> <span class="p">?</span> <span class="o">::</span><span class="no">Time</span><span class="p">.</span><span class="nf">zone</span><span class="p">.</span><span class="nf">now</span> <span class="p">:</span> <span class="o">::</span><span class="no">Time</span><span class="p">.</span><span class="nf">now</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="c1"># activesupport-4.2.5/lib/active_support/core_ext/date/calculations.rb</span>
<span class="c1"># line 46</span>
<span class="k">class</span> <span class="nc">Date</span>
  <span class="k">def</span> <span class="nf">current</span>
    <span class="o">::</span><span class="no">Time</span><span class="p">.</span><span class="nf">zone</span> <span class="p">?</span> <span class="o">::</span><span class="no">Time</span><span class="p">.</span><span class="nf">zone</span><span class="p">.</span><span class="nf">today</span> <span class="p">:</span> <span class="o">::</span><span class="no">Date</span><span class="p">.</span><span class="nf">today</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div>
<p>You can always use <code>Time.zone.today</code> and <code>Time.zone.now</code> for a deterministic result.</p>
<h3 tabindex="-1" id="dealing-with-daylight-saving-time">Dealing with Daylight Saving Time<a class="anchor" href="#dealing-with-daylight-saving-time" aria-hidden="true" tabindex="-1"></a>
</h3>
<p>ActiveSupport uses the <a href="https://rubygems.org/gems/tzinfo">TZInfo</a> gem so it can know about time zones. This gem will load all this information from your operating system; in Ubuntu, this information comes from the <code>tzdata</code> package.</p>

<p>This means that keeping your server up-to-date will provide fresh information about <abbr title="Daylight Saving Time">DST</abbr> and all time zones from all around the world.</p>

<p>The code below shows how the dates know about <abbr title="Daylight Saving Time">DST</abbr>, considering Brazil&rsquo;s <abbr title="Daylight Saving Time">DST</abbr> for 2016.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="no">Time</span><span class="p">.</span><span class="nf">zone</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s1">'2015-10-17'</span><span class="p">).</span><span class="nf">dst?</span>
<span class="c1">#=&gt; false</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">zone</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s1">'2015-10-18'</span><span class="p">).</span><span class="nf">dst?</span>
<span class="c1">#=&gt; true</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">zone</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s1">'2016-02-20'</span><span class="p">).</span><span class="nf">dst?</span>
<span class="c1">#=&gt; true</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">zone</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s1">'2016-02-21'</span><span class="p">).</span><span class="nf">dst?</span>
<span class="c1">#=&gt; false</span>
</code></pre></div>
<p>Sometimes you have to parse dates and <abbr title="Daylight Saving Time">DST</abbr> can bring unexpected consequences. Recently I had to integrate some user-provided dates in calendar and the biggest challenge was displaying future dates, considering time zones and <abbr title="Daylight Saving Time">DST</abbr>.</p>

<p>Turns out the solution is simple; when parsing dates, ignore the time zone information from the string and use the <code>Time.use_zone</code>.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="no">Time</span><span class="p">.</span><span class="nf">current</span>
<span class="c1">#=&gt; Mon, 07 Dec 2015 18:57:51 BRST -02:00</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">use_zone</span><span class="p">(</span><span class="s2">"America/Sao_Paulo"</span><span class="p">)</span> <span class="k">do</span>
  <span class="n">starts_at</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">zone</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s2">"2016-03-05 10:00"</span><span class="p">)</span>
  <span class="c1">#=&gt; Sat, 05 Mar 2016 10:00:00 BRT -03:00</span>

  <span class="n">ends_at</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">zone</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s2">"2016-03-05 17:00"</span><span class="p">)</span>
  <span class="c1">#=&gt; Sat, 05 Mar 2016 17:00:00 BRT -03:00</span>
<span class="k">end</span>
</code></pre></div><h3 tabindex="-1" id="how-dates-are-persisted">How dates are persisted<a class="anchor" href="#how-dates-are-persisted" aria-hidden="true" tabindex="-1"></a>
</h3>
<p>ActiveRecord will persist dates in <a href="https://en.wikipedia.org/wiki/Coordinated_Universal_Time">UTC</a> (Coordinated Universal Time). This behavior is defined by the <code>ActiveRecord::Base.default_timezone</code> and <code>ActiveRecord::Base.time_zone_aware_attributes</code> properties.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span><span class="p">.</span><span class="nf">default_timezone</span>
<span class="c1">#=&gt; :utc</span>

<span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span><span class="p">.</span><span class="nf">time_zone_aware_attributes</span>
<span class="c1">#=&gt; true</span>
</code></pre></div>
<p>Every time you persist an date attribute to the database, ActiveRecord will convert it to UTC. And after loading a record, ActiveRecord will do the other way around, converting the date back to the time zone defined in your application<sup id="fnref4"><a href="#fn4">4</a></sup>.</p>

<p>Notice that dates can be off by some hours if you use <code>Date.today</code> or <code>Time.now</code>.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="no">Time</span><span class="p">.</span><span class="nf">zone</span> <span class="o">=</span> <span class="s2">"America/Los_Angeles"</span>
<span class="c1">#=&gt; "America/Los_Angeles"</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">now</span><span class="p">.</span><span class="nf">beginning_of_day</span><span class="p">.</span><span class="nf">utc</span>
<span class="c1">#=&gt; 2015-12-08 02:00:00 UTC</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">current</span><span class="p">.</span><span class="nf">beginning_of_day</span><span class="p">.</span><span class="nf">utc</span>
<span class="c1">#=&gt; 2015-12-08 08:00:00 UTC</span>
</code></pre></div>
<p>This happens because <code>Time.now</code> ignores the time zone defined by the application and will use the system&rsquo;s one (in this case <code>BRST-0200</code>). That&rsquo;s why is extremely important for you to use ActiveSupport methods, like I said before.</p>

<p>Since ActiveRecord assumes that all dates are persisted as UTC, you can also end up with wrongs dates if you update database records from outside the application. PostgreSQL has a column type that knows about time zones and will convert dates before persisting it. Unfortunately Rails doesn&rsquo;t support <a href="http://www.postgresql.org/docs/current/static/datatype-datetime.html"><code>time with time zone</code></a>, but you can easily add it to your application by creating an initializer with the following code:</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="c1"># config/initializers/active_record.rb</span>
<span class="no">ActiveRecord</span><span class="o">::</span><span class="no">ConnectionAdapters</span><span class="o">::</span><span class="no">PostgreSQLAdapter</span><span class="o">::</span>
  <span class="no">NATIVE_DATABASE_TYPES</span><span class="p">[</span><span class="ss">:datetime</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="ss">name: </span><span class="s2">"timestamp with time zone"</span><span class="p">}</span>
</code></pre></div><h3 tabindex="-1" id="querying-your-database">Querying your database<a class="anchor" href="#querying-your-database" aria-hidden="true" tabindex="-1"></a>
</h3>
<p>We already know that ActiveRecord assumes your dates are stored as UTC. To make your queries work as expected, all you have to do is use dates that have this time zone knowledge (like <code>Time.current</code>) and the library will do the right thing.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="no">Article</span><span class="p">.</span><span class="nf">where</span><span class="p">(</span><span class="s2">"published_at &gt;= ?"</span><span class="p">,</span> <span class="no">Time</span><span class="p">.</span><span class="nf">current</span><span class="p">)</span>
</code></pre></div>
<p>Remember that dates generated by <code>1.day.ago</code> and <code>Date.current.beginning_of_month</code> will consider the time zone, so feel free to use them.</p>
<h3 tabindex="-1" id="parsing-dates-sent-by-users">Parsing dates sent by users<a class="anchor" href="#parsing-dates-sent-by-users" aria-hidden="true" tabindex="-1"></a>
</h3>
<p>When parsing dates you must also consider time zones. Ruby has the methods <code>Time.parse</code> and <code>Date.parse</code>, but they ignore this information. In this case you should use <code>Time.zone.parse</code> or your dates can be off by a few hours.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="no">Time</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s2">"8:47am Dec 7th, 2015"</span><span class="p">).</span><span class="nf">utc</span>
<span class="c1">#=&gt; 2015-12-07 10:47:00 UTC</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">zone</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s2">"8:47am Dec 7th, 2015"</span><span class="p">).</span><span class="nf">utc</span>
<span class="c1">#=&gt; 2015-12-07 16:47:00 UTC</span>
</code></pre></div>
<p>If you have numbers that represent the date, use <code>Time.zone.local</code> instead of <code>Time.new</code>.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="no">Time</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">47</span><span class="p">,</span> <span class="mi">0</span><span class="p">).</span><span class="nf">utc</span>
<span class="c1">#=&gt; 2015-12-07 10:47:00 UTC</span>

<span class="no">Time</span><span class="p">.</span><span class="nf">zone</span><span class="p">.</span><span class="nf">local</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">47</span><span class="p">,</span> <span class="mi">0</span><span class="p">).</span><span class="nf">utc</span>
<span class="c1">#=&gt; 2015-12-07 16:47:00 UTC</span>
</code></pre></div>
<p>Finally, use <code>Time.zone.at</code> if you have a Unix timestamp.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="no">Time</span><span class="p">.</span><span class="nf">zone</span><span class="p">.</span><span class="nf">at</span><span class="p">(</span><span class="mi">1449506820</span><span class="p">)</span>
<span class="c1">#=&gt; 2015-12-07 16:47:00 UTC</span>
</code></pre></div>
<p>To set the time zone for an execution context, use the method <code>Time.use_zone</code>, which sets the specified time zone only while running the block.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="no">Time</span><span class="p">.</span><span class="nf">zone</span> <span class="o">=</span> <span class="s2">"America/Sao_Paulo"</span>
<span class="c1">#=&gt; "America/Sao_Paulo"</span>

<span class="n">now_in_sao_paulo</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">current</span>
<span class="c1">#=&gt; Tue, 08 Dec 2015 09:40:31 BRST -02:00</span>

<span class="n">now_in_los_angeles</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">use_zone</span><span class="p">(</span><span class="s2">"America/Los_Angeles"</span><span class="p">)</span> <span class="k">do</span>
  <span class="no">Time</span><span class="p">.</span><span class="nf">current</span>
<span class="k">end</span>
<span class="c1">#=&gt; Tue, 08 Dec 2015 03:40:31 PST -08:00</span>

<span class="n">now_in_sao_paulo</span><span class="p">.</span><span class="nf">utc</span>
<span class="c1">#=&gt; 2015-12-08 11:40:31 UTC</span>

<span class="n">now_in_los_angeles</span><span class="p">.</span><span class="nf">utc</span>
<span class="c1">#=&gt; 2015-12-08 11:40:31 UTC</span>

<span class="n">now_in_sao_paulo</span><span class="p">.</span><span class="nf">to_i</span> <span class="o">==</span> <span class="n">now_in_los_angeles</span><span class="p">.</span><span class="nf">to_i</span>
<span class="c1">#=&gt; true</span>
</code></pre></div>
<p>You can use this method on your controller so you can set the user&rsquo;s time zone.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">ApplicationController</span> <span class="o">&lt;</span> <span class="no">ActionController</span><span class="o">::</span><span class="no">Base</span>
  <span class="n">around_action</span> <span class="ss">:set_timezone</span><span class="p">,</span> <span class="ss">if: :logged_in?</span>

  <span class="kp">private</span>

  <span class="k">def</span> <span class="nf">set_timezone</span><span class="p">(</span><span class="o">&amp;</span><span class="n">action</span><span class="p">)</span>
    <span class="no">Time</span><span class="p">.</span><span class="nf">use_zone</span><span class="p">(</span><span class="n">current_user</span><span class="p">.</span><span class="nf">time_zone</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">action</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div><h3 tabindex="-1" id="working-with-dates-in-javascript">Working with dates in JavaScript<a class="anchor" href="#working-with-dates-in-javascript" aria-hidden="true" tabindex="-1"></a>
</h3>
<p>You may need to communicate with your Rails application from JavaScript. The best way of dealing with dates is by using <a href="http://momentjs.com">moment.js</a>.</p>

<p>Use the <code>Time#iso8601</code> method to send formatted dates to JavaScript; this will generate something like <code>2015-12-07T19:25:45Z</code>. You can then use moment.js to convert this string into a JavaScript <code>Date</code> object.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="kd">var</span> <span class="nx">date</span> <span class="o">=</span> <span class="nf">moment</span><span class="p">(</span><span class="dl">"</span><span class="s2">2015-12-07T19:25:45Z</span><span class="dl">"</span><span class="p">);</span>
<span class="c1">//=&gt; Mon Dec 07 2015 17:25:45 GMT-0200 (BRST)</span>

<span class="nx">date</span><span class="p">.</span><span class="nf">format</span><span class="p">(</span><span class="dl">"</span><span class="s2">MMM DD, YYYY - h:mma</span><span class="dl">"</span><span class="p">);</span>
<span class="c1">//=&gt; Dec 07, 2015 - 5:25pm</span>
</code></pre></div>
<p>Notice that moment.js will use the browser&rsquo;s time zone (<code>BRST-0200</code> in my case); if you need to convert the date to a different time zone, use <a href="http://momentjs.com/timezone">momentjs-timezone</a>.</p>
<h2 tabindex="-1" id="wrapping-up">Wrapping up<a class="anchor" href="#wrapping-up" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>If you application deals with dates, most notably date calculations, understand how Rails&rsquo; date handling works is essential. To make your life easier, just follow these rules:</p>

<ul>
<li>Use <code>Etc/UTC</code> as your server&rsquo;s time zone.</li>
<li>Always define the time zone in your Rails application, even if it&rsquo;s <code>Etc/UTC</code>.</li>
<li>Deal with time zone presentation on your application&rsquo;s layer.</li>
<li>Use <code>Time.current</code> and <code>Date.current</code> to retrieve the current date.</li>
<li>Use <code>Time.zone.parse</code> to convert string into dates.</li>
<li>Convert <code>Date</code> em <code>ActiveSupport::TimeWithZone</code> for comparison, like <code>date.in_time_zone == Time.current.beginning_of_day</code>.</li>
</ul>
<h2 tabindex="-1" id="resources">Resources<a class="anchor" href="#resources" aria-hidden="true" tabindex="-1"></a>
</h2>
<ul>
<li><a href="http://brendankemp.com/essays/dealing-with-time-zones-using-rails-and-postgres/">Dealing With Time Zones Using Rails and Postgres</a></li>
<li><a href="https://www.reinteractive.net/posts/168-dealing-with-timezones-effectively-in-rails">Dealing with timezones effectively in Rails</a></li>
<li><a href="http://alwayscoding.ca/momentos/2013/08/22/handling-dates-and-timezones-in-ruby-and-rails/">Handling Dates &amp; Timezones in Ruby &amp; Rails</a></li>
<li><a href="http://brendankemp.com/essays/handling-time-zones-in-rails/">Handling Time Zones in Rails</a></li>
<li><a href="http://makandracards.com/makandra/646-how-rails-and-mysql-are-handling-time-zones">How Rails and MySQL are handling time zones</a></li>
<li><a href="http://danilenko.org/2012/7/6/rails_timezones/">The Exhaustive Guide to Rails Time Zones</a></li>
<li><a href="http://yellerapp.com/posts/2015-01-12-the-worst-server-setup-you-can-make.html">The Worst Server Setup Mistake You Can Make</a></li>
<li><a href="http://www.elabs.se/blog/36-working-with-time-zones-in-ruby-on-rails">Working with time zones in Ruby on Rails</a></li>
</ul>

<div class="footnotes">
<hr>
<ol>

<li id="fn1">
<p>There&rsquo;s another class called <code>DateTime</code>, which is just a <code>Date</code> subclass that knows about the time part. The <code>Time</code> class had limitations on older Ruby versions running in 32-bit systems; in this case you should use <code>DateTime</code> instead. Ruby 1.9.2 fixed this problem and you can just use <code>Date</code> and <code>Time</code> classes, which now have <a href="https://gist.github.com/fnando/54ddc21c4640d09c30ce">similar performance</a>.&nbsp;<a href="#fnref1">&#8617;</a></p>
</li>

<li id="fn2">
<p>PostgreSQL will only use the <code>TZ</code> environment variable if the <code>timezone</code> configuration is not specified on your <code>postgresql.conf</code> file. Since this configuration always have a default value, is unlikely that PostgreSQL will use <code>TZ</code> unless you really want it to.&nbsp;<a href="#fnref2">&#8617;</a></p>
</li>

<li id="fn3">
<p>Methods like <code>Time.zone.today</code>, <code>Date.yesterday</code> and <code>Date.tomorrow</code> return objects from the <code>Date</code> class and don&rsquo;t know about time zone. Convert the <code>Date</code> object into a <code>ActiveSupport::TimeWithZone</code> instance with <code>date.in_time_zone</code>.&nbsp;<a href="#fnref3">&#8617;</a></p>
</li>

<li id="fn4">
<p>The <code>ActiveRecord::Base.time_zone_aware_attributes</code> configuration is disabled if you&rsquo;re using ActiveRecord outside Rails.&nbsp;<a href="#fnref4">&#8617;</a></p>
</li>

</ol>
</div>
]]>
      </content:encoded>
      <link>https://nandovieira.com/working-with-dates-on-ruby-on-rails</link>
      <guid>https://nandovieira.com/working-with-dates-on-ruby-on-rails</guid>
      <pubDate>Wed, 09 Dec 2015 10:40:00 -0200</pubDate>
    </item>
    <item>
      <title>Using ES6 with Asset Pipeline on Ruby on Rails</title>
      <description>
        <![CDATA[<blockquote><p><small>This article has been updated. Read <a href="//nandovieira.com/using-es2015-with-asset-pipeline-on-ruby-on-rails">Using ES2015 with Asset Pipeline on Ruby on Rails</a> instead.</small></p>
</blockquote>
<p>JavaScript is all new. Until recently, we had no new features. The last significant update was back in 2009, with <abbr title="ECMAScript 5">ES5</abbr>&lsquo;s release. And you couldn&rsquo;t use all features due to browser incompatibility.</p>

<p>To increase the compatibility level, we had to use things like <a href="https://github.com/es-shims/es5-shim/">es5-shim</a>, which conditionally checked if a feature was available, adding a <em>polyfill</em> if the browser didn&rsquo;t implement it.</p>

<p>And then the first pre-processors came in, like <a href="http://coffeescript.org">CoffeeScript</a>. You could write different constructions, that were compiled to code that the browsers could actually understand.</p>

<p>Interestingly, <a href="https://brendaneich.com">Brendan Eich</a> announced in 2009 a new JavaScript version called <em>Harmony</em><sup id="fnref1"><a href="#fn1">1</a></sup>, which is now called <abbr title="ECMAScript 6">ES6</abbr>. The first drafts were published in 2011, but the final specification was released in June of this year.</p>

<p><abbr title="ECMAScript 6">ES6</abbr> has so many new features:</p>

<ul>
<li>Class definition</li>
<li>String interpolation</li>
<li>Fat arrow functions</li>
<li><a href="http://babeljs.io/docs/learn-es2015/">More!</a></li>
</ul>

<p>Browsers are implementing new features in a fast pace, but it would still take some time until we could actually use <abbr title="ECMAScript 6">ES6</abbr>. Maybe years. Fortunately, we have <a href="https://babeljs.io">Babel.js</a>. We can use all these new features today without worrying with browser compatibility.</p>

<p>Babel.js<sup id="fnref2"><a href="#fn2">2</a></sup> is just a pre-processor. You write code that uses these new features, which will be exported as code that browsers can understand, even those that don&rsquo;t fully understand <abbr title="ECMAScript 6">ES6</abbr>.</p>
<h2 tabindex="-1" id="using-babel-js">Using Babel.js<a class="anchor" href="#using-babel-js" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>To install Babel make sure you have <a href="http://nodejs.com">Node.js</a> installed. Then you can install Babel using NPM.</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ npm install babel -g
</code></pre></div>
<p>You can now use <code>babel</code> command to <em>compile</em> your JavaScript files. You can enable the watch mode, which will automatically compile modified files. The following example with watch <code>src</code> and export files to <code>dist</code>.</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ babel --watch --out-dir=dist src
</code></pre></div>
<p>One of the features I like the most is the new class definition, which abstracts constructor functions. In the following example I create a <code>User</code> class that receives two arguments in the initialization.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="kd">class</span> <span class="nc">User</span> <span class="p">{</span>
  <span class="nf">constructor</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">email</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="nx">name</span><span class="p">;</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">email</span> <span class="o">=</span> <span class="nx">email</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>After processing this file with Babel, this is what we have:</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="dl">'</span><span class="s1">use strict</span><span class="dl">'</span><span class="p">;</span>

<span class="kd">var</span> <span class="nx">_createClass</span> <span class="o">=</span> <span class="p">(</span><span class="nf">function </span><span class="p">()</span> <span class="p">{</span> <span class="kd">function</span> <span class="nf">defineProperties</span><span class="p">(</span><span class="nx">target</span><span class="p">,</span> <span class="nx">props</span><span class="p">)</span> <span class="p">{</span> <span class="k">for </span><span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">props</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">descriptor</span> <span class="o">=</span> <span class="nx">props</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span> <span class="nx">descriptor</span><span class="p">.</span><span class="nx">enumerable</span> <span class="o">=</span> <span class="nx">descriptor</span><span class="p">.</span><span class="nx">enumerable</span> <span class="o">||</span> <span class="kc">false</span><span class="p">;</span> <span class="nx">descriptor</span><span class="p">.</span><span class="nx">configurable</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span> <span class="k">if </span><span class="p">(</span><span class="dl">'</span><span class="s1">value</span><span class="dl">'</span> <span class="k">in</span> <span class="nx">descriptor</span><span class="p">)</span> <span class="nx">descriptor</span><span class="p">.</span><span class="nx">writable</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span> <span class="nb">Object</span><span class="p">.</span><span class="nf">defineProperty</span><span class="p">(</span><span class="nx">target</span><span class="p">,</span> <span class="nx">descriptor</span><span class="p">.</span><span class="nx">key</span><span class="p">,</span> <span class="nx">descriptor</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="k">return</span> <span class="nf">function </span><span class="p">(</span><span class="nx">Constructor</span><span class="p">,</span> <span class="nx">protoProps</span><span class="p">,</span> <span class="nx">staticProps</span><span class="p">)</span> <span class="p">{</span> <span class="k">if </span><span class="p">(</span><span class="nx">protoProps</span><span class="p">)</span> <span class="nf">defineProperties</span><span class="p">(</span><span class="nx">Constructor</span><span class="p">.</span><span class="nx">prototype</span><span class="p">,</span> <span class="nx">protoProps</span><span class="p">);</span> <span class="k">if </span><span class="p">(</span><span class="nx">staticProps</span><span class="p">)</span> <span class="nf">defineProperties</span><span class="p">(</span><span class="nx">Constructor</span><span class="p">,</span> <span class="nx">staticProps</span><span class="p">);</span> <span class="k">return</span> <span class="nx">Constructor</span><span class="p">;</span> <span class="p">};</span> <span class="p">})();</span>

<span class="kd">function</span> <span class="nf">_classCallCheck</span><span class="p">(</span><span class="nx">instance</span><span class="p">,</span> <span class="nx">Constructor</span><span class="p">)</span> <span class="p">{</span> <span class="k">if </span><span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="nx">instance</span> <span class="k">instanceof</span> <span class="nx">Constructor</span><span class="p">))</span> <span class="p">{</span> <span class="k">throw</span> <span class="k">new</span> <span class="nc">TypeError</span><span class="p">(</span><span class="dl">'</span><span class="s1">Cannot call a class as a function</span><span class="dl">'</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span>

<span class="kd">var</span> <span class="nx">User</span> <span class="o">=</span> <span class="p">(</span><span class="nf">function </span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">function</span> <span class="nf">User</span><span class="p">()</span> <span class="p">{</span>
    <span class="nf">_classCallCheck</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="nx">User</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="nf">_createClass</span><span class="p">(</span><span class="nx">User</span><span class="p">,</span> <span class="p">[{</span>
    <span class="na">key</span><span class="p">:</span> <span class="dl">'</span><span class="s1">construct</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">value</span><span class="p">:</span> <span class="kd">function</span> <span class="nf">construct</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">email</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="nx">name</span><span class="p">;</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">email</span> <span class="o">=</span> <span class="nx">email</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}]);</span>

  <span class="k">return</span> <span class="nx">User</span><span class="p">;</span>
<span class="p">})();</span>

<span class="kd">var</span> <span class="nx">user</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">User</span><span class="p">(</span><span class="dl">'</span><span class="s1">John</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">john@example.com</span><span class="dl">'</span><span class="p">);</span>

<span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">name:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">user</span><span class="p">.</span><span class="nx">name</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">email:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">user</span><span class="p">.</span><span class="nx">email</span><span class="p">);</span>
</code></pre></div>
<p>Note that Babel generates all the required code for supporting the class definition. Alternatively, you can use <code>--external-helpers</code> to generate code that uses helpers (helpers can be generated with the <code>babel-external-helpers</code> command).</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ babel --external-helpers --watch --out-dir=dist src
</code></pre></div>
<p>Now all supporting code will use the <code>babelHelpers</code> object.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="dl">'</span><span class="s1">use strict</span><span class="dl">'</span><span class="p">;</span>

<span class="kd">var</span> <span class="nx">User</span> <span class="o">=</span> <span class="p">(</span><span class="nf">function </span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">function</span> <span class="nf">User</span><span class="p">()</span> <span class="p">{</span>
    <span class="nx">babelHelpers</span><span class="p">.</span><span class="nf">classCallCheck</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="nx">User</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="nx">babelHelpers</span><span class="p">.</span><span class="nf">createClass</span><span class="p">(</span><span class="nx">User</span><span class="p">,</span> <span class="p">[{</span>
    <span class="na">key</span><span class="p">:</span> <span class="dl">'</span><span class="s1">construct</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">value</span><span class="p">:</span> <span class="kd">function</span> <span class="nf">construct</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">email</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="nx">name</span><span class="p">;</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">email</span> <span class="o">=</span> <span class="nx">email</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}]);</span>
  <span class="k">return</span> <span class="nx">User</span><span class="p">;</span>
<span class="p">})();</span>

<span class="kd">var</span> <span class="nx">user</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">User</span><span class="p">(</span><span class="dl">'</span><span class="s1">John</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">john@example.com</span><span class="dl">'</span><span class="p">);</span>

<span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">name:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">user</span><span class="p">.</span><span class="nx">name</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">email:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">user</span><span class="p">.</span><span class="nx">email</span><span class="p">);</span>
</code></pre></div><h2 tabindex="-1" id="using-babel-js-with-asset-pipeline">Using Babel.js with Asset Pipeline<a class="anchor" href="#using-babel-js-with-asset-pipeline" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>Instead of using Babel&rsquo;s <abbr title="Command-Line Interface">CLI</abbr>, some people prefer <em>builders</em> like <a href="http://gruntjs.com">Grunt</a> or <a href="http://gulpjs.com">Gulp</a> to automate the compilation process. But if you&rsquo;re using Ruby on Rails you&rsquo;re more likely to use <a href="http://guides.rubyonrails.org/asset_pipeline.html">Asset Pipeline</a> for front-end assets compilation.</p>

<p>Unfortunately there&rsquo;s no built-in support on the stable release of <a href="https://github.com/rails/sprockets">Sprockets</a>. But if you like to live on the edge<sup id="fnref3"><a href="#fn3">3</a></sup>, you can use the <em>master</em> branch.</p>

<p>Update your <code>Gemfile</code> to include these dependencies.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="n">source</span> <span class="s1">'https://rubygems.org'</span>

<span class="n">gem</span> <span class="s1">'rails'</span><span class="p">,</span> <span class="s1">'4.2.4'</span>
<span class="n">gem</span> <span class="s1">'sqlite3'</span>
<span class="n">gem</span> <span class="s1">'uglifier'</span><span class="p">,</span> <span class="s1">'&gt;= 1.3.0'</span>

<span class="n">gem</span> <span class="s1">'sass-rails'</span><span class="p">,</span> <span class="ss">github: </span><span class="s1">'rails/sass-rails'</span><span class="p">,</span> <span class="ss">branch: </span><span class="s1">'master'</span>
<span class="n">gem</span> <span class="s1">'sprockets-rails'</span><span class="p">,</span> <span class="ss">github: </span><span class="s1">'rails/sprockets-rails'</span><span class="p">,</span> <span class="ss">branch: </span><span class="s1">'master'</span>
<span class="n">gem</span> <span class="s1">'sprockets'</span><span class="p">,</span> <span class="ss">github: </span><span class="s1">'rails/sprockets'</span><span class="p">,</span> <span class="ss">branch: </span><span class="s1">'master'</span>
<span class="n">gem</span> <span class="s1">'babel-transpiler'</span>

<span class="n">gem</span> <span class="s1">'turbolinks'</span>
<span class="n">gem</span> <span class="s1">'jquery-rails'</span>
</code></pre></div>
<p>That&rsquo;s it! Now all <code>.es6</code> files will be compiled using Babel. Create a file at <code>app/assets/javascripts/hello.es6</code> with the following code:</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="kd">class</span> <span class="nc">Hello</span> <span class="p">{</span>
  <span class="nf">constructor</span><span class="p">()</span> <span class="p">{</span>
    <span class="nf">alert</span><span class="p">(</span><span class="dl">'</span><span class="s1">Hello!</span><span class="dl">'</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="k">new</span> <span class="nc">Hello</span><span class="p">();</span>
</code></pre></div>
<p>Make sure you&rsquo;re loading <code>hello.es6</code> at <code>app/assets/javascripts/application.js</code>.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="c1">//= require_tree .</span>
<span class="c1">//= require_self</span>
</code></pre></div>
<p>Now if you access the page on your browser, you&rsquo;ll see an <code>alert</code> box like this:</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/es6-alert-hello.png" alt="Alert box - Hello"></p>

<p>If you want the new <a href="https://babeljs.io/docs/usage/modules/">module system</a>, you still have some things to configure.</p>
<h3 tabindex="-1" id="using-es6-modules">Using <abbr title="ECMAScript 6">ES6</abbr> modules<a class="anchor" href="#using-es6-modules" aria-hidden="true" tabindex="-1"></a>
</h3>
<p><abbr title="ECMAScript 6">ES6</abbr> introduced module support; instead of defining your code in the global scope, you can use a scope per file. Importing modules is pretty straightforward:</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="k">import</span> <span class="nx">Foo</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">foo</span><span class="dl">'</span><span class="p">;</span>
</code></pre></div>
<p>This will load <code>Foo</code> from <code>foo.js</code>. One single file can export several units (functions, objects, anything you want), and you don&rsquo;t have to load everything at once, pretty much like <a href="http://python.org">Python</a>.</p>

<p>Babel has no idea of how these modules should be exported, and by default, will use the <a href="http://www.commonjs.org/specs/modules/1.0/">CommonJS</a> format, which can&rsquo;t be used by the browser, so we&rsquo;ll use another approach.</p>

<p>The easiest way is using <a href="http://requirejs.org/docs/whyamd.html">AMD</a>, and for this we&rsquo;ll use <a href="https://github.com/jrburke/almond">almond</a>. You can install almond with whatever you&rsquo;re using for managing packages; in this article I&rsquo;ll use <a href="http://rails-assets.org">http://rails-assets.org</a>, a bower-to-rubygems converter. Update your <code>Gemfile</code> like the following:</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="n">source</span> <span class="s1">'https://rubygems.org'</span>

<span class="n">gem</span> <span class="s1">'rails'</span><span class="p">,</span> <span class="s1">'4.2.4'</span>
<span class="n">gem</span> <span class="s1">'sqlite3'</span>
<span class="n">gem</span> <span class="s1">'uglifier'</span><span class="p">,</span> <span class="s1">'&gt;= 1.3.0'</span>

<span class="n">gem</span> <span class="s1">'sass-rails'</span><span class="p">,</span> <span class="ss">github: </span><span class="s1">'rails/sass-rails'</span><span class="p">,</span> <span class="ss">branch: </span><span class="s1">'master'</span>
<span class="n">gem</span> <span class="s1">'sprockets-rails'</span><span class="p">,</span> <span class="ss">github: </span><span class="s1">'rails/sprockets-rails'</span><span class="p">,</span> <span class="ss">branch: </span><span class="s1">'master'</span>
<span class="n">gem</span> <span class="s1">'sprockets'</span><span class="p">,</span> <span class="ss">github: </span><span class="s1">'rails/sprockets'</span><span class="p">,</span> <span class="ss">branch: </span><span class="s1">'master'</span>
<span class="n">gem</span> <span class="s1">'babel-transpiler'</span>

<span class="n">gem</span> <span class="s1">'turbolinks'</span>
<span class="n">gem</span> <span class="s1">'jquery-rails'</span>

<span class="n">source</span> <span class="s1">'https://rails-assets.org'</span> <span class="k">do</span>
  <span class="n">gem</span> <span class="s1">'rails-assets-almond'</span>
<span class="k">end</span>
</code></pre></div>
<p>You also have to configure Babel; just create a file at <code>config/initializers/babel.rb</code> with the following code:</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">config</span><span class="p">.</span><span class="nf">assets</span><span class="p">.</span><span class="nf">configure</span> <span class="k">do</span> <span class="o">|</span><span class="n">env</span><span class="o">|</span>
  <span class="n">babel</span> <span class="o">=</span> <span class="no">Sprockets</span><span class="o">::</span><span class="no">BabelProcessor</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span>
    <span class="s1">'modules'</span>    <span class="o">=&gt;</span> <span class="s1">'amd'</span><span class="p">,</span>
    <span class="s1">'moduleIds'</span>  <span class="o">=&gt;</span> <span class="kp">true</span>
  <span class="p">)</span>
  <span class="n">env</span><span class="p">.</span><span class="nf">register_transformer</span> <span class="s1">'application/ecmascript-6'</span><span class="p">,</span> <span class="s1">'application/javascript'</span><span class="p">,</span> <span class="n">babel</span>
<span class="k">end</span>
</code></pre></div>
<p>Finally, update <code>app/assets/javascripts/application.js</code> so it loads almond.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="c1">//= require almond</span>
<span class="c1">//= require jquery</span>
<span class="c1">//= require turbolinks</span>
<span class="c1">//= require_tree .</span>
<span class="c1">//= require_self</span>
</code></pre></div>
<p>Now you have to think about the execution process. Are you going to use some code dispatcher? Or execute code based on the view that is being rendered? Are you going to create your own execution mechanism? The answer depends on your own workflow, so I&rsquo;m not going to give you too many alternatives here.</p>

<p>We&rsquo;re going to create a boot script that uses the controller and action names to execute the JavaScript you need for that specific page. Just add the <code>require</code> call to your <code>app/assets/javascripts/application.js</code> file.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="c1">//= require almond</span>
<span class="c1">//= require jquery</span>
<span class="c1">//= require turbolinks</span>
<span class="c1">//= require_tree .</span>
<span class="c1">//= require_self</span>

<span class="nf">require</span><span class="p">([</span><span class="dl">'</span><span class="s1">application/boot</span><span class="dl">'</span><span class="p">]);</span>
</code></pre></div>
<p>You have to create <code>app/assets/javascripts/application/boot.es6</code>. I&rsquo;ll listen to some events, like DOM&rsquo;s <code>ready</code> and Turbolinks&rsquo; <code>page load</code>.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="k">import</span> <span class="nx">$</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">jquery</span><span class="dl">'</span><span class="p">;</span>

<span class="kd">function</span> <span class="nf">runner</span><span class="p">()</span> <span class="p">{</span>
  <span class="c1">// All scripts must live in app/assets/javascripts/application/pages/**/*.es6.</span>
  <span class="kd">var</span> <span class="nx">path</span> <span class="o">=</span> <span class="nf">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">body</span><span class="dl">'</span><span class="p">).</span><span class="nf">data</span><span class="p">(</span><span class="dl">'</span><span class="s1">route</span><span class="dl">'</span><span class="p">);</span>

  <span class="c1">// Load script for this page.</span>
  <span class="c1">// We should use System.import, but it's not worth the trouble, so</span>
  <span class="c1">// let's use almond's require instead.</span>
  <span class="k">try</span> <span class="p">{</span>
    <span class="nf">require</span><span class="p">([</span><span class="nx">path</span><span class="p">],</span> <span class="nx">onload</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">catch </span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
    <span class="nf">handleError</span><span class="p">(</span><span class="nx">error</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nf">onload</span><span class="p">(</span><span class="nx">Page</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// Instantiate the page, passing &lt;body&gt; as the root element.</span>
  <span class="kd">var</span> <span class="nx">page</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Page</span><span class="p">(</span><span class="nf">$</span><span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">));</span>

  <span class="c1">// Set up page and run scripts for it.</span>
  <span class="k">if </span><span class="p">(</span><span class="nx">page</span><span class="p">.</span><span class="nx">setup</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">page</span><span class="p">.</span><span class="nf">setup</span><span class="p">();</span>
  <span class="p">}</span>

  <span class="nx">page</span><span class="p">.</span><span class="nf">run</span><span class="p">();</span>
<span class="p">}</span>

<span class="c1">// Handles exception.</span>
<span class="kd">function</span> <span class="nf">handleError</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if </span><span class="p">(</span><span class="nx">error</span><span class="p">.</span><span class="nx">message</span><span class="p">.</span><span class="nf">match</span><span class="p">(</span><span class="sr">/undefined missing/</span><span class="p">))</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nf">warn</span><span class="p">(</span><span class="dl">'</span><span class="s1">missing module:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">error</span><span class="p">.</span><span class="nx">message</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="dl">'</span><span class="s1"> </span><span class="dl">'</span><span class="p">).</span><span class="nf">pop</span><span class="p">());</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="k">throw</span> <span class="nx">error</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="nf">$</span><span class="p">(</span><span class="nb">window</span><span class="p">)</span>
  <span class="p">.</span><span class="nf">ready</span><span class="p">(</span><span class="nx">runner</span><span class="p">)</span>
  <span class="p">.</span><span class="nf">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">page:load</span><span class="dl">'</span><span class="p">,</span> <span class="nx">runner</span><span class="p">);</span>
</code></pre></div>
<p>This script needs a <code>data-route</code> property property on your <code>&lt;body&gt;</code> element. You can add something like the following to your layout file (e.g. <code>app/views/layouts/application.html.erb</code>):</p>
<div class="highlight"><pre class="highlight erb"><code><span class="nt">&lt;body</span> <span class="na">data-route=</span><span class="s">"application/pages/</span><span class="cp">&lt;%=</span> <span class="n">controller</span><span class="p">.</span><span class="nf">controller_name</span> <span class="cp">%&gt;</span><span class="s">/</span><span class="cp">&lt;%=</span> <span class="n">controller</span><span class="p">.</span><span class="nf">action_name</span> <span class="cp">%&gt;</span><span class="s">"</span><span class="nt">&gt;</span>
</code></pre></div>
<p>Now let&rsquo;s create a class that will be executed when the template is rendered. We&rsquo;ll use the <code>site</code> controller and <code>home</code> action as example. For this you&rsquo;ll need to create the <code>app/assets/javascripts/application/pages/site/home.es6</code> file.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="k">export</span> <span class="k">default</span> <span class="kd">class</span> <span class="nc">Home</span> <span class="p">{</span>
  <span class="nf">constructor</span><span class="p">(</span><span class="nx">root</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">root</span> <span class="o">=</span> <span class="nx">root</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
    <span class="c1">// add event listeners</span>
    <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">-&gt; setting up listeners and whatnot</span><span class="dl">'</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="nf">run</span><span class="p">()</span> <span class="p">{</span>
    <span class="c1">// trigger initial action (e.g. perform http requests)</span>
    <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">-&gt; perform initial actions</span><span class="dl">'</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>Note that we&rsquo;re exporting the <code>Home</code> class as the default module. This is the module that will be used when you have something like <code>import Home from &#39;application/pages/home&#39;;</code>.</p>

<p>Also note that we&rsquo;re defining the <code>Home#constructor</code> method; this is the method that is executed when the class is instantiated.</p>

<p>I said this before, but class definition is one of the things I like the most. Compare it with the constructor function form:</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="kd">function</span> <span class="nf">Home</span><span class="p">(</span><span class="nx">root</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">this</span><span class="p">.</span><span class="nx">root</span> <span class="o">=</span> <span class="nx">root</span><span class="p">;</span>
<span class="p">}</span>

<span class="nx">Home</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">setup</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  <span class="c1">// add listeners</span>
<span class="p">};</span>

<span class="nx">Home</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">run</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  <span class="c1">// initial execution</span>
<span class="p">};</span>
</code></pre></div>
<p>They&rsquo;re are similar, but using the <code>class</code> keyword makes closer to what we use in other languages. Here&rsquo;s how you would write the same class in Ruby:</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">Home</span>
  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">root</span><span class="p">)</span>
    <span class="vi">@root</span> <span class="o">=</span> <span class="n">root</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">setup</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">run</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div>
<p><abbr title="ECMAScript 6">ES6</abbr> has other niceties. To discover what&rsquo;s new, I recommend the <a href="http://exploringjs.com/">Exploring <abbr title="ECMAScript 6">ES6</abbr></a> book, which <a href="http://exploringjs.com/es6/">you can read for free</a>.</p>
<h3 tabindex="-1" id="almond-js-gotcha">Almond.js gotcha<a class="anchor" href="#almond-js-gotcha" aria-hidden="true" tabindex="-1"></a>
</h3>
<p>There is one gotcha when using Almond; all modules must be explicitly named. This means that libraries like <a href="https://qunitjs.com">qunit</a> won&rsquo;t work out of the box because they&rsquo;re anonymous modules. To solve this problem, you should load the library before loading Almond and then exporting the module yourself.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="c1">//= require qunit</span>
<span class="c1">//= require almond</span>

<span class="nf">define</span><span class="p">(</span><span class="dl">'</span><span class="s1">qunit</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">QUnit</span><span class="p">;</span>
<span class="p">});</span>
</code></pre></div>
<p>The problem of doing this is that the library won&rsquo;t detect AMD support and will export global variables, but I still prefer this behavior over compiling code with an optimizer like <a href="http://requirejs.org/docs/download.html#rjs">r.js</a> or a library that integrates that into Rails.</p>
<h2 tabindex="-1" id="wrapping-up">Wrapping up<a class="anchor" href="#wrapping-up" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>Using <abbr title="ECMAScript 6">ES6</abbr> today is a viable option. With Babel you can use all these new features without worrying with browser compatibility. The integration with Asset Pipeline make things easier, even for those that don&rsquo;t fully grasp the Node.js ecosystem.</p>

<p>There&rsquo;s a working repository <a href="https://github.com/fnando/using-es6-with-asset-pipeline-on-ruby-on-rails/tree/babel-transpiler">available at Github</a>.</p>

<div class="footnotes">
<hr>
<ol>

<li id="fn1">
<p><abbr title="ECMAScript 6">ES6</abbr> is also known as <em>ES.Next</em> or <em>ES2015</em> (this is the official name, defined after I first published this article).&nbsp;<a href="#fnref1">&#8617;</a></p>
</li>

<li id="fn2">
<p>This project used to be called <em>6to5</em>.js. <a href="http://babeljs.io/blog/2015/02/15/not-born-to-die/">Read more.</a>&nbsp;<a href="#fnref2">&#8617;</a></p>
</li>

<li id="fn3">
<p>The <a href="https://golang.org">Go</a> community does this all the time, so maybe is not a big deal. ¯\<em>(ツ)</em>/¯&nbsp;<a href="#fnref3">&#8617;</a></p>
</li>

</ol>
</div>
]]>
      </description>
      <content:encoded>
        <![CDATA[<blockquote><p><small>This article has been updated. Read <a href="//nandovieira.com/using-es2015-with-asset-pipeline-on-ruby-on-rails">Using ES2015 with Asset Pipeline on Ruby on Rails</a> instead.</small></p>
</blockquote>
<p>JavaScript is all new. Until recently, we had no new features. The last significant update was back in 2009, with <abbr title="ECMAScript 5">ES5</abbr>&lsquo;s release. And you couldn&rsquo;t use all features due to browser incompatibility.</p>

<p>To increase the compatibility level, we had to use things like <a href="https://github.com/es-shims/es5-shim/">es5-shim</a>, which conditionally checked if a feature was available, adding a <em>polyfill</em> if the browser didn&rsquo;t implement it.</p>

<p>And then the first pre-processors came in, like <a href="http://coffeescript.org">CoffeeScript</a>. You could write different constructions, that were compiled to code that the browsers could actually understand.</p>

<p>Interestingly, <a href="https://brendaneich.com">Brendan Eich</a> announced in 2009 a new JavaScript version called <em>Harmony</em><sup id="fnref1"><a href="#fn1">1</a></sup>, which is now called <abbr title="ECMAScript 6">ES6</abbr>. The first drafts were published in 2011, but the final specification was released in June of this year.</p>

<p><abbr title="ECMAScript 6">ES6</abbr> has so many new features:</p>

<ul>
<li>Class definition</li>
<li>String interpolation</li>
<li>Fat arrow functions</li>
<li><a href="http://babeljs.io/docs/learn-es2015/">More!</a></li>
</ul>

<p>Browsers are implementing new features in a fast pace, but it would still take some time until we could actually use <abbr title="ECMAScript 6">ES6</abbr>. Maybe years. Fortunately, we have <a href="https://babeljs.io">Babel.js</a>. We can use all these new features today without worrying with browser compatibility.</p>

<p>Babel.js<sup id="fnref2"><a href="#fn2">2</a></sup> is just a pre-processor. You write code that uses these new features, which will be exported as code that browsers can understand, even those that don&rsquo;t fully understand <abbr title="ECMAScript 6">ES6</abbr>.</p>
<h2 tabindex="-1" id="using-babel-js">Using Babel.js<a class="anchor" href="#using-babel-js" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>To install Babel make sure you have <a href="http://nodejs.com">Node.js</a> installed. Then you can install Babel using NPM.</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ npm install babel -g
</code></pre></div>
<p>You can now use <code>babel</code> command to <em>compile</em> your JavaScript files. You can enable the watch mode, which will automatically compile modified files. The following example with watch <code>src</code> and export files to <code>dist</code>.</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ babel --watch --out-dir=dist src
</code></pre></div>
<p>One of the features I like the most is the new class definition, which abstracts constructor functions. In the following example I create a <code>User</code> class that receives two arguments in the initialization.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="kd">class</span> <span class="nc">User</span> <span class="p">{</span>
  <span class="nf">constructor</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">email</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="nx">name</span><span class="p">;</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">email</span> <span class="o">=</span> <span class="nx">email</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>After processing this file with Babel, this is what we have:</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="dl">'</span><span class="s1">use strict</span><span class="dl">'</span><span class="p">;</span>

<span class="kd">var</span> <span class="nx">_createClass</span> <span class="o">=</span> <span class="p">(</span><span class="nf">function </span><span class="p">()</span> <span class="p">{</span> <span class="kd">function</span> <span class="nf">defineProperties</span><span class="p">(</span><span class="nx">target</span><span class="p">,</span> <span class="nx">props</span><span class="p">)</span> <span class="p">{</span> <span class="k">for </span><span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">props</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">descriptor</span> <span class="o">=</span> <span class="nx">props</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span> <span class="nx">descriptor</span><span class="p">.</span><span class="nx">enumerable</span> <span class="o">=</span> <span class="nx">descriptor</span><span class="p">.</span><span class="nx">enumerable</span> <span class="o">||</span> <span class="kc">false</span><span class="p">;</span> <span class="nx">descriptor</span><span class="p">.</span><span class="nx">configurable</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span> <span class="k">if </span><span class="p">(</span><span class="dl">'</span><span class="s1">value</span><span class="dl">'</span> <span class="k">in</span> <span class="nx">descriptor</span><span class="p">)</span> <span class="nx">descriptor</span><span class="p">.</span><span class="nx">writable</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span> <span class="nb">Object</span><span class="p">.</span><span class="nf">defineProperty</span><span class="p">(</span><span class="nx">target</span><span class="p">,</span> <span class="nx">descriptor</span><span class="p">.</span><span class="nx">key</span><span class="p">,</span> <span class="nx">descriptor</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="k">return</span> <span class="nf">function </span><span class="p">(</span><span class="nx">Constructor</span><span class="p">,</span> <span class="nx">protoProps</span><span class="p">,</span> <span class="nx">staticProps</span><span class="p">)</span> <span class="p">{</span> <span class="k">if </span><span class="p">(</span><span class="nx">protoProps</span><span class="p">)</span> <span class="nf">defineProperties</span><span class="p">(</span><span class="nx">Constructor</span><span class="p">.</span><span class="nx">prototype</span><span class="p">,</span> <span class="nx">protoProps</span><span class="p">);</span> <span class="k">if </span><span class="p">(</span><span class="nx">staticProps</span><span class="p">)</span> <span class="nf">defineProperties</span><span class="p">(</span><span class="nx">Constructor</span><span class="p">,</span> <span class="nx">staticProps</span><span class="p">);</span> <span class="k">return</span> <span class="nx">Constructor</span><span class="p">;</span> <span class="p">};</span> <span class="p">})();</span>

<span class="kd">function</span> <span class="nf">_classCallCheck</span><span class="p">(</span><span class="nx">instance</span><span class="p">,</span> <span class="nx">Constructor</span><span class="p">)</span> <span class="p">{</span> <span class="k">if </span><span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="nx">instance</span> <span class="k">instanceof</span> <span class="nx">Constructor</span><span class="p">))</span> <span class="p">{</span> <span class="k">throw</span> <span class="k">new</span> <span class="nc">TypeError</span><span class="p">(</span><span class="dl">'</span><span class="s1">Cannot call a class as a function</span><span class="dl">'</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span>

<span class="kd">var</span> <span class="nx">User</span> <span class="o">=</span> <span class="p">(</span><span class="nf">function </span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">function</span> <span class="nf">User</span><span class="p">()</span> <span class="p">{</span>
    <span class="nf">_classCallCheck</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="nx">User</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="nf">_createClass</span><span class="p">(</span><span class="nx">User</span><span class="p">,</span> <span class="p">[{</span>
    <span class="na">key</span><span class="p">:</span> <span class="dl">'</span><span class="s1">construct</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">value</span><span class="p">:</span> <span class="kd">function</span> <span class="nf">construct</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">email</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="nx">name</span><span class="p">;</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">email</span> <span class="o">=</span> <span class="nx">email</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}]);</span>

  <span class="k">return</span> <span class="nx">User</span><span class="p">;</span>
<span class="p">})();</span>

<span class="kd">var</span> <span class="nx">user</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">User</span><span class="p">(</span><span class="dl">'</span><span class="s1">John</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">john@example.com</span><span class="dl">'</span><span class="p">);</span>

<span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">name:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">user</span><span class="p">.</span><span class="nx">name</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">email:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">user</span><span class="p">.</span><span class="nx">email</span><span class="p">);</span>
</code></pre></div>
<p>Note that Babel generates all the required code for supporting the class definition. Alternatively, you can use <code>--external-helpers</code> to generate code that uses helpers (helpers can be generated with the <code>babel-external-helpers</code> command).</p>
<div class="highlight"><pre class="highlight plaintext"><code>$ babel --external-helpers --watch --out-dir=dist src
</code></pre></div>
<p>Now all supporting code will use the <code>babelHelpers</code> object.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="dl">'</span><span class="s1">use strict</span><span class="dl">'</span><span class="p">;</span>

<span class="kd">var</span> <span class="nx">User</span> <span class="o">=</span> <span class="p">(</span><span class="nf">function </span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">function</span> <span class="nf">User</span><span class="p">()</span> <span class="p">{</span>
    <span class="nx">babelHelpers</span><span class="p">.</span><span class="nf">classCallCheck</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="nx">User</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="nx">babelHelpers</span><span class="p">.</span><span class="nf">createClass</span><span class="p">(</span><span class="nx">User</span><span class="p">,</span> <span class="p">[{</span>
    <span class="na">key</span><span class="p">:</span> <span class="dl">'</span><span class="s1">construct</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">value</span><span class="p">:</span> <span class="kd">function</span> <span class="nf">construct</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">email</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="nx">name</span><span class="p">;</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">email</span> <span class="o">=</span> <span class="nx">email</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}]);</span>
  <span class="k">return</span> <span class="nx">User</span><span class="p">;</span>
<span class="p">})();</span>

<span class="kd">var</span> <span class="nx">user</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">User</span><span class="p">(</span><span class="dl">'</span><span class="s1">John</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">john@example.com</span><span class="dl">'</span><span class="p">);</span>

<span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">name:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">user</span><span class="p">.</span><span class="nx">name</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">email:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">user</span><span class="p">.</span><span class="nx">email</span><span class="p">);</span>
</code></pre></div><h2 tabindex="-1" id="using-babel-js-with-asset-pipeline">Using Babel.js with Asset Pipeline<a class="anchor" href="#using-babel-js-with-asset-pipeline" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>Instead of using Babel&rsquo;s <abbr title="Command-Line Interface">CLI</abbr>, some people prefer <em>builders</em> like <a href="http://gruntjs.com">Grunt</a> or <a href="http://gulpjs.com">Gulp</a> to automate the compilation process. But if you&rsquo;re using Ruby on Rails you&rsquo;re more likely to use <a href="http://guides.rubyonrails.org/asset_pipeline.html">Asset Pipeline</a> for front-end assets compilation.</p>

<p>Unfortunately there&rsquo;s no built-in support on the stable release of <a href="https://github.com/rails/sprockets">Sprockets</a>. But if you like to live on the edge<sup id="fnref3"><a href="#fn3">3</a></sup>, you can use the <em>master</em> branch.</p>

<p>Update your <code>Gemfile</code> to include these dependencies.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="n">source</span> <span class="s1">'https://rubygems.org'</span>

<span class="n">gem</span> <span class="s1">'rails'</span><span class="p">,</span> <span class="s1">'4.2.4'</span>
<span class="n">gem</span> <span class="s1">'sqlite3'</span>
<span class="n">gem</span> <span class="s1">'uglifier'</span><span class="p">,</span> <span class="s1">'&gt;= 1.3.0'</span>

<span class="n">gem</span> <span class="s1">'sass-rails'</span><span class="p">,</span> <span class="ss">github: </span><span class="s1">'rails/sass-rails'</span><span class="p">,</span> <span class="ss">branch: </span><span class="s1">'master'</span>
<span class="n">gem</span> <span class="s1">'sprockets-rails'</span><span class="p">,</span> <span class="ss">github: </span><span class="s1">'rails/sprockets-rails'</span><span class="p">,</span> <span class="ss">branch: </span><span class="s1">'master'</span>
<span class="n">gem</span> <span class="s1">'sprockets'</span><span class="p">,</span> <span class="ss">github: </span><span class="s1">'rails/sprockets'</span><span class="p">,</span> <span class="ss">branch: </span><span class="s1">'master'</span>
<span class="n">gem</span> <span class="s1">'babel-transpiler'</span>

<span class="n">gem</span> <span class="s1">'turbolinks'</span>
<span class="n">gem</span> <span class="s1">'jquery-rails'</span>
</code></pre></div>
<p>That&rsquo;s it! Now all <code>.es6</code> files will be compiled using Babel. Create a file at <code>app/assets/javascripts/hello.es6</code> with the following code:</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="kd">class</span> <span class="nc">Hello</span> <span class="p">{</span>
  <span class="nf">constructor</span><span class="p">()</span> <span class="p">{</span>
    <span class="nf">alert</span><span class="p">(</span><span class="dl">'</span><span class="s1">Hello!</span><span class="dl">'</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="k">new</span> <span class="nc">Hello</span><span class="p">();</span>
</code></pre></div>
<p>Make sure you&rsquo;re loading <code>hello.es6</code> at <code>app/assets/javascripts/application.js</code>.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="c1">//= require_tree .</span>
<span class="c1">//= require_self</span>
</code></pre></div>
<p>Now if you access the page on your browser, you&rsquo;ll see an <code>alert</code> box like this:</p>

<p><img src="https://s3.amazonaws.com/nandovieira/media/es6-alert-hello.png" alt="Alert box - Hello"></p>

<p>If you want the new <a href="https://babeljs.io/docs/usage/modules/">module system</a>, you still have some things to configure.</p>
<h3 tabindex="-1" id="using-es6-modules">Using <abbr title="ECMAScript 6">ES6</abbr> modules<a class="anchor" href="#using-es6-modules" aria-hidden="true" tabindex="-1"></a>
</h3>
<p><abbr title="ECMAScript 6">ES6</abbr> introduced module support; instead of defining your code in the global scope, you can use a scope per file. Importing modules is pretty straightforward:</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="k">import</span> <span class="nx">Foo</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">foo</span><span class="dl">'</span><span class="p">;</span>
</code></pre></div>
<p>This will load <code>Foo</code> from <code>foo.js</code>. One single file can export several units (functions, objects, anything you want), and you don&rsquo;t have to load everything at once, pretty much like <a href="http://python.org">Python</a>.</p>

<p>Babel has no idea of how these modules should be exported, and by default, will use the <a href="http://www.commonjs.org/specs/modules/1.0/">CommonJS</a> format, which can&rsquo;t be used by the browser, so we&rsquo;ll use another approach.</p>

<p>The easiest way is using <a href="http://requirejs.org/docs/whyamd.html">AMD</a>, and for this we&rsquo;ll use <a href="https://github.com/jrburke/almond">almond</a>. You can install almond with whatever you&rsquo;re using for managing packages; in this article I&rsquo;ll use <a href="http://rails-assets.org">http://rails-assets.org</a>, a bower-to-rubygems converter. Update your <code>Gemfile</code> like the following:</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="n">source</span> <span class="s1">'https://rubygems.org'</span>

<span class="n">gem</span> <span class="s1">'rails'</span><span class="p">,</span> <span class="s1">'4.2.4'</span>
<span class="n">gem</span> <span class="s1">'sqlite3'</span>
<span class="n">gem</span> <span class="s1">'uglifier'</span><span class="p">,</span> <span class="s1">'&gt;= 1.3.0'</span>

<span class="n">gem</span> <span class="s1">'sass-rails'</span><span class="p">,</span> <span class="ss">github: </span><span class="s1">'rails/sass-rails'</span><span class="p">,</span> <span class="ss">branch: </span><span class="s1">'master'</span>
<span class="n">gem</span> <span class="s1">'sprockets-rails'</span><span class="p">,</span> <span class="ss">github: </span><span class="s1">'rails/sprockets-rails'</span><span class="p">,</span> <span class="ss">branch: </span><span class="s1">'master'</span>
<span class="n">gem</span> <span class="s1">'sprockets'</span><span class="p">,</span> <span class="ss">github: </span><span class="s1">'rails/sprockets'</span><span class="p">,</span> <span class="ss">branch: </span><span class="s1">'master'</span>
<span class="n">gem</span> <span class="s1">'babel-transpiler'</span>

<span class="n">gem</span> <span class="s1">'turbolinks'</span>
<span class="n">gem</span> <span class="s1">'jquery-rails'</span>

<span class="n">source</span> <span class="s1">'https://rails-assets.org'</span> <span class="k">do</span>
  <span class="n">gem</span> <span class="s1">'rails-assets-almond'</span>
<span class="k">end</span>
</code></pre></div>
<p>You also have to configure Babel; just create a file at <code>config/initializers/babel.rb</code> with the following code:</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">config</span><span class="p">.</span><span class="nf">assets</span><span class="p">.</span><span class="nf">configure</span> <span class="k">do</span> <span class="o">|</span><span class="n">env</span><span class="o">|</span>
  <span class="n">babel</span> <span class="o">=</span> <span class="no">Sprockets</span><span class="o">::</span><span class="no">BabelProcessor</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span>
    <span class="s1">'modules'</span>    <span class="o">=&gt;</span> <span class="s1">'amd'</span><span class="p">,</span>
    <span class="s1">'moduleIds'</span>  <span class="o">=&gt;</span> <span class="kp">true</span>
  <span class="p">)</span>
  <span class="n">env</span><span class="p">.</span><span class="nf">register_transformer</span> <span class="s1">'application/ecmascript-6'</span><span class="p">,</span> <span class="s1">'application/javascript'</span><span class="p">,</span> <span class="n">babel</span>
<span class="k">end</span>
</code></pre></div>
<p>Finally, update <code>app/assets/javascripts/application.js</code> so it loads almond.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="c1">//= require almond</span>
<span class="c1">//= require jquery</span>
<span class="c1">//= require turbolinks</span>
<span class="c1">//= require_tree .</span>
<span class="c1">//= require_self</span>
</code></pre></div>
<p>Now you have to think about the execution process. Are you going to use some code dispatcher? Or execute code based on the view that is being rendered? Are you going to create your own execution mechanism? The answer depends on your own workflow, so I&rsquo;m not going to give you too many alternatives here.</p>

<p>We&rsquo;re going to create a boot script that uses the controller and action names to execute the JavaScript you need for that specific page. Just add the <code>require</code> call to your <code>app/assets/javascripts/application.js</code> file.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="c1">//= require almond</span>
<span class="c1">//= require jquery</span>
<span class="c1">//= require turbolinks</span>
<span class="c1">//= require_tree .</span>
<span class="c1">//= require_self</span>

<span class="nf">require</span><span class="p">([</span><span class="dl">'</span><span class="s1">application/boot</span><span class="dl">'</span><span class="p">]);</span>
</code></pre></div>
<p>You have to create <code>app/assets/javascripts/application/boot.es6</code>. I&rsquo;ll listen to some events, like DOM&rsquo;s <code>ready</code> and Turbolinks&rsquo; <code>page load</code>.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="k">import</span> <span class="nx">$</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">jquery</span><span class="dl">'</span><span class="p">;</span>

<span class="kd">function</span> <span class="nf">runner</span><span class="p">()</span> <span class="p">{</span>
  <span class="c1">// All scripts must live in app/assets/javascripts/application/pages/**/*.es6.</span>
  <span class="kd">var</span> <span class="nx">path</span> <span class="o">=</span> <span class="nf">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">body</span><span class="dl">'</span><span class="p">).</span><span class="nf">data</span><span class="p">(</span><span class="dl">'</span><span class="s1">route</span><span class="dl">'</span><span class="p">);</span>

  <span class="c1">// Load script for this page.</span>
  <span class="c1">// We should use System.import, but it's not worth the trouble, so</span>
  <span class="c1">// let's use almond's require instead.</span>
  <span class="k">try</span> <span class="p">{</span>
    <span class="nf">require</span><span class="p">([</span><span class="nx">path</span><span class="p">],</span> <span class="nx">onload</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">catch </span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
    <span class="nf">handleError</span><span class="p">(</span><span class="nx">error</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nf">onload</span><span class="p">(</span><span class="nx">Page</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// Instantiate the page, passing &lt;body&gt; as the root element.</span>
  <span class="kd">var</span> <span class="nx">page</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Page</span><span class="p">(</span><span class="nf">$</span><span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">));</span>

  <span class="c1">// Set up page and run scripts for it.</span>
  <span class="k">if </span><span class="p">(</span><span class="nx">page</span><span class="p">.</span><span class="nx">setup</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">page</span><span class="p">.</span><span class="nf">setup</span><span class="p">();</span>
  <span class="p">}</span>

  <span class="nx">page</span><span class="p">.</span><span class="nf">run</span><span class="p">();</span>
<span class="p">}</span>

<span class="c1">// Handles exception.</span>
<span class="kd">function</span> <span class="nf">handleError</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if </span><span class="p">(</span><span class="nx">error</span><span class="p">.</span><span class="nx">message</span><span class="p">.</span><span class="nf">match</span><span class="p">(</span><span class="sr">/undefined missing/</span><span class="p">))</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nf">warn</span><span class="p">(</span><span class="dl">'</span><span class="s1">missing module:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">error</span><span class="p">.</span><span class="nx">message</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="dl">'</span><span class="s1"> </span><span class="dl">'</span><span class="p">).</span><span class="nf">pop</span><span class="p">());</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="k">throw</span> <span class="nx">error</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="nf">$</span><span class="p">(</span><span class="nb">window</span><span class="p">)</span>
  <span class="p">.</span><span class="nf">ready</span><span class="p">(</span><span class="nx">runner</span><span class="p">)</span>
  <span class="p">.</span><span class="nf">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">page:load</span><span class="dl">'</span><span class="p">,</span> <span class="nx">runner</span><span class="p">);</span>
</code></pre></div>
<p>This script needs a <code>data-route</code> property property on your <code>&lt;body&gt;</code> element. You can add something like the following to your layout file (e.g. <code>app/views/layouts/application.html.erb</code>):</p>
<div class="highlight"><pre class="highlight erb"><code><span class="nt">&lt;body</span> <span class="na">data-route=</span><span class="s">"application/pages/</span><span class="cp">&lt;%=</span> <span class="n">controller</span><span class="p">.</span><span class="nf">controller_name</span> <span class="cp">%&gt;</span><span class="s">/</span><span class="cp">&lt;%=</span> <span class="n">controller</span><span class="p">.</span><span class="nf">action_name</span> <span class="cp">%&gt;</span><span class="s">"</span><span class="nt">&gt;</span>
</code></pre></div>
<p>Now let&rsquo;s create a class that will be executed when the template is rendered. We&rsquo;ll use the <code>site</code> controller and <code>home</code> action as example. For this you&rsquo;ll need to create the <code>app/assets/javascripts/application/pages/site/home.es6</code> file.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="k">export</span> <span class="k">default</span> <span class="kd">class</span> <span class="nc">Home</span> <span class="p">{</span>
  <span class="nf">constructor</span><span class="p">(</span><span class="nx">root</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">root</span> <span class="o">=</span> <span class="nx">root</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
    <span class="c1">// add event listeners</span>
    <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">-&gt; setting up listeners and whatnot</span><span class="dl">'</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="nf">run</span><span class="p">()</span> <span class="p">{</span>
    <span class="c1">// trigger initial action (e.g. perform http requests)</span>
    <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">-&gt; perform initial actions</span><span class="dl">'</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>Note that we&rsquo;re exporting the <code>Home</code> class as the default module. This is the module that will be used when you have something like <code>import Home from &#39;application/pages/home&#39;;</code>.</p>

<p>Also note that we&rsquo;re defining the <code>Home#constructor</code> method; this is the method that is executed when the class is instantiated.</p>

<p>I said this before, but class definition is one of the things I like the most. Compare it with the constructor function form:</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="kd">function</span> <span class="nf">Home</span><span class="p">(</span><span class="nx">root</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">this</span><span class="p">.</span><span class="nx">root</span> <span class="o">=</span> <span class="nx">root</span><span class="p">;</span>
<span class="p">}</span>

<span class="nx">Home</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">setup</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  <span class="c1">// add listeners</span>
<span class="p">};</span>

<span class="nx">Home</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">run</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  <span class="c1">// initial execution</span>
<span class="p">};</span>
</code></pre></div>
<p>They&rsquo;re are similar, but using the <code>class</code> keyword makes closer to what we use in other languages. Here&rsquo;s how you would write the same class in Ruby:</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">Home</span>
  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">root</span><span class="p">)</span>
    <span class="vi">@root</span> <span class="o">=</span> <span class="n">root</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">setup</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">run</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div>
<p><abbr title="ECMAScript 6">ES6</abbr> has other niceties. To discover what&rsquo;s new, I recommend the <a href="http://exploringjs.com/">Exploring <abbr title="ECMAScript 6">ES6</abbr></a> book, which <a href="http://exploringjs.com/es6/">you can read for free</a>.</p>
<h3 tabindex="-1" id="almond-js-gotcha">Almond.js gotcha<a class="anchor" href="#almond-js-gotcha" aria-hidden="true" tabindex="-1"></a>
</h3>
<p>There is one gotcha when using Almond; all modules must be explicitly named. This means that libraries like <a href="https://qunitjs.com">qunit</a> won&rsquo;t work out of the box because they&rsquo;re anonymous modules. To solve this problem, you should load the library before loading Almond and then exporting the module yourself.</p>
<div class="highlight"><pre class="highlight javascript"><code><span class="c1">//= require qunit</span>
<span class="c1">//= require almond</span>

<span class="nf">define</span><span class="p">(</span><span class="dl">'</span><span class="s1">qunit</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">QUnit</span><span class="p">;</span>
<span class="p">});</span>
</code></pre></div>
<p>The problem of doing this is that the library won&rsquo;t detect AMD support and will export global variables, but I still prefer this behavior over compiling code with an optimizer like <a href="http://requirejs.org/docs/download.html#rjs">r.js</a> or a library that integrates that into Rails.</p>
<h2 tabindex="-1" id="wrapping-up">Wrapping up<a class="anchor" href="#wrapping-up" aria-hidden="true" tabindex="-1"></a>
</h2>
<p>Using <abbr title="ECMAScript 6">ES6</abbr> today is a viable option. With Babel you can use all these new features without worrying with browser compatibility. The integration with Asset Pipeline make things easier, even for those that don&rsquo;t fully grasp the Node.js ecosystem.</p>

<p>There&rsquo;s a working repository <a href="https://github.com/fnando/using-es6-with-asset-pipeline-on-ruby-on-rails/tree/babel-transpiler">available at Github</a>.</p>

<div class="footnotes">
<hr>
<ol>

<li id="fn1">
<p><abbr title="ECMAScript 6">ES6</abbr> is also known as <em>ES.Next</em> or <em>ES2015</em> (this is the official name, defined after I first published this article).&nbsp;<a href="#fnref1">&#8617;</a></p>
</li>

<li id="fn2">
<p>This project used to be called <em>6to5</em>.js. <a href="http://babeljs.io/blog/2015/02/15/not-born-to-die/">Read more.</a>&nbsp;<a href="#fnref2">&#8617;</a></p>
</li>

<li id="fn3">
<p>The <a href="https://golang.org">Go</a> community does this all the time, so maybe is not a big deal. ¯\<em>(ツ)</em>/¯&nbsp;<a href="#fnref3">&#8617;</a></p>
</li>

</ol>
</div>
]]>
      </content:encoded>
      <link>https://nandovieira.com/using-es6-with-asset-pipeline-on-ruby-on-rails</link>
      <guid>https://nandovieira.com/using-es6-with-asset-pipeline-on-ruby-on-rails</guid>
      <pubDate>Sun, 27 Sep 2015 20:08:00 -0300</pubDate>
    </item>
    <item>
      <title>Don&amp;#39;t tell me what to do</title>
      <description>
        <![CDATA[<p>Do you know what&rsquo;s wrong with articles or solutions saying that you should
<a href="http://solnic.eu/2015/09/18/ditch-your-orm.html">ditch your <abbr title="Object-Relational Mapping">ORM</abbr></a>,
<a href="http://trailblazerb.org">abstract everything from the framework</a>,
<a href="http://confreaks.tv/videos/rubymidwest2011-keynote-architecture-the-lost-years">create six layers of abstraction</a>
and break you app into dozens of microservices? <em>There&rsquo;s no silver bullet</em>.</p>

<p>The problem is that developers that advocate a more complex architecture assume
that everybody has the same deep knowledge about software design. And that&rsquo;s
simply not true. Sure we have to raise the bar and make less experienced
developers improve their game, but you can&rsquo;t just say what they should do. You
don&rsquo;t know the context of the company. You don&rsquo;t know if proving a business
model is more important than having the perfect software design. You don&rsquo;t know
if a self-taught developer can even understand the implications of what you&rsquo;re
advocating.</p>

<p>And without this context, developers may try to follow your advice and create
software that is worst than before, because they just don&rsquo;t know how to correct
apply the patterns you&rsquo;re promoting.</p>

<p>Every single architecture, every single design pattern, every single solution
has pros and cons. Ditching your <abbr title="Object-Relational Mapping">ORM</abbr> may have benefits? Maybe. But it also has
problems. And here lies the problem. When you&rsquo;re advocating something, you&rsquo;re
more likely to hide the bad parts of the solution you&rsquo;re advocating, leaving
this exercise to the reader, that may or may not be able to balance these
implications.</p>

<p>If you&rsquo;re advocating something, be honest. Say how your solution is better than
other solutions, but also be clear about the implications and known problems.</p>

<p><a href="https://blog.dnl.dev/don-t-tell-me-what-to-do/">As a friend once said</a>, don&rsquo;t
tell me what to do, just show me what you did.</p>
]]>
      </description>
      <content:encoded>
        <![CDATA[<p>Do you know what&rsquo;s wrong with articles or solutions saying that you should
<a href="http://solnic.eu/2015/09/18/ditch-your-orm.html">ditch your <abbr title="Object-Relational Mapping">ORM</abbr></a>,
<a href="http://trailblazerb.org">abstract everything from the framework</a>,
<a href="http://confreaks.tv/videos/rubymidwest2011-keynote-architecture-the-lost-years">create six layers of abstraction</a>
and break you app into dozens of microservices? <em>There&rsquo;s no silver bullet</em>.</p>

<p>The problem is that developers that advocate a more complex architecture assume
that everybody has the same deep knowledge about software design. And that&rsquo;s
simply not true. Sure we have to raise the bar and make less experienced
developers improve their game, but you can&rsquo;t just say what they should do. You
don&rsquo;t know the context of the company. You don&rsquo;t know if proving a business
model is more important than having the perfect software design. You don&rsquo;t know
if a self-taught developer can even understand the implications of what you&rsquo;re
advocating.</p>

<p>And without this context, developers may try to follow your advice and create
software that is worst than before, because they just don&rsquo;t know how to correct
apply the patterns you&rsquo;re promoting.</p>

<p>Every single architecture, every single design pattern, every single solution
has pros and cons. Ditching your <abbr title="Object-Relational Mapping">ORM</abbr> may have benefits? Maybe. But it also has
problems. And here lies the problem. When you&rsquo;re advocating something, you&rsquo;re
more likely to hide the bad parts of the solution you&rsquo;re advocating, leaving
this exercise to the reader, that may or may not be able to balance these
implications.</p>

<p>If you&rsquo;re advocating something, be honest. Say how your solution is better than
other solutions, but also be clear about the implications and known problems.</p>

<p><a href="https://blog.dnl.dev/don-t-tell-me-what-to-do/">As a friend once said</a>, don&rsquo;t
tell me what to do, just show me what you did.</p>
]]>
      </content:encoded>
      <link>https://nandovieira.com/dont-tell-me-what-to-do</link>
      <guid>https://nandovieira.com/dont-tell-me-what-to-do</guid>
      <pubDate>Sun, 06 Sep 2015 14:24:00 -0300</pubDate>
    </item>
  </channel>
</rss>
