<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="http://yizeng.me/feed.xml" rel="self" type="application/atom+xml" /><link href="http://yizeng.me/" rel="alternate" type="text/html" hreflang="en" /><updated>2026-04-05T11:03:08+02:00</updated><id>http://yizeng.me/feed.xml</id><title type="html">Yi Zeng’s Blog</title><subtitle>Personal blog for Yi Zeng, a senior software engineer based in Berlin, Germany.</subtitle><author><name>Yi Zeng</name></author><entry><title type="html">Truthy, Falsey, nil?, any?, empty?, blank? and present? in Ruby and Ruby on Rails</title><link href="http://yizeng.me/2021/04/04/truhty-falsey-nil-any-empty-blank-present-in-ruby-and-ruby-on-rails/" rel="alternate" type="text/html" title="Truthy, Falsey, nil?, any?, empty?, blank? and present? in Ruby and Ruby on Rails" /><published>2021-04-04T00:00:00+02:00</published><updated>2021-04-04T00:00:00+02:00</updated><id>http://yizeng.me/2021/04/04/truhty-falsey-nil-any-empty-blank-present-in-ruby-and-ruby-on-rails</id><content type="html" xml:base="http://yizeng.me/2021/04/04/truhty-falsey-nil-any-empty-blank-present-in-ruby-and-ruby-on-rails/"><![CDATA[<p>Here's a quick comparison of Ruby's truthy, falsey, <code class="language-plaintext highlighter-rouge">nil?</code>, <code class="language-plaintext highlighter-rouge">any?</code>, <code class="language-plaintext highlighter-rouge">empty?</code>
against Ruby on Rails' <code class="language-plaintext highlighter-rouge">blank?</code> and <code class="language-plaintext highlighter-rouge">present?</code>.</p>

<ul class="toc" id="markdown-toc">
  <li><a href="#heading-tldr" id="markdown-toc-heading-tldr">TL;DR</a>    <ul>
      <li><a href="#heading-ruby" id="markdown-toc-heading-ruby">Ruby</a></li>
      <li><a href="#heading-rails" id="markdown-toc-heading-rails">Rails</a></li>
      <li><a href="#heading-summary" id="markdown-toc-heading-summary">Summary</a></li>
    </ul>
  </li>
  <li><a href="#heading-difference-between-any-and-empty" id="markdown-toc-heading-difference-between-any-and-empty">Difference between any? and empty?</a></li>
  <li><a href="#heading-difference-between-empty-and-blank" id="markdown-toc-heading-difference-between-empty-and-blank">Difference between empty? and blank?</a></li>
  <li><a href="#heading-difference-between-blank-and-present" id="markdown-toc-heading-difference-between-blank-and-present">Difference between blank? and present?</a></li>
  <li><a href="#heading-the-presence-method" id="markdown-toc-heading-the-presence-method">The presence method</a></li>
  <li><a href="#heading-bonus-logical-operators" id="markdown-toc-heading-bonus-logical-operators">[Bonus] Logical Operators</a></li>
  <li><a href="#heading-sample-code" id="markdown-toc-heading-sample-code">Sample Code</a>    <ul>
      <li><a href="#heading-example" id="markdown-toc-heading-example">Example</a></li>
      <li><a href="#heading-results" id="markdown-toc-heading-results">Results</a></li>
    </ul>
  </li>
</ul>

<h2 id="heading-tldr">TL;DR</h2>

<h3 id="heading-ruby">Ruby</h3>

<table>
  <tbody>
    <tr>
      <td>Truthy</td>
      <td>Used to evaluate <code class="language-plaintext highlighter-rouge">if</code>/<code class="language-plaintext highlighter-rouge">unless</code> conditions. Everything is truthy except for <code class="language-plaintext highlighter-rouge">nil</code> and <code class="language-plaintext highlighter-rouge">false</code>.</td>
    </tr>
    <tr>
      <td>Falsey</td>
      <td>The negation of truthy. Only <code class="language-plaintext highlighter-rouge">nil</code> and <code class="language-plaintext highlighter-rouge">false</code> are falsey in Ruby.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">nil?</code></td>
      <td>Defined on Ruby's <a href="https://ruby-doc.org/core-3.0.0/Object.html#method-i-nil-3F" target="_blank">Object</a> class.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">empty?</code></td>
      <td>Defined only for certain objects of <a href="https://ruby-doc.org/core-3.0.0/Array.html#method-i-empty-3F" target="_blank">Array</a>, <a href="https://ruby-doc.org/core-3.0.0/Hash.html#method-i-empty-3F" target="_blank">Hash</a>, <a href="https://ruby-doc.org/core-3.0.0/String.html#method-i-empty-3F" target="_blank">String</a> etc. It throws <code class="language-plaintext highlighter-rouge">NoMethodError</code> for nil, TrueClass, FalseClass, Integer etc.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">any?</code></td>
      <td>Defined in the mixin module <a href="https://ruby-doc.org/core-3.0.0/Enumerable.html#method-i-any-3F" target="_blank">Enumerable</a>, which is included in classes like Array and Hash.</td>
    </tr>
  </tbody>
</table>

<h3 id="heading-rails">Rails</h3>

<table>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">blank?</code>, <code class="language-plaintext highlighter-rouge">present?</code></td>
      <td>Defined in Ruby on Rails' <a href="https://api.rubyonrails.org/classes/Object.html#method-i-blank-3F" target="_blank">ActiveSupport</a>, which can also be installed independently without Rails.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">blank?</code></td>
      <td><code class="language-plaintext highlighter-rouge">nil</code>, <code class="language-plaintext highlighter-rouge">false</code>, empty/whitespace strings, empty arrays, hashes and any other objects with <code class="language-plaintext highlighter-rouge">object.empty?</code> is true.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">present?</code></td>
      <td>The negation of <code class="language-plaintext highlighter-rouge">blank?</code>, i.e. <code class="language-plaintext highlighter-rouge">!blank?</code>.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">0</code></td>
      <td>is not blank (i.e. <code class="language-plaintext highlighter-rouge">0.present? # =&gt; true</code>).</td>
    </tr>
  </tbody>
</table>

<h3 id="heading-summary">Summary</h3>

<table>
  <thead>
    <tr>
      <th>Methods</th>
      <th>Values</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Truthy</td>
      <td>true, "", "\n", "Hello", 0, 1.25, [], [nil, false], [1, 2], {}, {:colour=&gt;nil}</td>
    </tr>
    <tr>
      <td>Falsey</td>
      <td>nil, false</td>
    </tr>
    <tr>
      <td>nil?</td>
      <td>nil</td>
    </tr>
    <tr>
      <td>any?</td>
      <td>[1, 2], {:colour=&gt;nil}</td>
    </tr>
    <tr>
      <td>empty?</td>
      <td>"", [], {}</td>
    </tr>
    <tr>
      <td>blank?</td>
      <td>nil, false, "", "\n", [], {}</td>
    </tr>
    <tr>
      <td>present?</td>
      <td>true, "Hello", 0, 1.25, [nil, false], [1, 2], {:colour=&gt;nil}</td>
    </tr>
  </tbody>
</table>

<table>
  <thead>
    <tr>
      <th> </th>
      <th>truthy</th>
      <th>falsey</th>
      <th>nil?</th>
      <th>any?</th>
      <th>empty?</th>
      <th>blank?</th>
      <th>present?</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>nil</td>
      <td> </td>
      <td>✓</td>
      <td>✓</td>
      <td>NoMethodError</td>
      <td>NoMethodError</td>
      <td>✓</td>
      <td> </td>
    </tr>
    <tr>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>false</td>
      <td> </td>
      <td>✓</td>
      <td> </td>
      <td>NoMethodError</td>
      <td>NoMethodError</td>
      <td>✓</td>
      <td> </td>
    </tr>
    <tr>
      <td>true</td>
      <td>✓</td>
      <td> </td>
      <td> </td>
      <td>NoMethodError</td>
      <td>NoMethodError</td>
      <td> </td>
      <td>✓</td>
    </tr>
    <tr>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>""</td>
      <td>✓</td>
      <td> </td>
      <td> </td>
      <td>NoMethodError</td>
      <td>✓</td>
      <td>✓</td>
      <td> </td>
    </tr>
    <tr>
      <td>"\n"</td>
      <td>✓</td>
      <td> </td>
      <td> </td>
      <td>NoMethodError</td>
      <td> </td>
      <td>✓</td>
      <td> </td>
    </tr>
    <tr>
      <td>"Hello"</td>
      <td>✓</td>
      <td> </td>
      <td> </td>
      <td>NoMethodError</td>
      <td> </td>
      <td> </td>
      <td>✓</td>
    </tr>
    <tr>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>0</td>
      <td>✓</td>
      <td> </td>
      <td> </td>
      <td>NoMethodError</td>
      <td>NoMethodError</td>
      <td> </td>
      <td>✓</td>
    </tr>
    <tr>
      <td>1.25</td>
      <td>✓</td>
      <td> </td>
      <td> </td>
      <td>NoMethodError</td>
      <td>NoMethodError</td>
      <td> </td>
      <td>✓</td>
    </tr>
    <tr>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>[]</td>
      <td>✓</td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td>✓</td>
      <td>✓</td>
      <td> </td>
    </tr>
    <tr>
      <td>[nil, false]</td>
      <td>✓</td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td>✓</td>
    </tr>
    <tr>
      <td>[1, 2]</td>
      <td>✓</td>
      <td> </td>
      <td> </td>
      <td>✓</td>
      <td> </td>
      <td> </td>
      <td>✓</td>
    </tr>
    <tr>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>{}</td>
      <td>✓</td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td>✓</td>
      <td>✓</td>
      <td> </td>
    </tr>
    <tr>
      <td>{ colour: nil }</td>
      <td>✓</td>
      <td> </td>
      <td> </td>
      <td>✓</td>
      <td> </td>
      <td> </td>
      <td>✓</td>
    </tr>
    <tr>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>Object responds to empty? and evaluate to true</td>
      <td>✓</td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td>✓</td>
      <td>✓</td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h2 id="heading-difference-between-any-and-empty">Difference between any? and empty?</h2>

<p><code class="language-plaintext highlighter-rouge">any?</code> is defined in the mixin module <a href="https://ruby-doc.org/core-3.0.0/Enumerable.html#method-i-any-3F" target="_blank">Enumerable</a>
to check if the enumberable contains the values evaluated from a given block.
When no block is given,
it returns <strong>true</strong> if the object contains a value other than false or nil.</p>

<p>Since <a href="https://ruby-doc.org/core-3.0.0/Array.html#method-i-empty-3F" target="_blank">Array</a>, <a href="https://ruby-doc.org/core-3.0.0/Hash.html#method-i-empty-3F" target="_blank">Hash</a> both include the Enumerable module,
both <code class="language-plaintext highlighter-rouge">any?</code> and <code class="language-plaintext highlighter-rouge">empty?</code> methods become available.</p>

<ul>
  <li>For Hashes, <code class="language-plaintext highlighter-rouge">any?</code> is an antonym of <code class="language-plaintext highlighter-rouge">empty?</code>, i.e. <code class="language-plaintext highlighter-rouge">{}.any?</code> is false and <code class="language-plaintext highlighter-rouge">{}.empty?</code> is true.</li>
  <li>For Arrays, <code class="language-plaintext highlighter-rouge">any?</code> is almost the antonym of <code class="language-plaintext highlighter-rouge">empty?</code>, except for <code class="language-plaintext highlighter-rouge">nil</code> and <code class="language-plaintext highlighter-rouge">false</code>, that <strong><code class="language-plaintext highlighter-rouge">[nil, false].any?</code> is false, while <code class="language-plaintext highlighter-rouge">[nil, false].empty?</code> is also false</strong>.</li>
</ul>

<h2 id="heading-difference-between-empty-and-blank">Difference between empty? and blank?</h2>

<p><code class="language-plaintext highlighter-rouge">empty?</code> is simply a Ruby method defined on certain classes,
e.g. <a href="https://ruby-doc.org/core-3.0.0/Array.html#method-i-empty-3F" target="_blank">Array</a>, <a href="https://ruby-doc.org/core-3.0.0/Hash.html#method-i-empty-3F" target="_blank">Hash</a>, <a href="https://ruby-doc.org/core-3.0.0/String.html#method-i-empty-3F" target="_blank">String</a>,
while <code class="language-plaintext highlighter-rouge">blank?</code>/<code class="language-plaintext highlighter-rouge">present?</code> are Ruby on Rails methods defined in
<a href="https://api.rubyonrails.org/classes/Object.html#method-i-blank-3F" target="_blank">ActiveSupport</a>, which can also be installed independently with Rails.</p>

<p>By definition, <code class="language-plaintext highlighter-rouge">blank?</code> includes:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">nil</code></li>
  <li><code class="language-plaintext highlighter-rouge">false</code></li>
  <li>empty/whitespace strings</li>
  <li>empty arrays, hashes</li>
  <li>any other objects with <code class="language-plaintext highlighter-rouge">object.empty?</code> is true</li>
</ul>

<h2 id="heading-difference-between-blank-and-present">Difference between blank? and present?</h2>

<p><a href="https://api.rubyonrails.org/classes/Object.html#method-i-present-3F" target="_blank"><code class="language-plaintext highlighter-rouge">present?</code></a> is just a negation of <code class="language-plaintext highlighter-rouge">blank?</code>.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># File activesupport/lib/active_support/core_ext/object/blank.rb, line 25</span>
<span class="k">def</span> <span class="nf">present?</span>
  <span class="o">!</span><span class="n">blank?</span>
<span class="k">end</span>
</code></pre></div></div>

<h2 id="heading-the-presence-method">The presence method</h2>

<p>Defined together with <a href="https://api.rubyonrails.org/classes/Object.html#method-i-present-3F" target="_blank"><code class="language-plaintext highlighter-rouge">present?</code></a>,
<code class="language-plaintext highlighter-rouge">presence</code> method returns the receiver if it's present otherwise returns nil.</p>

<p><code class="language-plaintext highlighter-rouge">object.presence</code> is equivalent to <code class="language-plaintext highlighter-rouge">object.present? ? object : nil</code>.</p>

<p>For example, something like</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>def display_name
  return user.name if user.name.present?

  "Demo User"
end
</code></pre></div></div>

<p>becomes</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>def display_name
  user.name.presence || "Demo User"
end
</code></pre></div></div>

<h2 id="heading-bonus-logical-operators">[Bonus] Logical Operators</h2>

<table>
  <thead>
    <tr>
      <th>Operator</th>
      <th>Name</th>
      <th>Description</th>
      <th>Example</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>!</td>
      <td>Logical Not</td>
      <td>Negates the truthy or falsey value of an object</td>
      <td><code class="language-plaintext highlighter-rouge">!user.active?</code></td>
    </tr>
    <tr>
      <td>!!</td>
      <td>Logical Truthy</td>
      <td>Returns the truthy or falsey of an object</td>
      <td><code class="language-plaintext highlighter-rouge">!![]</code></td>
    </tr>
    <tr>
      <td>&amp;&amp;</td>
      <td>Logical And</td>
      <td>Returns true if both statements are truthy</td>
      <td><code class="language-plaintext highlighter-rouge">user.name.present? &amp;&amp; user.active?</code></td>
    </tr>
    <tr>
      <td>||</td>
      <td>Logicall Or</td>
      <td>Returns true if one of the statements is truthy</td>
      <td><code class="language-plaintext highlighter-rouge">user.name.blank? || user.discarded?</code></td>
    </tr>
  </tbody>
</table>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kp">nil</span> <span class="o">&amp;&amp;</span> <span class="kp">true</span> <span class="c1"># =&gt; nil</span>
<span class="kp">true</span> <span class="o">&amp;&amp;</span> <span class="kp">nil</span> <span class="c1"># =&gt; nil</span>
<span class="kp">nil</span> <span class="o">||</span> <span class="s2">""</span> <span class="c1"># =&gt; ""</span>
<span class="kp">nil</span> <span class="o">||</span> <span class="kp">true</span> <span class="c1"># =&gt; true</span>
<span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="mf">1.25</span> <span class="c1"># =&gt; 1.25</span>
<span class="mi">0</span> <span class="o">||</span> <span class="mf">1.25</span> <span class="c1"># =&gt; 0</span>
</code></pre></div></div>

<h2 id="heading-sample-code">Sample Code</h2>

<h3 id="heading-example">Example</h3>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">require</span> <span class="s2">"active_support"</span>
<span class="nb">require</span> <span class="s2">"active_support/core_ext/object/blank"</span>

<span class="no">VALUES</span> <span class="o">=</span> <span class="p">[</span>
  <span class="kp">nil</span><span class="p">,</span> <span class="kp">false</span><span class="p">,</span> <span class="kp">true</span><span class="p">,</span> <span class="s2">""</span><span class="p">,</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">,</span> <span class="s2">"Hello"</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mf">1.25</span><span class="p">,</span> <span class="p">[],</span> <span class="p">[</span><span class="kp">nil</span><span class="p">,</span> <span class="kp">false</span><span class="p">],</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">],</span> <span class="p">{},</span> <span class="p">{</span> <span class="ss">colour: </span><span class="kp">nil</span> <span class="p">}</span>
<span class="p">]</span>

<span class="nb">puts</span> <span class="sb">`ruby -v`</span>
<span class="nb">puts</span> <span class="sb">`rails -v`</span>
<span class="nb">puts</span>

<span class="nb">puts</span> <span class="s2">"All Values: </span><span class="si">#{</span><span class="no">VALUES</span><span class="si">}</span><span class="s2">"</span>
<span class="nb">puts</span>

<span class="nb">puts</span> <span class="s2">"Truthy: </span><span class="si">#{</span><span class="no">VALUES</span><span class="p">.</span><span class="nf">select</span> <span class="p">{</span> <span class="o">|</span><span class="n">v</span><span class="o">|</span> <span class="o">!!</span><span class="n">v</span> <span class="o">==</span> <span class="kp">true</span> <span class="si">}</span><span class="s2"> }"</span>
<span class="nb">puts</span> <span class="s2">"Falsey: </span><span class="si">#{</span><span class="no">VALUES</span><span class="p">.</span><span class="nf">select</span> <span class="p">{</span> <span class="o">|</span><span class="n">v</span><span class="o">|</span> <span class="o">!!</span><span class="n">v</span> <span class="o">==</span> <span class="kp">false</span> <span class="si">}</span><span class="s2"> }"</span>
<span class="nb">puts</span> <span class="s2">"nil?: </span><span class="si">#{</span><span class="no">VALUES</span><span class="p">.</span><span class="nf">select</span> <span class="p">{</span> <span class="o">|</span><span class="n">v</span><span class="o">|</span> <span class="n">v</span><span class="p">.</span><span class="nf">nil?</span> <span class="o">==</span> <span class="kp">true</span> <span class="si">}</span><span class="s2"> }"</span>
<span class="nb">puts</span> <span class="s2">"any?: </span><span class="si">#{</span><span class="no">VALUES</span><span class="p">.</span><span class="nf">select</span> <span class="p">{</span> <span class="o">|</span><span class="n">v</span><span class="o">|</span> <span class="n">v</span><span class="p">.</span><span class="nf">respond_to?</span><span class="p">(</span><span class="ss">:any?</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="n">v</span><span class="p">.</span><span class="nf">any?</span> <span class="o">==</span> <span class="kp">true</span> <span class="si">}</span><span class="s2"> }"</span>
<span class="nb">puts</span> <span class="s2">"empty?: </span><span class="si">#{</span><span class="no">VALUES</span><span class="p">.</span><span class="nf">select</span> <span class="p">{</span> <span class="o">|</span><span class="n">v</span><span class="o">|</span> <span class="n">v</span><span class="p">.</span><span class="nf">respond_to?</span><span class="p">(</span><span class="ss">:empty?</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="n">v</span><span class="p">.</span><span class="nf">empty?</span> <span class="o">==</span> <span class="kp">true</span> <span class="si">}</span><span class="s2"> }"</span>

<span class="nb">puts</span>

<span class="nb">puts</span> <span class="s2">"blank?: </span><span class="si">#{</span><span class="no">VALUES</span><span class="p">.</span><span class="nf">select</span> <span class="p">{</span> <span class="o">|</span><span class="n">v</span><span class="o">|</span> <span class="n">v</span><span class="p">.</span><span class="nf">blank?</span> <span class="o">==</span> <span class="kp">true</span> <span class="si">}</span><span class="s2"> }"</span>
<span class="nb">puts</span> <span class="s2">"present?: </span><span class="si">#{</span><span class="no">VALUES</span><span class="p">.</span><span class="nf">select</span> <span class="p">{</span> <span class="o">|</span><span class="n">v</span><span class="o">|</span> <span class="n">v</span><span class="p">.</span><span class="nf">present?</span> <span class="o">==</span> <span class="kp">true</span> <span class="si">}</span><span class="s2"> }"</span>
</code></pre></div></div>

<h3 id="heading-results">Results</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-darwin18]
Rails 6.0.3.5

All Values: nil, false, true, "", "\n", "Hello", 0, 1.25, [], [nil, false], [1, 2], {}, {:colour=&gt;nil}

Truthy: true, "", "\n", "Hello", 0, 1.25, [], [nil, false], [1, 2], {}, {:colour=&gt;nil}
Falsey: nil, false
nil?: nil
any?: [1, 2], {:colour=&gt;nil}
empty?: "", [], {}

blank?: nil, false, "", "\n", [], {}
present?: true, "Hello", 0, 1.25, [nil, false], [1, 2], {:colour=&gt;nil}
</code></pre></div></div>]]></content><author><name>Yi Zeng</name></author><category term="articles" /><category term="ruby" /><category term="ruby on rails" /><summary type="html"><![CDATA[What's truthy, falsey, nil?, empty?, blank? and present? in Ruby and Ruby on Rails.]]></summary></entry><entry><title type="html">Benchmark converting string keys of a hash to symbols</title><link href="http://yizeng.me/2020/02/04/benchmark-converting-string-keys-of-a-hash-to-symbols/" rel="alternate" type="text/html" title="Benchmark converting string keys of a hash to symbols" /><published>2020-02-04T00:00:00+01:00</published><updated>2020-02-04T00:00:00+01:00</updated><id>http://yizeng.me/2020/02/04/benchmark-converting-string-keys-of-a-hash-to-symbols</id><content type="html" xml:base="http://yizeng.me/2020/02/04/benchmark-converting-string-keys-of-a-hash-to-symbols/"><![CDATA[<p>There are several ways of converting string keys to symbols in hash.
But ever wondered how fast they are and what's the difference?</p>

<h2 id="heading-tldr">TL;DR</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>                                user     system       total           real
transform_keys:             6.994507    0.014749    7.009256  (  7.022791)
using_each_with_object:    11.424220    0.041001   11.465221  ( 11.511807)
symbolize_keys:             7.628721    0.018207    7.646928  (  7.665882)

with_indifferent_access:  107.970348    0.446824  108.417172  (109.052287)
deep_symbolize_keys:       95.842033    0.551120   96.393153  ( 97.145661)
</code></pre></div></div>

<h2 id="heading-shallow-symbolizing">Shallow Symbolizing</h2>

<p>Shallow symbolizing means it will only convert the string keys of a hash
to symbols without converting the keys in the nested hashes.</p>

<h3 id="heading-ruby--25">Ruby &lt; 2.5</h3>

<p>Before Ruby 2.5, there are no handy methods in Ruby to convert string hash keys to symbols.
But the enumberable method <code class="language-plaintext highlighter-rouge">each_with_object</code> can be used to achieve it.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="s2">"dimensions"</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="s2">"height"</span> <span class="o">=&gt;</span> <span class="mi">10</span> <span class="p">}</span> <span class="p">}.</span><span class="nf">each_with_object</span><span class="p">({})</span> <span class="p">{</span> <span class="o">|</span><span class="p">(</span><span class="n">k</span><span class="p">,</span><span class="n">v</span><span class="p">),</span> <span class="n">h</span><span class="o">|</span> <span class="n">h</span><span class="p">[</span><span class="n">k</span><span class="p">.</span><span class="nf">to_sym</span><span class="p">]</span> <span class="o">=</span> <span class="n">v</span> <span class="p">}</span>
</code></pre></div></div>

<h3 id="heading-ruby--25-1">Ruby &gt;= 2.5</h3>

<p>Since Ruby 2.5, a new method <code class="language-plaintext highlighter-rouge">transform_keys</code> has been added to Hash class,
which can be used to convert string keys to symbols.</p>

<p><a href="https://ruby-doc.org/core-2.5.0/Hash.html#method-i-transform_keys" target="_blank">https://ruby-doc.org/core-2.5.0/Hash.html#method-i-transform_keys</a></p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="s2">"dimensions"</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="s2">"height"</span> <span class="o">=&gt;</span> <span class="mi">10</span> <span class="p">}</span> <span class="p">}.</span><span class="nf">transform_keys</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:to_sym</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="heading-ruby-on-rails">Ruby on Rails</h3>

<p>Ruby on Rails' active support provides <code class="language-plaintext highlighter-rouge">symbolize_keys</code>
on Hash class to symbolize keys.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="s2">"dimensions"</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="s2">"height"</span> <span class="o">=&gt;</span> <span class="mi">10</span> <span class="p">}</span> <span class="p">}.</span><span class="nf">symbolize_keys</span>
</code></pre></div></div>

<h2 id="heading-deep-symbolizing">Deep Symbolizing</h2>

<p>Ruby on Rails' active_support provides two ways of achieving this.</p>

<h3 id="heading-with_indifferent_access">with_indifferent_access</h3>

<p><a href="https://api.rubyonrails.org/classes/ActiveSupport/HashWithIndifferentAccess.html" target="_blank">https://api.rubyonrails.org/classes/ActiveSupport/HashWithIndifferentAccess.html</a></p>

<p>It converts the hash keys (nested) so that strings and hashes are considered same.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">hash</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"dimensions"</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="s2">"height"</span> <span class="o">=&gt;</span> <span class="mi">10</span> <span class="p">}</span> <span class="p">}.</span><span class="nf">with_indifferent_access</span>
<span class="c1"># hash.dig(:dimensions, :height) # =&gt; 10</span>
<span class="c1"># hash.dig('dimensions', 'height') # =&gt; 10</span>
</code></pre></div></div>

<h3 id="heading-deep_symbolize_keys">deep_symbolize_keys</h3>

<p><a href="https://apidock.com/rails/v6.0.0/Hash/deep_symbolize_keys" target="_blank">https://apidock.com/rails/v6.0.0/Hash/deep_symbolize_keys</a></p>

<p>It is similar to <code class="language-plaintext highlighter-rouge">symbolize_keys</code> method,
but it includes the keys from the root hash and from all nested hashes and arrays.</p>

<p>Unlike <code class="language-plaintext highlighter-rouge">with_indifferent_access</code>, once converted,
the keys in the new hash can no longer be retrieved via original string format.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">hash</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"dimensions"</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="s2">"height"</span> <span class="o">=&gt;</span> <span class="mi">10</span> <span class="p">}</span> <span class="p">}.</span><span class="nf">deep_symbolize_keys</span>
<span class="c1"># hash.dig(:dimensions, :height) # =&gt; 10</span>
<span class="c1"># hash.dig('dimensions', 'height') # =&gt; nil</span>
</code></pre></div></div>

<h2 id="heading-benchmarking">Benchmarking</h2>

<h3 id="heading-code">Code</h3>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">require</span> <span class="s1">'benchmark'</span>
<span class="nb">require</span> <span class="s1">'active_support/all'</span>

<span class="no">TIMES</span> <span class="o">=</span> <span class="mi">50_000_000</span>
<span class="no">HASH</span> <span class="o">=</span> <span class="p">{</span>
  <span class="s2">"colour"</span> <span class="o">=&gt;</span> <span class="s2">"red"</span><span class="p">,</span>
  <span class="s2">"sizes"</span> <span class="o">=&gt;</span> <span class="p">[</span>
    <span class="s2">"measurements_1"</span> <span class="o">=&gt;</span> <span class="p">{</span>
      <span class="s2">"height"</span> <span class="o">=&gt;</span> <span class="mi">1</span><span class="p">,</span>
      <span class="s2">"length"</span> <span class="o">=&gt;</span> <span class="mi">2</span><span class="p">,</span>
      <span class="s2">"depth"</span> <span class="o">=&gt;</span> <span class="mi">3</span><span class="p">,</span>
    <span class="p">},</span>
    <span class="s2">"measurements_2"</span> <span class="o">=&gt;</span> <span class="p">{</span>
      <span class="s2">"height"</span> <span class="o">=&gt;</span> <span class="mi">10</span><span class="p">,</span>
      <span class="s2">"length"</span> <span class="o">=&gt;</span> <span class="mi">20</span><span class="p">,</span>
      <span class="s2">"depth"</span> <span class="o">=&gt;</span> <span class="mi">30</span><span class="p">,</span>
    <span class="p">}</span>
  <span class="p">]</span>
<span class="p">}</span>

<span class="no">Benchmark</span><span class="p">.</span><span class="nf">bm</span> <span class="k">do</span> <span class="o">|</span><span class="n">x</span><span class="o">|</span>
  <span class="n">x</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s2">"transform_keys:"</span><span class="p">)</span> <span class="p">{</span> <span class="no">TIMES</span><span class="p">.</span><span class="nf">times</span> <span class="p">{</span> <span class="no">HASH</span><span class="p">.</span><span class="nf">transform_keys</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:to_sym</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span>
  <span class="n">x</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s2">"using_each_with_object:"</span><span class="p">)</span> <span class="p">{</span> <span class="no">TIMES</span><span class="p">.</span><span class="nf">times</span> <span class="p">{</span> <span class="no">HASH</span><span class="p">.</span><span class="nf">each_with_object</span><span class="p">({})</span> <span class="p">{</span> <span class="o">|</span><span class="p">(</span><span class="n">k</span><span class="p">,</span><span class="n">v</span><span class="p">),</span> <span class="n">h</span><span class="o">|</span> <span class="n">h</span><span class="p">[</span><span class="n">k</span><span class="p">.</span><span class="nf">to_sym</span><span class="p">]</span> <span class="o">=</span> <span class="n">v</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span>
  <span class="n">x</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s2">"symbolize_keys:"</span><span class="p">)</span> <span class="p">{</span> <span class="no">TIMES</span><span class="p">.</span><span class="nf">times</span> <span class="p">{</span> <span class="no">HASH</span><span class="p">.</span><span class="nf">symbolize_keys</span> <span class="p">}</span> <span class="p">}</span>
  <span class="n">x</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s2">"with_indifferent_access:"</span><span class="p">)</span> <span class="p">{</span> <span class="no">TIMES</span><span class="p">.</span><span class="nf">times</span> <span class="p">{</span> <span class="no">HASH</span><span class="p">.</span><span class="nf">with_indifferent_access</span> <span class="p">}</span> <span class="p">}</span>
  <span class="n">x</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s2">"deep_symbolize_keys:"</span><span class="p">)</span> <span class="p">{</span> <span class="no">TIMES</span><span class="p">.</span><span class="nf">times</span> <span class="p">{</span> <span class="no">HASH</span><span class="p">.</span><span class="nf">deep_symbolize_keys</span> <span class="p">}</span> <span class="p">}</span>
<span class="k">end</span>
</code></pre></div></div>

<h3 id="heading-results">Results</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>                                user     system       total           real
transform_keys:             6.994507    0.014749    7.009256  (  7.022791)
using_each_with_object:    11.424220    0.041001   11.465221  ( 11.511807)
symbolize_keys:             7.628721    0.018207    7.646928  (  7.665882)

with_indifferent_access:  107.970348    0.446824  108.417172  (109.052287)
deep_symbolize_keys:       95.842033    0.551120   96.393153  ( 97.145661)
</code></pre></div></div>]]></content><author><name>Yi Zeng</name></author><category term="articles" /><category term="benchmarking" /><category term="ruby" /><category term="ruby on rails" /><summary type="html"><![CDATA[Benchmark the performance of various ways of converting string keys of a (nested) hash to symbols using transform_keys, with_indifferent_access, symbolize_keys and more.]]></summary></entry><entry><title type="html">Reduce GraphQL Ruby mutation arguments in resolve method</title><link href="http://yizeng.me/2020/01/05/reduce-graphql-ruby-mutation-arguments-in-resolve-method/" rel="alternate" type="text/html" title="Reduce GraphQL Ruby mutation arguments in resolve method" /><published>2020-01-05T00:00:00+01:00</published><updated>2020-01-05T00:00:00+01:00</updated><id>http://yizeng.me/2020/01/05/reduce-graphql-ruby-mutation-arguments-in-resolve-method</id><content type="html" xml:base="http://yizeng.me/2020/01/05/reduce-graphql-ruby-mutation-arguments-in-resolve-method/"><![CDATA[<p><a href="https://graphql-ruby.org/" target="_blank">GraphQL Ruby gem</a> helps to add GraphQL support to Ruby or Ruby on Rails applications.</p>

<p>The <a href="https://graphql-ruby.org/guides" target="_blank">official guides</a>
or <a href="https://www.howtographql.com/" target="_blank">How to GraphQL</a> website would be a good starting point.</p>

<h2 id="heading-the-basics">The Basics</h2>

<p>Here is a basic sign up mutation that handles the sign up process:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Mutations::SignUp</span> <span class="o">&lt;</span> <span class="no">Mutations</span><span class="o">::</span><span class="no">BaseMutation</span>
  <span class="n">null</span> <span class="kp">true</span>

  <span class="n">argument</span> <span class="ss">:email</span><span class="p">,</span> <span class="no">String</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>
  <span class="n">argument</span> <span class="ss">:password</span><span class="p">,</span> <span class="no">String</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>

  <span class="n">field</span> <span class="ss">:token</span><span class="p">,</span> <span class="no">String</span><span class="p">,</span> <span class="ss">null: </span><span class="kp">true</span>
  <span class="n">field</span> <span class="ss">:user</span><span class="p">,</span> <span class="no">Types</span><span class="o">::</span><span class="no">UserType</span><span class="p">,</span> <span class="ss">null: </span><span class="kp">true</span>

  <span class="k">def</span> <span class="nf">resolve</span><span class="p">(</span><span class="n">email</span><span class="p">:,</span> <span class="n">password</span><span class="p">:)</span>
    <span class="c1"># create your user with passed in email and password.</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h2 id="heading-the-problem">The Problem</h2>

<p>But what if it's a third party client calling this mutation endpoint with a lot of parameters?</p>

<p>Here is an example (the parameters are illustrative only, which may not make total sense).</p>

<p>Mutation's <code class="language-plaintext highlighter-rouge">resolve</code> function would now have 10+ parameters.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Mutations::SignUp</span> <span class="o">&lt;</span> <span class="no">Mutations</span><span class="o">::</span><span class="no">BaseMutation</span>
  <span class="n">null</span> <span class="kp">true</span>

  <span class="n">argument</span> <span class="ss">:email</span><span class="p">,</span> <span class="no">String</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>
  <span class="n">argument</span> <span class="ss">:password</span><span class="p">,</span> <span class="no">String</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>
  <span class="n">argument</span> <span class="ss">:expires_at</span><span class="p">,</span> <span class="no">GraphQL</span><span class="o">::</span><span class="no">Types</span><span class="o">::</span><span class="no">BigInt</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>
  <span class="n">argument</span> <span class="ss">:expires_in</span><span class="p">,</span> <span class="no">GraphQL</span><span class="o">::</span><span class="no">Types</span><span class="o">::</span><span class="no">BigInt</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>
  <span class="n">argument</span> <span class="ss">:resource_state</span><span class="p">,</span> <span class="no">Int</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>
  <span class="n">argument</span> <span class="ss">:username</span><span class="p">,</span> <span class="no">String</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">false</span>
  <span class="n">argument</span> <span class="ss">:firstname</span><span class="p">,</span> <span class="no">String</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>
  <span class="n">argument</span> <span class="ss">:lastname</span><span class="p">,</span> <span class="no">String</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>
  <span class="n">argument</span> <span class="ss">:sex</span><span class="p">,</span> <span class="no">String</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">false</span>
  <span class="n">argument</span> <span class="ss">:active_till</span><span class="p">,</span> <span class="no">GraphQL</span><span class="o">::</span><span class="no">Types</span><span class="o">::</span><span class="no">ISO8601DateTime</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>

  <span class="n">field</span> <span class="ss">:token</span><span class="p">,</span> <span class="no">String</span><span class="p">,</span> <span class="ss">null: </span><span class="kp">true</span>
  <span class="n">field</span> <span class="ss">:user</span><span class="p">,</span> <span class="no">Types</span><span class="o">::</span><span class="no">UserType</span><span class="p">,</span> <span class="ss">null: </span><span class="kp">true</span>

  <span class="k">def</span> <span class="nf">resolve</span><span class="p">(</span><span class="n">email</span><span class="p">:,</span> <span class="n">password</span><span class="p">:,</span> <span class="n">expires_at</span><span class="p">:,</span> <span class="n">expires_in</span><span class="p">:,</span> <span class="n">resource_state</span><span class="p">:,</span>
              <span class="n">username</span><span class="p">:,</span> <span class="n">firstname</span><span class="p">:,</span> <span class="n">lastname</span><span class="p">:,</span> <span class="n">lastname</span><span class="p">:,</span> <span class="n">sex</span><span class="p">:,</span> <span class="n">active_till</span><span class="p">:)</span>
    <span class="c1"># create your user with data passed in.</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h2 id="heading-the-solutions">The Solutions</h2>

<h3 id="heading-use-double-splat-operator-">Use double splat operator (**)</h3>

<p>Ruby 2.0 introduced double splat operator captures all keyword arguments.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Mutations::SignUp</span> <span class="o">&lt;</span> <span class="no">Mutations</span><span class="o">::</span><span class="no">BaseMutation</span>
  <span class="n">null</span> <span class="kp">true</span>

  <span class="n">argument</span> <span class="ss">:email</span><span class="p">,</span> <span class="no">String</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>
  <span class="n">argument</span> <span class="ss">:password</span><span class="p">,</span> <span class="no">String</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>
  <span class="n">argument</span> <span class="ss">:expires_at</span><span class="p">,</span> <span class="no">GraphQL</span><span class="o">::</span><span class="no">Types</span><span class="o">::</span><span class="no">BigInt</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>
  <span class="n">argument</span> <span class="ss">:expires_in</span><span class="p">,</span> <span class="no">GraphQL</span><span class="o">::</span><span class="no">Types</span><span class="o">::</span><span class="no">BigInt</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>
  <span class="n">argument</span> <span class="ss">:resource_state</span><span class="p">,</span> <span class="no">Int</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>
  <span class="n">argument</span> <span class="ss">:username</span><span class="p">,</span> <span class="no">String</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">false</span>
  <span class="n">argument</span> <span class="ss">:firstname</span><span class="p">,</span> <span class="no">String</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>
  <span class="n">argument</span> <span class="ss">:lastname</span><span class="p">,</span> <span class="no">String</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>
  <span class="n">argument</span> <span class="ss">:sex</span><span class="p">,</span> <span class="no">String</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">false</span>
  <span class="n">argument</span> <span class="ss">:active_till</span><span class="p">,</span> <span class="no">GraphQL</span><span class="o">::</span><span class="no">Types</span><span class="o">::</span><span class="no">ISO8601DateTime</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>

  <span class="n">field</span> <span class="ss">:token</span><span class="p">,</span> <span class="no">String</span><span class="p">,</span> <span class="ss">null: </span><span class="kp">true</span>
  <span class="n">field</span> <span class="ss">:user</span><span class="p">,</span> <span class="no">Types</span><span class="o">::</span><span class="no">UserType</span><span class="p">,</span> <span class="ss">null: </span><span class="kp">true</span>

  <span class="k">def</span> <span class="nf">resolve</span><span class="p">(</span><span class="o">**</span><span class="n">arguments</span><span class="p">)</span>
    <span class="c1"># create your user with data passed in.</span>
    <span class="c1"># arguments is a hash like this: { email: '', password: '', expires_at: 1234556, ... }</span>
    <span class="c1"># use arguments[:email] to access them.</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h3 id="heading-create-a-new-type">Create a new Type</h3>

<p><a href="https://graphql-ruby.org/guides#type-definitions-guides" target="_blank">https://graphql-ruby.org/guides#type-definitions-guides</a></p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">module</span> <span class="nn">Types</span>
  <span class="k">class</span> <span class="nc">SignUpArguments</span> <span class="o">&lt;</span> <span class="no">Types</span><span class="o">::</span><span class="no">BaseInputObject</span>
    <span class="n">argument</span> <span class="ss">:expires_at</span><span class="p">,</span> <span class="no">GraphQL</span><span class="o">::</span><span class="no">Types</span><span class="o">::</span><span class="no">BigInt</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>
    <span class="n">argument</span> <span class="ss">:expires_in</span><span class="p">,</span> <span class="no">GraphQL</span><span class="o">::</span><span class="no">Types</span><span class="o">::</span><span class="no">BigInt</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>
    <span class="n">argument</span> <span class="ss">:resource_state</span><span class="p">,</span> <span class="no">Int</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>
    <span class="n">argument</span> <span class="ss">:username</span><span class="p">,</span> <span class="no">String</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">false</span>
    <span class="n">argument</span> <span class="ss">:firstname</span><span class="p">,</span> <span class="no">String</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>
    <span class="n">argument</span> <span class="ss">:lastname</span><span class="p">,</span> <span class="no">String</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>
    <span class="n">argument</span> <span class="ss">:sex</span><span class="p">,</span> <span class="no">String</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">false</span>
    <span class="n">argument</span> <span class="ss">:active_till</span><span class="p">,</span> <span class="no">GraphQL</span><span class="o">::</span><span class="no">Types</span><span class="o">::</span><span class="no">ISO8601DateTime</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>
  <span class="k">end</span>
<span class="k">end</span>

</code></pre></div></div>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Mutations::SignUp</span> <span class="o">&lt;</span> <span class="no">Mutations</span><span class="o">::</span><span class="no">BaseMutation</span>
  <span class="n">null</span> <span class="kp">true</span>

  <span class="n">argument</span> <span class="ss">:email</span><span class="p">,</span> <span class="no">String</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>
  <span class="n">argument</span> <span class="ss">:password</span><span class="p">,</span> <span class="no">String</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>
  <span class="n">argument</span> <span class="ss">:sign_up_details</span><span class="p">,</span> <span class="no">Types</span><span class="o">::</span><span class="no">SignUpArguments</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span>

  <span class="n">field</span> <span class="ss">:token</span><span class="p">,</span> <span class="no">String</span><span class="p">,</span> <span class="ss">null: </span><span class="kp">true</span>
  <span class="n">field</span> <span class="ss">:user</span><span class="p">,</span> <span class="no">Types</span><span class="o">::</span><span class="no">UserType</span><span class="p">,</span> <span class="ss">null: </span><span class="kp">true</span>

  <span class="k">def</span> <span class="nf">resolve</span><span class="p">(</span><span class="n">email</span><span class="p">:,</span> <span class="n">password</span><span class="p">:,</span> <span class="n">sign_up_details</span><span class="p">:)</span>
    <span class="c1"># create your user with data passed in.</span>
    <span class="c1"># sign_up_details.to_h =&gt; { expires_at: 1234556, ... }</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>]]></content><author><name>Yi Zeng</name></author><category term="articles" /><category term="graphql" /><category term="ruby" /><category term="ruby on rails" /><summary type="html"><![CDATA[Reduce GraphQL Ruby mutation arguments in the resolve method.]]></summary></entry><entry><title type="html">Add Sidekiq to a Docker Compose managed Rails project</title><link href="http://yizeng.me/2019/11/17/add-sidekiq-to-a-docker-compose-managed-rails-project/" rel="alternate" type="text/html" title="Add Sidekiq to a Docker Compose managed Rails project" /><published>2019-11-17T00:00:00+01:00</published><updated>2019-11-17T00:00:00+01:00</updated><id>http://yizeng.me/2019/11/17/add-sidekiq-to-a-docker-compose-managed-rails-project</id><content type="html" xml:base="http://yizeng.me/2019/11/17/add-sidekiq-to-a-docker-compose-managed-rails-project/"><![CDATA[<p><a href="https://github.com/mperham/sidekiq" target="_blank">Sidekiq</a> is a simple, efficient background processing for Ruby
that has a lot of advantage over delayed_job or Resque<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>.</p>

<p>Here are the few simple steps to integrate Sidekiq into a Dockerized Rails project:</p>

<p>If the project has not been dockerized yet,
the <a href="/2019/11/09/setup-a-ruby-on-rails-6-api-project-with-docker-compose/" target="_blank">previous post</a> can be helpful.</p>

<h2 id="heading-add-sidekiq-to-gemfile">Add Sidekiq to Gemfile</h2>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">gem</span> <span class="s1">'sidekiq'</span> <span class="c1"># Simple, efficient background processing for Ruby</span>
</code></pre></div></div>

<h2 id="heading-build-project">Build project</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker-compose build
</code></pre></div></div>

<h2 id="heading-setup-sidekiq">Setup Sidekiq</h2>

<p>Follow the steps below to setup Sidekiq.</p>

<ol>
  <li>
    <p>Generate a Sidekiq worker.</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker-compose run web bundle <span class="nb">exec </span>rails g sidekiq:worker Hard
</code></pre></div>    </div>
  </li>
  <li>
    <p>If ActiveJob is used, update <code class="language-plaintext highlighter-rouge">config/application.rb</code>.</p>

    <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">config</span><span class="p">.</span><span class="nf">active_job</span><span class="p">.</span><span class="nf">queue_adapter</span> <span class="o">=</span> <span class="ss">:sidekiq</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>If ActiveJob is not used, delete <code class="language-plaintext highlighter-rouge">app/jobs</code> folder.</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">rm</span> <span class="nt">-r</span> app/jobs/
</code></pre></div>    </div>
  </li>
  <li>
    <p>Set the Sidekiq Redis URL.</p>

    <p>By default, Sidekiq tries to connect to Redis at localhost:6379,
 which needs to be changed the Redis URL in Docker.</p>

    <ul>
      <li>
        <p>Create <code class="language-plaintext highlighter-rouge">config/initializers/sidekiq.rb</code>.</p>

        <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">touch </span>config/initializers/sidekiq.rb
</code></pre></div>        </div>
      </li>
      <li>
        <p>Set URL in initializer.</p>

        <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Sidekiq</span><span class="p">.</span><span class="nf">configure_server</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
  <span class="n">config</span><span class="p">.</span><span class="nf">redis</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">url: </span><span class="no">ENV</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="s1">'REDIS_URL_SIDEKIQ'</span><span class="p">,</span> <span class="s1">'redis://localhost:6379/1'</span><span class="p">)</span> <span class="p">}</span>
<span class="k">end</span>

<span class="no">Sidekiq</span><span class="p">.</span><span class="nf">configure_client</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
  <span class="n">config</span><span class="p">.</span><span class="nf">redis</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">url: </span><span class="no">ENV</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="s1">'REDIS_URL_SIDEKIQ'</span><span class="p">,</span> <span class="s1">'redis://localhost:6379/1'</span><span class="p">)</span> <span class="p">}</span>
<span class="k">end</span>
</code></pre></div>        </div>
      </li>
    </ul>
  </li>
  <li>
    <p>Add Sidekiq and Redis to docker-compose.yml</p>

    <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">version</span><span class="pi">:</span> <span class="s1">'</span><span class="s">3'</span>

<span class="na">services</span><span class="pi">:</span>
  <span class="na">db</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s1">'</span><span class="s">postgres:10-alpine'</span>
    <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">postgres:/var/lib/postgresql/data'</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">5432:5432'</span>

  <span class="na">redis</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s1">'</span><span class="s">redis:5-alpine'</span>
    <span class="na">command</span><span class="pi">:</span> <span class="s">redis-server</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">6379:6379'</span>
    <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">redis:/data'</span>

  <span class="na">sidekiq</span><span class="pi">:</span>
    <span class="na">depends_on</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">db'</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">redis'</span>
    <span class="na">build</span><span class="pi">:</span> <span class="s">.</span>
    <span class="na">command</span><span class="pi">:</span> <span class="s">bundle exec sidekiq</span>
    <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">.:/project'</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">/project/tmp'</span> <span class="c1"># don't mount tmp directory</span>
    <span class="na">environment</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">REDIS_URL_SIDEKIQ=redis://redis:6379/1</span>

  <span class="na">web</span><span class="pi">:</span>
    <span class="na">depends_on</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">db'</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">redis'</span>
    <span class="na">build</span><span class="pi">:</span> <span class="s">.</span>
    <span class="na">command</span><span class="pi">:</span> <span class="s">bash -c "rm -f tmp/pids/server.pid &amp;&amp; bundle exec rails s -p 3000 -b '0.0.0.0' -e ${RAILS_ENV}"</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">3000:3000'</span>
    <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">.:/project'</span>
    <span class="na">environment</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">REDIS_URL_SIDEKIQ=redis://redis:6379/1</span>

<span class="na">volumes</span><span class="pi">:</span>
  <span class="na">redis</span><span class="pi">:</span>
  <span class="na">postgres</span><span class="pi">:</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>Customize <code class="language-plaintext highlighter-rouge">REDIS_URL_SIDEKIQ</code></p>

    <p>Either add as an environment variable in <code class="language-plaintext highlighter-rouge">docker-compose.yml</code>:</p>

    <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">sidekiq</span><span class="pi">:</span>
  <span class="na">depends_on</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s1">'</span><span class="s">db'</span>
    <span class="pi">-</span> <span class="s1">'</span><span class="s">redis'</span>
  <span class="na">build</span><span class="pi">:</span> <span class="s">.</span>
  <span class="na">command</span><span class="pi">:</span> <span class="s">bundle exec sidekiq</span>
  <span class="na">volumes</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s1">'</span><span class="s">.:/project'</span>
  <span class="na">environment</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">REDIS_URL_SIDEKIQ=redis://redis:6379/12</span>
</code></pre></div>    </div>

    <p>Or add to <code class="language-plaintext highlighter-rouge">.env</code> file and load it in <code class="language-plaintext highlighter-rouge">docker-compose.yml</code></p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>OTHER_ENV=example
REDIS_URL_SIDEKIQ=redis://redis:6379/12
</code></pre></div>    </div>

    <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">sidekiq</span><span class="pi">:</span>
  <span class="na">depends_on</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s1">'</span><span class="s">db'</span>
    <span class="pi">-</span> <span class="s1">'</span><span class="s">redis'</span>
  <span class="na">build</span><span class="pi">:</span> <span class="s">.</span>
  <span class="na">command</span><span class="pi">:</span> <span class="s">bundle exec sidekiq</span>
  <span class="na">volumes</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s1">'</span><span class="s">.:/project'</span>
  <span class="na">env_file</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s1">'</span><span class="s">.env'</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>Add UI page to <code class="language-plaintext highlighter-rouge">routes.rb</code>.</p>

    <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">require</span> <span class="s1">'sidekiq/web'</span>
<span class="n">mount</span> <span class="no">Sidekiq</span><span class="o">::</span><span class="no">Web</span> <span class="o">=&gt;</span> <span class="s1">'/sidekiq'</span>
</code></pre></div>    </div>
  </li>
</ol>

<h2 id="heading-start-server">Start Server</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker-compose up <span class="nt">--build</span>
</code></pre></div></div>

<p>Go to <a href="http://localhost:3000/sidekiq" target="_blank">http://localhost:3000/sidekiq</a> to verify.</p>

<h2 id="heading-troubleshooting">Troubleshooting</h2>

<blockquote>
  <p>Errno::ENOENT - No such file or directory - bs_fetch:atomic_write_cache_file:rename</p>
</blockquote>

<ul>
  <li>
    <p><strong>Reason</strong>: Two processes are trying to load the /app/tmp directory at the same time<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>.</p>
  </li>
  <li>
    <p><strong>Solution</strong>: Add <code class="language-plaintext highlighter-rouge">- '/project/tmp'</code> to Sidekiq volumes in <code class="language-plaintext highlighter-rouge">docker-compose.yml</code>.</p>
  </li>
</ul>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p><a href="https://github.com/mperham/sidekiq/wiki/FAQ#how-does-sidekiq-compare-to-resque-or-delayed_job" target="_blank">Sidekiq Comparison</a> <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>See <a href="https://github.com/Shopify/bootsnap/issues/177#issuecomment-491711481" target="_blank">this</a> GitHub Comment. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Yi Zeng</name></author><category term="tutorials" /><category term="docker" /><category term="redis" /><category term="ruby on rails" /><category term="sidekiq" /><summary type="html"><![CDATA[How to add Sidekiq to a Docker Compose managed Rails project.]]></summary></entry><entry><title type="html">Use Redis for caching to a Docker Compose managed Rails project</title><link href="http://yizeng.me/2019/11/16/use-redis-for-caching-to-a-docker-compose-managed-rails-project/" rel="alternate" type="text/html" title="Use Redis for caching to a Docker Compose managed Rails project" /><published>2019-11-16T00:00:00+01:00</published><updated>2019-11-16T00:00:00+01:00</updated><id>http://yizeng.me/2019/11/16/use-redis-for-caching-to-a-docker-compose-managed-rails-project</id><content type="html" xml:base="http://yizeng.me/2019/11/16/use-redis-for-caching-to-a-docker-compose-managed-rails-project/"><![CDATA[<p>Since Rails 5.2, Redis cache has been added to Rails for caching,
in additional to the default memcache.</p>

<h2 id="heading-add-redis-to-gemfile">Add Redis to Gemfile</h2>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">gem</span> <span class="s1">'redis'</span> <span class="c1"># A Ruby client library for Redis</span>
</code></pre></div></div>

<h2 id="heading-build-project">Build project</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker-compose build
</code></pre></div></div>

<h2 id="heading-setup-redis-for-caching">Setup Redis for caching</h2>

<ol>
  <li>
    <p>Update <code class="language-plaintext highlighter-rouge">config/environments/production.rb</code>.</p>

    <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Use Redis store for caching.</span>
<span class="n">config</span><span class="p">.</span><span class="nf">cache_store</span> <span class="o">=</span> <span class="ss">:redis_cache_store</span><span class="p">,</span> <span class="p">{</span> <span class="ss">url: </span><span class="no">ENV</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="s2">"REDIS_URL_CACHING"</span><span class="p">,</span> <span class="s2">"redis://localhost:6379/0"</span><span class="p">)</span> <span class="p">}</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>Update <code class="language-plaintext highlighter-rouge">config/environments/development.rb</code>.</p>

    <p>Change <code class="language-plaintext highlighter-rouge">memory_store</code> to <code class="language-plaintext highlighter-rouge">redis_cache_store</code> and caching can be toggled by <code class="language-plaintext highlighter-rouge">bundle exec rails dev:cache</code>.</p>

    <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">config</span><span class="p">.</span><span class="nf">cache_store</span> <span class="o">=</span> <span class="ss">:redis_cache_store</span><span class="p">,</span> <span class="p">{</span> <span class="ss">url: </span><span class="no">ENV</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="s2">"REDIS_URL_CACHING"</span><span class="p">,</span> <span class="s2">"redis://localhost:6379/0"</span><span class="p">)</span> <span class="p">}</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>Customize Redis URL</p>

    <p>Please refer to the previous blog post <a href="/2019/11/12/load-environment-variables-with-docker-compose/" target="_blank">here</a>.</p>
  </li>
</ol>

<h2 id="heading-add-redis-to-docker-composeyml">Add Redis to docker-compose.yml</h2>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">version</span><span class="pi">:</span> <span class="s1">'</span><span class="s">3'</span>

<span class="na">services</span><span class="pi">:</span>
  <span class="na">db</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s1">'</span><span class="s">postgres:10-alpine'</span>
    <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">postgres:/var/lib/postgresql/data'</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">5432:5432'</span>

  <span class="na">redis</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s1">'</span><span class="s">redis:5-alpine'</span>
    <span class="na">command</span><span class="pi">:</span> <span class="s">redis-server</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">6379:6379'</span>
    <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">redis:/data'</span>

  <span class="na">web</span><span class="pi">:</span>
    <span class="na">depends_on</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">db'</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">redis'</span>
    <span class="na">build</span><span class="pi">:</span> <span class="s">.</span>
    <span class="na">command</span><span class="pi">:</span> <span class="s">bash -c "rm -f tmp/pids/server.pid &amp;&amp; bundle exec rails s -p 3000 -b '0.0.0.0'"</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">3000:3000'</span>
    <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">.:/project'</span>
    <span class="na">environment</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">REDIS_URL_CACHING=redis://redis:6379/0</span>

<span class="na">volumes</span><span class="pi">:</span>
  <span class="na">redis</span><span class="pi">:</span>
  <span class="na">postgres</span><span class="pi">:</span>
</code></pre></div></div>

<h2 id="heading-start-server">Start Server</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker-compose up <span class="nt">--build</span>
</code></pre></div></div>]]></content><author><name>Yi Zeng</name></author><category term="tutorials" /><category term="docker" /><category term="redis" /><category term="ruby on rails" /><category term="sidekiq" /><summary type="html"><![CDATA[How to use Redis for caching to a Docker Compose managed Rails project.]]></summary></entry><entry><title type="html">Load environment variables with Docker Compose</title><link href="http://yizeng.me/2019/11/12/load-environment-variables-with-docker-compose/" rel="alternate" type="text/html" title="Load environment variables with Docker Compose" /><published>2019-11-12T00:00:00+01:00</published><updated>2019-11-12T00:00:00+01:00</updated><id>http://yizeng.me/2019/11/12/load-environment-variables-with-docker-compose</id><content type="html" xml:base="http://yizeng.me/2019/11/12/load-environment-variables-with-docker-compose/"><![CDATA[<p>Generally there are two ways to load environment variables with Docker Compose in <code class="language-plaintext highlighter-rouge">docker-compose.yml</code>.</p>

<h2 id="heading-set-directly">Set directly</h2>

<p><code class="language-plaintext highlighter-rouge">docker-compose.yml</code></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">web</span><span class="pi">:</span>
  <span class="na">depends_on</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s1">'</span><span class="s">db'</span>
  <span class="na">build</span><span class="pi">:</span> <span class="s">.</span>
  <span class="na">ports</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s1">'</span><span class="s">3000:3000'</span>
  <span class="na">environment</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">RAILS_LOG_TO_STDOUT=enabled</span>
</code></pre></div></div>

<h2 id="heading-specify-the-env_file">Specify the env_file</h2>

<p>For example, if the Rails project uses <a href="https://github.com/bkeepers/dotenv" target="_blank">dotenv</a> gem,
then the <code class="language-plaintext highlighter-rouge">.env</code> file at the root of the project can be passed into Docker Compose like the following:</p>

<p><code class="language-plaintext highlighter-rouge">docker-compose.yml</code></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">version</span><span class="pi">:</span> <span class="s1">'</span><span class="s">3'</span>

<span class="na">services</span><span class="pi">:</span>
  <span class="na">db</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s1">'</span><span class="s">postgres:10-alpine'</span>
    <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">postgres:/var/lib/postgresql/data'</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">5432:5432'</span>
    <span class="na">env_file</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">.env'</span>

  <span class="na">web</span><span class="pi">:</span>
    <span class="na">depends_on</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">db'</span>
    <span class="na">build</span><span class="pi">:</span> <span class="s">.</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">3000:3000'</span>
    <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">.:/project'</span>
    <span class="na">env_file</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">.env'</span>

<span class="na">volumes</span><span class="pi">:</span>
  <span class="na">postgres</span><span class="pi">:</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">.env</code></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>RAILS_LOG_TO_STDOUT=enabled
</code></pre></div></div>]]></content><author><name>Yi Zeng</name></author><category term="tutorials" /><category term="docker" /><category term="ruby on rails" /><summary type="html"><![CDATA[How to load environment variables with Docker Compose.]]></summary></entry><entry><title type="html">Setup a Ruby on Rails 6 API project with Docker Compose</title><link href="http://yizeng.me/2019/11/09/setup-a-ruby-on-rails-6-api-project-with-docker-compose/" rel="alternate" type="text/html" title="Setup a Ruby on Rails 6 API project with Docker Compose" /><published>2019-11-09T00:00:00+01:00</published><updated>2019-11-09T00:00:00+01:00</updated><id>http://yizeng.me/2019/11/09/setup-a-ruby-on-rails-6-api-project-with-docker-compose</id><content type="html" xml:base="http://yizeng.me/2019/11/09/setup-a-ruby-on-rails-6-api-project-with-docker-compose/"><![CDATA[<p>Here is a quick rundown on how to setup a Ruby on Rails 6 API project with Docker Compose.</p>

<ul class="toc" id="markdown-toc">
  <li><a href="#heading-prerequisites" id="markdown-toc-heading-prerequisites">Prerequisites</a>    <ul>
      <li><a href="#heading-docker-and-docker-compose" id="markdown-toc-heading-docker-and-docker-compose">Docker and Docker Compose</a></li>
      <li><a href="#heading-ruby" id="markdown-toc-heading-ruby">Ruby</a></li>
    </ul>
  </li>
  <li><a href="#heading-create-a-rails-6-api-project" id="markdown-toc-heading-create-a-rails-6-api-project">Create a Rails 6 API project</a></li>
  <li><a href="#heading-setup-docker" id="markdown-toc-heading-setup-docker">Setup Docker</a>    <ul>
      <li><a href="#heading-create-dockerignore" id="markdown-toc-heading-create-dockerignore">Create .dockerignore</a></li>
      <li><a href="#heading-create-dockerfile" id="markdown-toc-heading-create-dockerfile">Create Dockerfile</a></li>
    </ul>
  </li>
  <li><a href="#heading-setup-docker-compose" id="markdown-toc-heading-setup-docker-compose">Setup Docker Compose</a>    <ul>
      <li><a href="#heading-create-docker-composeyml" id="markdown-toc-heading-create-docker-composeyml">Create docker-compose.yml</a></li>
      <li><a href="#heading-config-databaseyml" id="markdown-toc-heading-config-databaseyml">Config database.yml</a></li>
    </ul>
  </li>
  <li><a href="#heading-start-rails-server" id="markdown-toc-heading-start-rails-server">Start Rails Server</a></li>
  <li><a href="#heading-install-rspec-optional" id="markdown-toc-heading-install-rspec-optional">Install RSpec (Optional)</a></li>
  <li><a href="#heading-troubleshooting" id="markdown-toc-heading-troubleshooting">Troubleshooting</a></li>
</ul>

<h2 id="heading-prerequisites">Prerequisites</h2>

<h3 id="heading-docker-and-docker-compose">Docker and Docker Compose</h3>

<p>Docker: <a href="https://www.docker.com/get-started" target="_blank">https://www.docker.com/get-started</a></p>

<p>Docker Compose: <a href="https://docs.docker.com/compose/install" target="_blank">https://docs.docker.com/compose/install/</a></p>

<p>For example, I have:</p>

<blockquote>
  <p>$ docker -v<br />
$ Docker version 19.03.2, build 6a30dfc</p>

  <p>$ docker-compose -v<br />
$ docker-compose version 1.24.1, build 4667896b</p>
</blockquote>

<h3 id="heading-ruby">Ruby</h3>

<p>Rails 6 requires Ruby 2.5.0 or newer<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>.</p>

<p>If the local machine hasn't got Ruby installed,
<a href="https://rvm.io/" target="_blank">RVM</a> would be a common solution to consider.</p>

<p>For installing RVM with default Ruby and Rails in one command, run:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="se">\c</span>url <span class="nt">-sSL</span> https://get.rvm.io | bash <span class="nt">-s</span> stable <span class="nt">--rails</span>
</code></pre></div></div>

<p>To verify the successful installation:</p>

<blockquote>
  <p>$ ruby -v<br />
$ ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-darwin18]</p>

  <p>$ rails -v<br />
$ Rails 6.0.2.1</p>
</blockquote>

<h2 id="heading-create-a-rails-6-api-project">Create a Rails 6 API project</h2>

<p>To create a new Ruby on Rails 6 API project, run:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rails new rails-6-api-docker-demo <span class="nt">--api</span> <span class="nt">--database</span><span class="o">=</span>postgresql <span class="nt">-T</span> <span class="nt">-C</span>
</code></pre></div></div>

<ul>
  <li><code class="language-plaintext highlighter-rouge">rails-6-api-docker-demo</code>: The name of the new project.</li>
  <li><code class="language-plaintext highlighter-rouge">--api</code>: Create an API only project.</li>
  <li><code class="language-plaintext highlighter-rouge">--database=postgresql</code>: Use <a href="https://www.postgresql.org/" target="_blank">PostgresQL</a> as the default database adapter.</li>
  <li><code class="language-plaintext highlighter-rouge">-T</code>: (Optional) Skip test files. <a href="https://rspec.info/" target="_blank">RSpec</a> would be a more common option.</li>
  <li><code class="language-plaintext highlighter-rouge">-C</code>: (Optional) Skip ActionCable if no WebSockets is needed for the project.</li>
</ul>

<h2 id="heading-setup-docker">Setup Docker</h2>

<h3 id="heading-create-dockerignore">Create .dockerignore</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd </span>rails-6-api-docker-demo
<span class="nb">touch</span> .dockerignore
</code></pre></div></div>

<p><a href="https://docs.docker.com/engine/reference/builder/#dockerignore-file" target="_blank"><code class="language-plaintext highlighter-rouge">.dockerignore</code></a>
files essentially works the same way as <code class="language-plaintext highlighter-rouge">.gitignore</code>, but for docker containers.
You may just use the content from <code class="language-plaintext highlighter-rouge">.gitignore</code> file plus the <code class="language-plaintext highlighter-rouge">.git</code> folder and <code class="language-plaintext highlighter-rouge">.gitignore</code> itself.</p>

<p><a href="https://www.gitignore.io/" target="_blank">gitignore.io</a> can be handy for generating the desired <code class="language-plaintext highlighter-rouge">.gitignore</code> file.
For example, <a href="https://www.gitignore.io/api/git,rails,rubymine" target="_blank">https://www.gitignore.io/api/git,rails,rubymine</a>
gives an example of <code class="language-plaintext highlighter-rouge">.gitignore</code> file for git, rails and rubymine IDE.</p>

<p><strong>Here's a complete example</strong>: <a href="https://gist.github.com/yizeng/eeeb48d6823801061791cc5581f7e1fc" target="_blank">https://gist.github.com/yizeng/eeeb48d6823801061791cc5581f7e1fc</a></p>

<h3 id="heading-create-dockerfile">Create Dockerfile</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">touch </span>Dockerfile
</code></pre></div></div>

<p>A Dockerfile is a text document that contains all the commands
a user could call on the command line to assemble an image.
Using docker build users can create an automated build
that executes several command-line instructions in succession.</p>

<p>Full documentation can be found <a href="https://docs.docker.com/engine/reference/builder/" target="_blank">here</a>.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>FROM ruby:alpine

RUN apk update <span class="o">&amp;&amp;</span> apk add bash build-base nodejs postgresql-dev tzdata

RUN <span class="nb">mkdir</span> /project
WORKDIR /project

COPY Gemfile Gemfile.lock ./
RUN gem <span class="nb">install </span>bundler <span class="nt">--no-document</span>
RUN bundle <span class="nb">install</span> <span class="nt">--no-binstubs</span> <span class="nt">--jobs</span> <span class="si">$(</span><span class="nb">nproc</span><span class="si">)</span> <span class="nt">--retry</span> 3

COPY <span class="nb">.</span> <span class="nb">.</span>

CMD <span class="o">[</span><span class="s2">"bundle"</span>, <span class="s2">"exec"</span>, <span class="s2">"rails"</span>, <span class="s2">"server"</span>, <span class="s2">"-b"</span>, <span class="s2">"0.0.0.0"</span><span class="o">]</span>
</code></pre></div></div>

<ul>
  <li><code class="language-plaintext highlighter-rouge">FROM ruby:alpine</code>: Select the base image to build from.
Here uses the latest alpine version of Ruby.
Note that the version needs to match the Ruby version in <code class="language-plaintext highlighter-rouge">Gemfile</code>.
If there is a mismatch (e.g. <code class="language-plaintext highlighter-rouge">ruby '2.6.5'</code> in <code class="language-plaintext highlighter-rouge">Gemfile</code>), then here would be <code class="language-plaintext highlighter-rouge">FROM ruby:2.6.5-alpine</code>.
More versions can be found at the <a href="https://hub.docker.com/_/ruby" target="_blank">Docker Hub</a>.</li>
  <li><code class="language-plaintext highlighter-rouge">RUN apk update &amp;&amp; apk add build-base nodejs postgresql-dev tzdata</code>: Install the required packages inside Docker.</li>
  <li><code class="language-plaintext highlighter-rouge">RUN mkdir /project</code>: Create a folder called <code class="language-plaintext highlighter-rouge">project</code> to host the codebase.</li>
  <li><code class="language-plaintext highlighter-rouge">WORKDIR /project</code>: Set the working directory to <code class="language-plaintext highlighter-rouge">project</code> folder.</li>
  <li><code class="language-plaintext highlighter-rouge">COPY Gemfile Gemfile.lock ./</code>: Copy the files to the working directory.</li>
  <li><code class="language-plaintext highlighter-rouge">RUN gem install bundler</code>: Install the bundler gem.
It needs to match the version in <code class="language-plaintext highlighter-rouge">Gemfile.lock</code>,
A specific version can be set like <code class="language-plaintext highlighter-rouge">RUN gem install bundler -v 2.0.2</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">COPY . .</code>: Copy the codebase into Docker.</li>
  <li><code class="language-plaintext highlighter-rouge">CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]</code>: Set the start command.</li>
</ul>

<h2 id="heading-setup-docker-compose">Setup Docker Compose</h2>

<h3 id="heading-create-docker-composeyml">Create docker-compose.yml</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">touch </span>docker-compose.yml
</code></pre></div></div>

<p>Docker compose is a tool to build a multi-container application,
where <code class="language-plaintext highlighter-rouge">docker-compose.yml</code> is the YAML config file that
tells Docker Compose how to build the containers together.</p>

<p>A more detailed documentation can be found <a href="https://docs.docker.com/compose/compose-file/" target="_blank">here</a>.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">version</span><span class="pi">:</span> <span class="s1">'</span><span class="s">3'</span>

<span class="na">services</span><span class="pi">:</span>
  <span class="na">db</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s1">'</span><span class="s">postgres:10-alpine'</span>
    <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">postgres:/var/lib/postgresql/data'</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">5432:5432'</span>
    <span class="na">environment</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">POSTGRES_HOST_AUTH_METHOD=trust</span>

  <span class="na">redis</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s1">'</span><span class="s">redis:5-alpine'</span>
    <span class="na">command</span><span class="pi">:</span> <span class="s">redis-server</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">6379:6379'</span>
    <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">redis:/data'</span>

  <span class="na">web</span><span class="pi">:</span>
    <span class="na">depends_on</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">db'</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">redis'</span>
    <span class="na">build</span><span class="pi">:</span> <span class="s">.</span>
    <span class="na">command</span><span class="pi">:</span> <span class="s">bash -c "rm -f tmp/pids/server.pid &amp;&amp; bundle exec rails s -p 3000 -b '0.0.0.0'"</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">3000:3000'</span>
    <span class="na">environment</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">DATABASE_HOST=db</span>

<span class="na">volumes</span><span class="pi">:</span>
  <span class="na">redis</span><span class="pi">:</span>
  <span class="na">postgres</span><span class="pi">:</span>
</code></pre></div></div>

<p>Here defines 3 services in order to run the Rails application:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">db</code>: The PostgreSQL database service, which uses <code class="language-plaintext highlighter-rouge">postgres:10-alpine</code> here for example.
It maps port 5432 inside Docker to the same port on local host machine.</li>
  <li><code class="language-plaintext highlighter-rouge">redis</code>: The Redis caching service, which uses <code class="language-plaintext highlighter-rouge">redis:5-alpine</code> here for example.
It maps port 6379 inside Docker to the same port on local host machine.
This is needed if Sidekiq is used for job processing
or Rails caching is set to Redis instead of memcache.</li>
  <li><code class="language-plaintext highlighter-rouge">web</code>: This is how the Rails API is composed.
It depends on <code class="language-plaintext highlighter-rouge">db</code> and <code class="language-plaintext highlighter-rouge">redis</code> with port 3000 exposed on host machine.</li>
</ul>

<h3 id="heading-config-databaseyml">Config database.yml</h3>

<p>The default Ruby on Rails' <code class="language-plaintext highlighter-rouge">database.yml</code> needs to be configed with
the Docker database URL by adding <code class="language-plaintext highlighter-rouge">host</code>, <code class="language-plaintext highlighter-rouge">username</code> and <code class="language-plaintext highlighter-rouge">password</code> to it.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">default</span><span class="pi">:</span> <span class="nl">&amp;default</span>
  <span class="na">adapter</span><span class="pi">:</span> <span class="s">postgresql</span>
  <span class="na">encoding</span><span class="pi">:</span> <span class="s">unicode</span>
  <span class="na">pool</span><span class="pi">:</span> <span class="s">&lt;%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %&gt;</span>
  <span class="na">host</span><span class="pi">:</span> <span class="s">&lt;%= ENV.fetch("DATABASE_HOST") { "localhost" } %&gt;</span>
  <span class="na">port</span><span class="pi">:</span> <span class="s">&lt;%= ENV.fetch("DATABASE_PORT") { 5432 } %&gt;</span>
  <span class="na">username</span><span class="pi">:</span> <span class="s">&lt;%= ENV.fetch("DATABASE_USERNAME") { "postgres" } %&gt;</span>
  <span class="na">password</span><span class="pi">:</span> <span class="s">&lt;%= ENV.fetch("DATABASE_PASSWORD") { "" } %&gt;</span>

<span class="na">development</span><span class="pi">:</span>
  <span class="na">&lt;&lt;</span><span class="pi">:</span> <span class="nv">*default</span>
  <span class="na">database</span><span class="pi">:</span> <span class="s">rails_6_api_docker_demo_development</span>

<span class="na">test</span><span class="pi">:</span>
  <span class="na">&lt;&lt;</span><span class="pi">:</span> <span class="nv">*default</span>
  <span class="na">database</span><span class="pi">:</span> <span class="s">rails_6_api_docker_demo_test</span>

<span class="na">production</span><span class="pi">:</span>
  <span class="na">&lt;&lt;</span><span class="pi">:</span> <span class="nv">*default</span>
  <span class="na">database</span><span class="pi">:</span> <span class="s">rails_6_api_docker_demo_production</span>
  <span class="na">username</span><span class="pi">:</span> <span class="s">&lt;%= ENV.fetch("DATABASE_USERNAME") { "rails_6_api_docker_demo" } %&gt;</span>
  <span class="na">password</span><span class="pi">:</span> <span class="s">&lt;%= ENV.fetch("DATABASE_PASSWORD") { "" } %&gt;</span>
</code></pre></div></div>

<h2 id="heading-start-rails-server">Start Rails Server</h2>

<p>Build the containers</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker-compose build
</code></pre></div></div>

<p>Before starting the server, Rails databases need to be created first.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker-compose run web bundle <span class="nb">exec </span>rails db:create
docker-compose run web bundle <span class="nb">exec </span>rails db:migrate
</code></pre></div></div>

<p>Finally, to build and spin up the Rails 6 API server:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker-compose up <span class="nt">--build</span>
</code></pre></div></div>

<p>Head over to <a href="http://localhost:3000" target="_blank">http://localhost:3000</a> to test it out and start building the real API!</p>

<h2 id="heading-install-rspec-optional">Install RSpec (Optional)</h2>

<p>If the project was initialized with <code class="language-plaintext highlighter-rouge">-T</code> option that no tests are generated,
a testing framework would be needed for the project.</p>

<p>Here is how to add <a href="https://rspec.info/" target="_blank">RSpec</a> support.</p>

<ol>
  <li>
    <p>Add rspec-rails to Gemfile</p>

    <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Add it inside group :development, :test</span>
<span class="n">group</span> <span class="ss">:development</span><span class="p">,</span> <span class="ss">:test</span> <span class="k">do</span>
  <span class="c1"># some existing gems...</span>
  <span class="c1"># ...</span>

  <span class="n">gem</span> <span class="s1">'rspec-rails'</span>
<span class="k">end</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>Rebuild the containers</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker-compose build
</code></pre></div>    </div>
  </li>
  <li>
    <p>Install RSpec</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker-compose run web bundle <span class="nb">exec </span>rails generate rspec:install
</code></pre></div>    </div>
  </li>
  <li>
    <p>Run the tests</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker-compose run web bundle <span class="nb">exec </span>rspec
</code></pre></div>    </div>
  </li>
</ol>

<h2 id="heading-troubleshooting">Troubleshooting</h2>

<blockquote>
  <p>Error starting userland proxy: listen tcp 0.0.0.0:6379: bind: address already in use</p>
</blockquote>

<ul>
  <li>
    <p><strong>Reason</strong>: Port 6379 is already in use on local machine.This is most likely that there is another Redis server running locally.</p>
  </li>
  <li>
    <p><strong>Solution</strong>: Either stop the Redis on local machine or map to another port in <code class="language-plaintext highlighter-rouge">docker-compose.yml</code>, like <code class="language-plaintext highlighter-rouge">- '6380:6379'</code>.</p>
  </li>
</ul>

<blockquote>
  <p>Error starting userland proxy: listen tcp 0.0.0.0:5432: bind: address already in use</p>
</blockquote>

<ul>
  <li>
    <p><strong>Reason</strong>: Port 5432 is already in use on local machine. This is most likely that there is another PostgreSQL server running locally.</p>
  </li>
  <li>
    <p><strong>Solution</strong>: Either stop the PostgreSQL on local machine or map to another port in <code class="language-plaintext highlighter-rouge">docker-compose.yml</code>, like <code class="language-plaintext highlighter-rouge">- '5434:5432'</code>.</p>
  </li>
</ul>

<blockquote>
  <p>Your Ruby version is 2.6.5, but your Gemfile specified 2.6.3<br />
ERROR: Service 'web' failed to build: The command '/bin/sh -c bundle install' returned a non-zero code: 18</p>
</blockquote>

<ul>
  <li>
    <p><strong>Reason</strong>: There is a Ruby version mismatch between <code class="language-plaintext highlighter-rouge">Gemfile</code> and Docker container.</p>
  </li>
  <li>
    <p><strong>Solution</strong>: Either update the Ruby version in <code class="language-plaintext highlighter-rouge">Gemfile</code> to <code class="language-plaintext highlighter-rouge">2.6.5</code>, or pull explicitly Ruby <code class="language-plaintext highlighter-rouge">2.6.3</code> image in <code class="language-plaintext highlighter-rouge">Dockerfile</code> like <code class="language-plaintext highlighter-rouge">FROM ruby:2.6.3-alpine</code>.</p>
  </li>
</ul>

<blockquote>
  <p>/usr/local/bundle/ruby/2.6.0/gems/activesupport-6.0.2.1/lib/active_support/railtie.rb:39:in<br />
rescue in block in &lt;class:Railtie&gt;': tzinfo-data is not present.<br />
Please add gem 'tzinfo-data' to your Gemfile and run bundle install (TZInfo::DataSourceNotFound)<br />
/usr/local/bundle/ruby/2.6.0/gems/tzinfo-1.2.5/lib/tzinfo/zoneinfo_data_source.rb:180:in `initialize':<br />
None of the paths included in TZInfo::ZoneinfoDataSource.search_path are valid zoneinfo directories. (TZInfo::ZoneinfoDirectoryNotFound)</p>
</blockquote>

<ul>
  <li>
    <p><strong>Reason</strong>: Required <code class="language-plaintext highlighter-rouge">tzdata</code> package was not installed in Docker container.</p>
  </li>
  <li>
    <p><strong>Solution</strong>: Include <code class="language-plaintext highlighter-rouge">tzdata</code> in <code class="language-plaintext highlighter-rouge">Dockerfile</code>'s <code class="language-plaintext highlighter-rouge">RUN apk add</code> command (see details above).</p>
  </li>
</ul>

<blockquote>
  <p>/usr/local/lib/ruby/2.6.0/rubygems.rb:283:in `find_spec_for_exe':<br />
Could not find 'bundler' (2.0.2) required by your /project/Gemfile.lock. (Gem::GemNotFoundException)<br />
ERROR: Service 'web' failed to build: The command '/bin/sh -c bundle install' returned a non-zero code: 1</p>
</blockquote>

<ul>
  <li>
    <p><strong>Reason</strong>: Required gem bundler was not installed in Docker container.</p>
  </li>
  <li>
    <p><strong>Solution</strong>: Include <code class="language-plaintext highlighter-rouge">RUN gem install bundler</code> command in <code class="language-plaintext highlighter-rouge">Dockerfile</code> (see details above).</p>
  </li>
</ul>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>https://edgeguides.rubyonrails.org/upgrading_ruby_on_rails.html#ruby-versions <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Yi Zeng</name></author><category term="tutorials" /><category term="api" /><category term="docker" /><category term="ruby on rails" /><summary type="html"><![CDATA[How to setup a Ruby on Rails 6 API project with Docker Compose.]]></summary></entry><entry><title type="html">Generate Rails test fixtures yaml from database dump</title><link href="http://yizeng.me/2017/07/16/generate-rails-test-fixtures-yaml-from-database-dump/" rel="alternate" type="text/html" title="Generate Rails test fixtures yaml from database dump" /><published>2017-07-16T00:00:00+02:00</published><updated>2017-07-16T00:00:00+02:00</updated><id>http://yizeng.me/2017/07/16/generate-rails-test-fixtures-yaml-from-database-dump</id><content type="html" xml:base="http://yizeng.me/2017/07/16/generate-rails-test-fixtures-yaml-from-database-dump/"><![CDATA[<p>I came across a problem while working on my little side project <a href="http://www.strafforts.com/">Strafforts</a>
(A Visualizer for Strava Estimated Best Efforts and Races),
that how to generate Ruby on Rails test fixtures yaml files from a development database dump.</p>

<p>After some Googling, <a href="http://www.austinstory.com/script-to-generate-fixtures-from-a-database/">this article</a> gave me directions and therefore made my own version of it.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">namespace</span> <span class="ss">:db</span> <span class="k">do</span>
  <span class="n">desc</span> <span class="s1">'Convert development DB to Rails test fixtures'</span>
  <span class="n">task</span> <span class="ss">to_fixtures: :environment</span> <span class="k">do</span>
    <span class="no">TABLES_TO_SKIP</span> <span class="o">=</span> <span class="sx">%w[ar_internal_metadata delayed_jobs schema_info schema_migrations]</span><span class="p">.</span><span class="nf">freeze</span>

    <span class="k">begin</span>
      <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span><span class="p">.</span><span class="nf">establish_connection</span>
      <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span><span class="p">.</span><span class="nf">connection</span><span class="p">.</span><span class="nf">tables</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">table_name</span><span class="o">|</span>
        <span class="k">next</span> <span class="k">if</span> <span class="no">TABLES_TO_SKIP</span><span class="p">.</span><span class="nf">include?</span><span class="p">(</span><span class="n">table_name</span><span class="p">)</span>

        <span class="n">conter</span> <span class="o">=</span> <span class="s1">'000'</span>
        <span class="n">file_path</span> <span class="o">=</span> <span class="s2">"</span><span class="si">#{</span><span class="no">Rails</span><span class="p">.</span><span class="nf">root</span><span class="si">}</span><span class="s2">/test/fixtures/</span><span class="si">#{</span><span class="n">table_name</span><span class="si">}</span><span class="s2">.yml"</span>
        <span class="no">File</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="n">file_path</span><span class="p">,</span> <span class="s1">'w'</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">file</span><span class="o">|</span>
          <span class="n">rows</span> <span class="o">=</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span><span class="p">.</span><span class="nf">connection</span><span class="p">.</span><span class="nf">select_all</span><span class="p">(</span><span class="s2">"SELECT * FROM </span><span class="si">#{</span><span class="n">table_name</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
          <span class="n">data</span> <span class="o">=</span> <span class="n">rows</span><span class="p">.</span><span class="nf">each_with_object</span><span class="p">({})</span> <span class="k">do</span> <span class="o">|</span><span class="n">record</span><span class="p">,</span> <span class="nb">hash</span><span class="o">|</span>
            <span class="n">suffix</span> <span class="o">=</span> <span class="n">record</span><span class="p">[</span><span class="s1">'id'</span><span class="p">].</span><span class="nf">blank?</span> <span class="p">?</span> <span class="n">conter</span><span class="p">.</span><span class="nf">succ!</span> <span class="p">:</span> <span class="n">record</span><span class="p">[</span><span class="s1">'id'</span><span class="p">]</span>
            <span class="nb">hash</span><span class="p">[</span><span class="s2">"</span><span class="si">#{</span><span class="n">table_name</span><span class="p">.</span><span class="nf">singularize</span><span class="si">}</span><span class="s2">_</span><span class="si">#{</span><span class="n">suffix</span><span class="si">}</span><span class="s2">"</span><span class="p">]</span> <span class="o">=</span> <span class="n">record</span>
          <span class="k">end</span>
          <span class="nb">puts</span> <span class="s2">"Writing table '</span><span class="si">#{</span><span class="n">table_name</span><span class="si">}</span><span class="s2">' to '</span><span class="si">#{</span><span class="n">file_path</span><span class="si">}</span><span class="s2">'"</span>
          <span class="n">file</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="nf">to_yaml</span><span class="p">)</span>
        <span class="k">end</span>
      <span class="k">end</span>
    <span class="k">ensure</span>
      <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span><span class="p">.</span><span class="nf">connection</span><span class="p">.</span><span class="nf">close</span> <span class="k">if</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span><span class="p">.</span><span class="nf">connection</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Then it can be used just like any other Rake tasks:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rake db:to_fixtures
</code></pre></div></div>

<p>All generated <code class="language-plaintext highlighter-rouge">*.yml</code> files will be fully compaitible with Rails and can be loaded to tests.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">---</span>
<span class="na">country_1</span><span class="pi">:</span>
  <span class="na">id</span><span class="pi">:</span> <span class="m">1</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">New Zealand</span>
  <span class="na">created_at</span><span class="pi">:</span> <span class="s1">'</span><span class="s">2017-02-20</span><span class="nv"> </span><span class="s">08:56:18.481455'</span>
  <span class="na">updated_at</span><span class="pi">:</span> <span class="s1">'</span><span class="s">2017-02-20</span><span class="nv"> </span><span class="s">08:56:18.481455'</span>
<span class="na">country_2</span><span class="pi">:</span>
  <span class="na">id</span><span class="pi">:</span> <span class="m">2</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">United States</span>
  <span class="na">created_at</span><span class="pi">:</span> <span class="s1">'</span><span class="s">2017-02-20</span><span class="nv"> </span><span class="s">09:51:23.430189'</span>
  <span class="na">updated_at</span><span class="pi">:</span> <span class="s1">'</span><span class="s">2017-02-20</span><span class="nv"> </span><span class="s">09:51:23.430189'</span>
<span class="na">country_3</span><span class="pi">:</span>
  <span class="na">id</span><span class="pi">:</span> <span class="m">3</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">Deutschland</span>
  <span class="na">created_at</span><span class="pi">:</span> <span class="s1">'</span><span class="s">2017-02-20</span><span class="nv"> </span><span class="s">09:53:23.819761'</span>
  <span class="na">updated_at</span><span class="pi">:</span> <span class="s1">'</span><span class="s">2017-02-20</span><span class="nv"> </span><span class="s">09:53:23.819761'</span>
</code></pre></div></div>]]></content><author><name>Yi Zeng</name></author><category term="tutorials" /><category term="database" /><category term="ruby on rails" /><summary type="html"><![CDATA[How to generate Ruby on Rails test fixtures yaml files from a development database dump.]]></summary></entry><entry><title type="html">Add TypeScript support to Ruby on Rails - A quick and hacky example</title><link href="http://yizeng.me/2017/05/20/add-typescript-support-to-ruby-on-rails-a-quick-and-hacky-example/" rel="alternate" type="text/html" title="Add TypeScript support to Ruby on Rails - A quick and hacky example" /><published>2017-05-20T00:00:00+02:00</published><updated>2017-05-20T00:00:00+02:00</updated><id>http://yizeng.me/2017/05/20/add-typescript-support-to-ruby-on-rails-a-quick-and-hacky-example</id><content type="html" xml:base="http://yizeng.me/2017/05/20/add-typescript-support-to-ruby-on-rails-a-quick-and-hacky-example/"><![CDATA[<blockquote>
  <p>For a proper and comprehensive integration of TypeScript in Rails, please see <a href="https://github.com/rails/webpacker">Webpacker</a> gem.</p>
</blockquote>

<p>As I'm currently learning <a href="https://www.typescriptlang.org/">TypeScript</a>,
I wondered if I could apply it to my little <a href="http://rubyonrails.org/">Ruby on Rails</a> project <a href="http://www.strafforts.com/">Strafforts</a>
(A Visualizer for Strava Estimated Best Efforts and Races),
so that I could use this real world project to help me learning TypeScript quickly.</p>

<p>Even though there is a gem called <a href="https://github.com/typescript-ruby/typescript-rails">typescript-rails</a>
which is a Rails asset pipeline wrapper for the TypeScript,
or a proper and comprehensive integration of TypeScript in Rails like <a href="https://github.com/rails/webpacker">Webpacker</a> gem.
I have decided to create my own quick and hacky way of introducing TypeScipt to my Ruby on Rails project,
mostly due to the following reasons:</p>

<ul>
  <li>The purpose is to learn TypeScript with all its setup process, so why not start from scratch by myself
as I'm not trying to create a nice and complete solution for everyone.</li>
  <li><code class="language-plaintext highlighter-rouge">typescript-rails</code> project hasn't been updated for almost a year.
Is it well maintained and fully compatible with the latest Rails 5.1.x?</li>
  <li><code class="language-plaintext highlighter-rouge">webpacker</code> is a whole solution which is too heavy for TypeScript learning purpose.</li>
  <li><a href="http://weblog.rubyonrails.org/2017/4/27/Rails-5-1-final/">Rails 5.1.x supports Yarn now</a>.
Why not try the traditional Node.js way instead of the Rails way?</li>
</ul>

<p>Therefore I have added TypeScript support to my Ruby on Rails project in a quick and hacky way.</p>

<ul class="toc" id="markdown-toc">
  <li><a href="#heading-install-yarn" id="markdown-toc-heading-install-yarn">Install Yarn</a></li>
  <li><a href="#heading-install-typescript-through-yarn" id="markdown-toc-heading-install-typescript-through-yarn">Install TypeScript through Yarn</a>    <ul>
      <li><a href="#heading-initialize" id="markdown-toc-heading-initialize">Initialize</a></li>
      <li><a href="#heading-add-typescipt-package" id="markdown-toc-heading-add-typescipt-package">Add TypeScipt package</a></li>
      <li><a href="#heading-add-to-assets-paths" id="markdown-toc-heading-add-to-assets-paths">Add to assets paths</a></li>
      <li><a href="#heading-update-gitignore" id="markdown-toc-heading-update-gitignore">Update .gitignore</a></li>
    </ul>
  </li>
  <li><a href="#heading-write-typescript" id="markdown-toc-heading-write-typescript">Write TypeScript</a></li>
  <li><a href="#heading-add-tsconfigjson" id="markdown-toc-heading-add-tsconfigjson">Add tsconfig.json</a></li>
  <li><a href="#heading-add-rake-build-task" id="markdown-toc-heading-add-rake-build-task">Add Rake build task</a></li>
  <li><a href="#heading-demos" id="markdown-toc-heading-demos">Demos</a></li>
</ul>

<h2 id="heading-install-yarn">Install Yarn</h2>

<p><a href="https://yarnpkg.com/lang/en/">Yarn</a> is a JavaScript package manager, a quick, secure and reliable npm alternative.
which is officially supported by Rails from version 5.1.</p>

<p>To install Yarn, please follow the official installation guide <a href="https://yarnpkg.com/en/docs/install">here</a>.
For macOS users, simply use homebrew <code class="language-plaintext highlighter-rouge">brew install yarn</code>.</p>

<h2 id="heading-install-typescript-through-yarn">Install TypeScript through Yarn</h2>

<h3 id="heading-initialize">Initialize</h3>

<p>For projects with Rails 5.1 or above,
<code class="language-plaintext highlighter-rouge">package.json</code> should have been created automatically when app was created.
Otherwise for older versions of Rails, use <code class="language-plaintext highlighter-rouge">yarn init</code> to create a <code class="language-plaintext highlighter-rouge">package.json</code> first.</p>

<h3 id="heading-add-typescipt-package">Add TypeScipt package</h3>

<p>Use the following command to add TypeScript package to the project.</p>

<blockquote>
  <p>yarn add typescript</p>
</blockquote>

<p>It will create <code class="language-plaintext highlighter-rouge">node_modules</code> folder under project root
and a <code class="language-plaintext highlighter-rouge">yarn.lock</code> file containing information about the package versions.
If installation is successful, TypeScript binary should be accessible in <code class="language-plaintext highlighter-rouge">node_modules/.bin/tsc</code>.</p>

<h3 id="heading-add-to-assets-paths">Add to assets paths</h3>

<p>For users with Rails 5.1 or above, this step should have been done automatically.
But for users with older Rails versions, <code class="language-plaintext highlighter-rouge">node_modules</code> folder needs to be added to Rails assets paths manually.</p>

<p>To do so, add the following line to <code class="language-plaintext highlighter-rouge">config/initializers/assets.rb</code>:</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><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">paths</span> <span class="o">&lt;&lt;</span> <span class="no">Rails</span><span class="p">.</span><span class="nf">root</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="s1">'node_modules'</span><span class="p">)</span></code></pre></figure>

<h3 id="heading-update-gitignore">Update .gitignore</h3>

<p>For users with Rails 5.0 or under, once <code class="language-plaintext highlighter-rouge">node_modules</code> folder is created,
remember to add it together with Yarn's error log file to <code class="language-plaintext highlighter-rouge">.gitignore</code>,
so that they will not be commited to the repository.</p>

<blockquote>
  <p>/node_modules<br />
/yarn-error.log</p>
</blockquote>

<h2 id="heading-write-typescript">Write TypeScript</h2>

<p>Now write some TypeScript to replace the existing CoffeeScript or plain JavaScript.
For example, create a TypeScript file <code class="language-plaintext highlighter-rouge">welcome.ts</code> under <code class="language-plaintext highlighter-rouge">app/assets/javascripts</code>.</p>

<figure class="highlight"><pre><code class="language-typescript" data-lang="typescript"><span class="kd">class</span> <span class="nx">HelloWorld</span> <span class="p">{</span>

    <span class="k">private</span> <span class="nx">name</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>

    <span class="kd">constructor</span><span class="p">(</span><span class="nx">name</span><span class="p">:</span> <span class="kr">string</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="p">}</span>

    <span class="nx">print</span><span class="p">()</span> <span class="p">{</span>
        <span class="nx">alert</span><span class="p">(</span><span class="s2">`Hello World, </span><span class="p">${</span><span class="k">this</span><span class="p">.</span><span class="nx">name</span><span class="p">}</span><span class="s2">!`</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">new</span> <span class="nx">HelloWorld</span><span class="p">(</span><span class="dl">'</span><span class="s1">John Doe</span><span class="dl">'</span><span class="p">).</span><span class="nx">print</span><span class="p">();</span></code></pre></figure>

<h2 id="heading-add-tsconfigjson">Add tsconfig.json</h2>

<p>Typically, to guide TypeScript compiler on how to generate JavaScript files,
add a TypeScript configuration file called <code class="language-plaintext highlighter-rouge">tsconfig.json</code> to the project.</p>

<p>The presence of a <code class="language-plaintext highlighter-rouge">tsconfig.json</code> file in a directory indicates that the directory is the root of a TypeScript project.
It specifies the root files and the compiler options required to compile the project.</p>

<p>Here for example, create a <code class="language-plaintext highlighter-rouge">tsconfig.json</code> file with some common options,
and tell compiler to convert <code class="language-plaintext highlighter-rouge">welcome.ts</code> to <code class="language-plaintext highlighter-rouge">generated/welcome.js</code>,
where everything in <code class="language-plaintext highlighter-rouge">generated</code> folder can be added to <code class="language-plaintext highlighter-rouge">.gitignore</code>.</p>

<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="p">{</span><span class="w">
  </span><span class="nl">"compilerOptions"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"outFile"</span><span class="p">:</span><span class="w"> </span><span class="s2">"app/assets/javascripts/generated/welcome.js"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"noImplicitAny"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
      </span><span class="nl">"noEmitOnError"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
      </span><span class="nl">"sourceMap"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
      </span><span class="nl">"target"</span><span class="p">:</span><span class="w"> </span><span class="s2">"es5"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"files"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
      </span><span class="s2">"app/assets/javascripts/welcome.ts"</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span></code></pre></figure>

<h2 id="heading-add-rake-build-task">Add Rake build task</h2>

<p>Finally create a new Rake task to compile to TypeScript files to JavaScript.
Note that this new task can be added to Rails' <code class="language-plaintext highlighter-rouge">rake assets:precompile</code> task,
so that every time <code class="language-plaintext highlighter-rouge">rake assets:precompile</code> is executed, <code class="language-plaintext highlighter-rouge">assets:tsc</code> will be run first.</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">namespace</span> <span class="ss">:assets</span> <span class="k">do</span>
  <span class="n">desc</span> <span class="s1">'Compile TypeScript Files'</span>
  <span class="n">task</span> <span class="ss">:tsc</span> <span class="k">do</span>
    <span class="nb">system</span><span class="p">(</span><span class="s1">'node_modules/.bin/tsc'</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="no">Rake</span><span class="o">::</span><span class="no">Task</span><span class="p">[</span><span class="s1">'assets:precompile'</span><span class="p">].</span><span class="nf">enhance</span> <span class="p">[</span><span class="s1">'assets:tsc'</span><span class="p">]</span></code></pre></figure>

<p>For example, add a <code class="language-plaintext highlighter-rouge">assets.rake</code> file to <code class="language-plaintext highlighter-rouge">lib/tasks</code> folder with the content above,
then call the following command to compile TypeScript.</p>

<blockquote>
  <p>rake assets:tsc</p>
</blockquote>

<h2 id="heading-demos">Demos</h2>

<p>A simple demo for this tutorial is hosted on <a href="https://github.com/yizeng/Demo-TypeScript-Support-to-Ruby-on-Rails">GitHub</a>.</p>

<p>The more complicated example that was actually applied to <a href="http://www.strafforts.com/">Strafforts</a>
can also be viewed on <a href="https://github.com/yizeng/strafforts">GitHub</a>,
which contains separate <code class="language-plaintext highlighter-rouge">tsconfig.json</code> files for compiling different pages/projects.</p>]]></content><author><name>Yi Zeng</name></author><category term="tutorials" /><category term="javascript" /><category term="typescript" /><category term="ruby on rails" /><category term="yarn" /><summary type="html"><![CDATA[How to add TypeScript support to Ruby on Rails in a quick and hacky way.]]></summary></entry><entry><title type="html">Use two different Pace.js progress bars on the same page</title><link href="http://yizeng.me/2017/04/15/use-two-different-pacejs-progress-bars-on-the-same-page/" rel="alternate" type="text/html" title="Use two different Pace.js progress bars on the same page" /><published>2017-04-15T00:00:00+02:00</published><updated>2017-04-15T00:00:00+02:00</updated><id>http://yizeng.me/2017/04/15/use-two-different-pacejs-progress-bars-on-the-same-page</id><content type="html" xml:base="http://yizeng.me/2017/04/15/use-two-different-pacejs-progress-bars-on-the-same-page/"><![CDATA[<p>I came across this problem while working on my little side project <a href="http://www.strafforts.com/">Strafforts</a>
(A Visualizer for Strava Estimated Best Efforts and Races),
that how to have two different <a href="http://github.hubspot.com/pace/docs/welcome/">Pace.js</a> progress bars on the same page within a single page application.</p>

<p>One progress bar is a global overlay that hides the entire application until the page is loaded,
while the other is a default progress bar when any Ajax is triggered without hiding any content.</p>

<p>After some Googling, I found <a href="https://github.com/HubSpot/pace/issues/135">this answer</a> works like a charm.
Hence I'm turning it into a simple working demo for anyone is interested.</p>

<ul class="toc" id="markdown-toc">
  <li><a href="#heading-demo" id="markdown-toc-heading-demo">Demo</a></li>
  <li><a href="#heading-add-first-progress-bar" id="markdown-toc-heading-add-first-progress-bar">Add first progress bar</a></li>
  <li><a href="#heading-add-second-progress-bar" id="markdown-toc-heading-add-second-progress-bar">Add second progress bar</a></li>
  <li><a href="#heading-make-them-working-together" id="markdown-toc-heading-make-them-working-together">Make them working together</a></li>
  <li><a href="#heading-demos-source-code" id="markdown-toc-heading-demos-source-code">Demo's source code</a></li>
</ul>

<h2 id="heading-demo">Demo</h2>

<iframe src="/assets/demo/2017-04-15-use-two-different-pacejs-progress-bars-on-the-same-page.html"></iframe>

<h2 id="heading-add-first-progress-bar">Add first progress bar</h2>

<p>First, add an ordinary Pace.js progress bar like usual.</p>

<ul>
  <li>Add references to <code class="language-plaintext highlighter-rouge">pace.min.js</code> and the theme CSS file.</li>
</ul>

<figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;head&gt;</span>
    ...
    <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"pace.min.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
    <span class="nt">&lt;link</span> <span class="na">href=</span><span class="s">"center-atom.css"</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;/head&gt;</span></code></pre></figure>

<ul>
  <li>Optionally, I want this one to be used as a global overlay,
which means nothing but this progress bar is shown before page has fully loaded.</li>
</ul>

<figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;head&gt;</span>
    ...
    <span class="nt">&lt;style&gt;</span>
        <span class="nt">body</span><span class="o">&gt;</span> <span class="nd">:not</span><span class="o">(</span><span class="nc">.pace</span><span class="o">),</span>
        <span class="nt">body</span><span class="nd">:before</span><span class="o">,</span>
        <span class="nt">body</span><span class="nd">:after</span> <span class="p">{</span>
            <span class="nl">-webkit-transition</span><span class="p">:</span> <span class="n">opacity</span> <span class="m">.4s</span> <span class="n">ease-in-out</span><span class="p">;</span>
            <span class="nl">-moz-transition</span><span class="p">:</span> <span class="n">opacity</span> <span class="m">.4s</span> <span class="n">ease-in-out</span><span class="p">;</span>
            <span class="nl">-o-transition</span><span class="p">:</span> <span class="n">opacity</span> <span class="m">.4s</span> <span class="n">ease-in-out</span><span class="p">;</span>
            <span class="nl">-ms-transition</span><span class="p">:</span> <span class="n">opacity</span> <span class="m">.4s</span> <span class="n">ease-in-out</span><span class="p">;</span>
            <span class="nl">transition</span><span class="p">:</span> <span class="n">opacity</span> <span class="m">.4s</span> <span class="n">ease-in-out</span>
        <span class="p">}</span>
        
        <span class="nt">body</span><span class="nd">:not</span><span class="o">(</span><span class="nc">.pace-done</span><span class="o">)&gt;</span> <span class="nd">:not</span><span class="o">(</span><span class="nc">.pace</span><span class="o">),</span>
        <span class="nt">body</span><span class="nd">:not</span><span class="o">(</span><span class="nc">.pace-done</span><span class="o">)</span><span class="nd">:before</span><span class="o">,</span>
        <span class="nt">body</span><span class="nd">:not</span><span class="o">(</span><span class="nc">.pace-done</span><span class="o">)</span><span class="nd">:after</span> <span class="p">{</span>
            <span class="nl">opacity</span><span class="p">:</span> <span class="m">0</span>
        <span class="p">}</span>
    <span class="nt">&lt;/style&gt;</span>
<span class="nt">&lt;/head&gt;</span></code></pre></figure>

<h2 id="heading-add-second-progress-bar">Add second progress bar</h2>

<p>Next step is to add another progress bar that will be shown inside application only,
when there are Ajax calls, state changes, etc.</p>

<figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;head&gt;</span>
    <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"pace.min.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
    <span class="nt">&lt;link</span> <span class="na">href=</span><span class="s">"center-atom.css"</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;link</span> <span class="na">href=</span><span class="s">"minimal.css"</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="nt">/&gt;</span><span class="c">&lt;!-- Add reference to a new Pace.js theme CSS. --&gt;</span>
    ...
<span class="nt">&lt;/head&gt;</span></code></pre></figure>

<h2 id="heading-make-them-working-together">Make them working together</h2>

<ul>
  <li>Set page states</li>
</ul>

<p>To indicate what the state the page is currently in and which progress bar should be shown,
add a class name on <code class="language-plaintext highlighter-rouge">body</code> element of the page.</p>

<p>For example, add <code class="language-plaintext highlighter-rouge">.content-loading</code> class to <code class="language-plaintext highlighter-rouge">body</code> in HTML markup,
which indicates the page hasn't been loaded and is in the initial state.</p>

<figure class="highlight"><pre><code class="language-html" data-lang="html">...
<span class="nt">&lt;body</span> <span class="na">class=</span><span class="s">"content-loading"</span><span class="nt">&gt;</span><span class="c">&lt;!-- Add class name for initial state. --&gt;</span>
    <span class="nt">&lt;button</span> <span class="na">onclick=</span><span class="s">"Pace.restart()"</span><span class="nt">&gt;</span>Trigger Ajax<span class="nt">&lt;/button&gt;</span>
    <span class="nt">&lt;button</span> <span class="na">onclick=</span><span class="s">"location.reload()"</span><span class="nt">&gt;</span>Reload Page<span class="nt">&lt;/button&gt;</span>
<span class="nt">&lt;/body&gt;</span></code></pre></figure>

<p>Then add a piece of JavaScript code to set page state (<code class="language-plaintext highlighter-rouge">.content-loading</code> class on <code class="language-plaintext highlighter-rouge">body</code>) to <code class="language-plaintext highlighter-rouge">.content-loaded</code>
when Pace.js finishes page loading.
Once this is executed, the in-app progress bar will be shown from then on for Ajax calls, etc.</p>

<figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;head&gt;</span>
    ...
    <span class="nt">&lt;script&gt;</span>
        <span class="nx">Pace</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">hide</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
            <span class="nb">document</span><span class="p">.</span><span class="nx">getElementsByTagName</span><span class="p">(</span><span class="dl">"</span><span class="s2">body</span><span class="dl">"</span><span class="p">)[</span><span class="mi">0</span><span class="p">].</span><span class="nx">classList</span><span class="p">.</span><span class="nx">remove</span><span class="p">(</span><span class="dl">'</span><span class="s1">content-loading</span><span class="dl">'</span><span class="p">);</span>
            <span class="nb">document</span><span class="p">.</span><span class="nx">getElementsByTagName</span><span class="p">(</span><span class="dl">"</span><span class="s2">body</span><span class="dl">"</span><span class="p">)[</span><span class="mi">0</span><span class="p">].</span><span class="nx">classList</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="dl">'</span><span class="s1">content-loaded</span><span class="dl">'</span><span class="p">);</span>
        <span class="p">});</span>
    <span class="nt">&lt;/script&gt;</span>
<span class="nt">&lt;/head&gt;</span></code></pre></figure>

<ul>
  <li>Wrap themes with unique class names</li>
</ul>

<p>Now the page has two states, one is the initial state where the first progress bar will be shown.
The other state is when page has been loaded,
the second progress bar will be shown for any Ajax calls, etc. within the application.</p>

<p>Therefore CSS themes need to be updated accordingly to reflect those two states.</p>

<p>If you are using CSS,
update all CSS rules in both themes to be under <code class="language-plaintext highlighter-rouge">.content-loading</code> or <code class="language-plaintext highlighter-rouge">.content-loaded</code>.
For example, <code class="language-plaintext highlighter-rouge">minimal.css</code> now looks like:</p>

<figure class="highlight"><pre><code class="language-scss" data-lang="scss"><span class="nc">.content-loaded</span> <span class="nc">.pace</span> <span class="p">{</span>
    <span class="na">-webkit-pointer-events</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span>
    <span class="nl">pointer-events</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span>
    <span class="na">-webkit-user-select</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span>
    <span class="na">-moz-user-select</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span>
    <span class="na">user-select</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.content-loaded</span> <span class="nc">.pace-inactive</span> <span class="p">{</span>
    <span class="nl">display</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.content-loaded</span> <span class="nc">.pace</span> <span class="nc">.pace-progress</span> <span class="p">{</span>
    <span class="nl">background</span><span class="p">:</span> <span class="mh">#29d</span><span class="p">;</span>
    <span class="nl">position</span><span class="p">:</span> <span class="nb">fixed</span><span class="p">;</span>
    <span class="nl">z-index</span><span class="p">:</span> <span class="m">2000</span><span class="p">;</span>
    <span class="nl">top</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
    <span class="nl">right</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span>
    <span class="nl">width</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span>
    <span class="nl">height</span><span class="p">:</span> <span class="m">2px</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>

<p>If you are using <a href="http://sass-lang.com/">Sass</a>, simply wrap themes with the class names like below:</p>

<figure class="highlight"><pre><code class="language-scss" data-lang="scss"><span class="c1">// center-atom.scss file</span>
<span class="nc">.content-loading</span> <span class="p">{</span>
    <span class="c1">// all theme styles.</span>
<span class="p">}</span>

<span class="c1">// minimal.scss file</span>
<span class="nc">.content-loaded</span> <span class="p">{</span>
    <span class="c1">// all theme styles.</span>
<span class="p">}</span></code></pre></figure>

<ul>
  <li>Update overlay styles</li>
</ul>

<p>If global overlay style in place,
the last step is to update it for <code class="language-plaintext highlighter-rouge">.content-loading</code> state only.
So that content in the application will still be visible during Ajax calls.</p>

<figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;head&gt;</span>
    ...
    <span class="nt">&lt;style&gt;</span>
        <span class="nc">.content-loading</span><span class="o">&gt;</span> <span class="nd">:not</span><span class="o">(</span><span class="nc">.pace</span><span class="o">),</span>
        <span class="nc">.content-loading</span><span class="nd">:before</span><span class="o">,</span>
        <span class="nc">.content-loading</span><span class="nd">:after</span> <span class="p">{</span>
            <span class="nl">-webkit-transition</span><span class="p">:</span> <span class="n">opacity</span> <span class="m">.4s</span> <span class="n">ease-in-out</span><span class="p">;</span>
            <span class="nl">-moz-transition</span><span class="p">:</span> <span class="n">opacity</span> <span class="m">.4s</span> <span class="n">ease-in-out</span><span class="p">;</span>
            <span class="nl">-o-transition</span><span class="p">:</span> <span class="n">opacity</span> <span class="m">.4s</span> <span class="n">ease-in-out</span><span class="p">;</span>
            <span class="nl">-ms-transition</span><span class="p">:</span> <span class="n">opacity</span> <span class="m">.4s</span> <span class="n">ease-in-out</span><span class="p">;</span>
            <span class="nl">transition</span><span class="p">:</span> <span class="n">opacity</span> <span class="m">.4s</span> <span class="n">ease-in-out</span>
        <span class="p">}</span>
        
        <span class="nc">.content-loading</span><span class="nd">:not</span><span class="o">(</span><span class="nc">.pace-done</span><span class="o">)&gt;</span> <span class="nd">:not</span><span class="o">(</span><span class="nc">.pace</span><span class="o">),</span>
        <span class="nc">.content-loading</span><span class="nd">:not</span><span class="o">(</span><span class="nc">.pace-done</span><span class="o">)</span><span class="nd">:before</span><span class="o">,</span>
        <span class="nc">.content-loading</span><span class="nd">:not</span><span class="o">(</span><span class="nc">.pace-done</span><span class="o">)</span><span class="nd">:after</span> <span class="p">{</span>
            <span class="nl">opacity</span><span class="p">:</span> <span class="m">0</span>
        <span class="p">}</span>
    <span class="nt">&lt;/style&gt;</span>
<span class="nt">&lt;/head&gt;</span></code></pre></figure>

<h2 id="heading-demos-source-code">Demo's source code</h2>

<p>See demo's on JSFiddle <a href="https://jsfiddle.net/yizeng/39oar0bw/">here</a>.</p>

<script async="" src="//jsfiddle.net/yizeng/39oar0bw/embed/"></script>]]></content><author><name>Yi Zeng</name></author><category term="tutorials" /><category term="javascript" /><summary type="html"><![CDATA[A demo on how to use two different Pace.js progress bars on the same page.]]></summary></entry></feed>