<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.4">Jekyll</generator><link href="https://rpereira.pt/feed.xml" rel="self" type="application/atom+xml" /><link href="https://rpereira.pt/" rel="alternate" type="text/html" /><updated>2025-12-26T17:58:14+00:00</updated><id>https://rpereira.pt/feed.xml</id><title type="html">Rui on Rails</title><subtitle>Strong opinions and thoughts on building software and tech.</subtitle><author><name>Rui Afonso Pereira</name></author><entry><title type="html">Want feedback? Seek it</title><link href="https://rpereira.pt/leadership/want-feedback-seek-it/" rel="alternate" type="text/html" title="Want feedback? Seek it" /><published>2023-01-19T08:30:00+00:00</published><updated>2023-01-19T08:30:00+00:00</updated><id>https://rpereira.pt/leadership/want-feedback-seek-it</id><content type="html" xml:base="https://rpereira.pt/leadership/want-feedback-seek-it/"><![CDATA[<p>Soliciting criticism is crucial for growth. If you feel you’re not getting
enough feedback, send this message to your peers:</p>

<blockquote>
  <p>Hi, I wanted to reach out and ask for your feedback on my work. 1+
constructive points would be much appreciated. Thanks!</p>
</blockquote>

<p>Make this a habit and build a track record of seeking feedback.</p>

<p>Now embrace the discomfort — it’s imperative to listen with intent to understand
and not to react defensively. Focus on understanding the feedback, and use it to
learn and grow. Additionally, to encourage more feedback, demonstrate your
appreciation by making visible changes or showing progress based on the feedback
received.</p>]]></content><author><name>Rui Afonso Pereira</name></author><category term="Leadership" /><category term="growth" /><summary type="html"><![CDATA[Soliciting criticism is crucial for growth. If you feel you’re not getting enough feedback, send this message to your peers:]]></summary></entry><entry><title type="html">Setup Docker for Go development with hot reload</title><link href="https://rpereira.pt/programming/setup-docker-for-go-development/" rel="alternate" type="text/html" title="Setup Docker for Go development with hot reload" /><published>2020-02-15T07:33:13+00:00</published><updated>2020-02-15T07:33:13+00:00</updated><id>https://rpereira.pt/programming/setup-docker-for-go-development</id><content type="html" xml:base="https://rpereira.pt/programming/setup-docker-for-go-development/"><![CDATA[<p>For the purpose of this article, we shall consider the following Gin webserver
written in Go that responds with <code class="language-plaintext highlighter-rouge">{ "message": "pong" }</code> for a <code class="language-plaintext highlighter-rouge">GET /ping</code>
request.</p>

<figure class="highlight"><pre><code class="language-go" data-lang="go"><span class="c">// main.go</span>
<span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="s">"github.com/gin-gonic/gin"</span>

<span class="k">func</span> <span class="n">setupRouter</span><span class="p">()</span> <span class="o">*</span><span class="n">gin</span><span class="o">.</span><span class="n">Engine</span> <span class="p">{</span>
  <span class="n">r</span> <span class="o">:=</span> <span class="n">gin</span><span class="o">.</span><span class="n">Default</span><span class="p">()</span>

  <span class="n">r</span><span class="o">.</span><span class="n">GET</span><span class="p">(</span><span class="s">"/ping"</span><span class="p">,</span> <span class="k">func</span><span class="p">(</span><span class="n">c</span> <span class="o">*</span><span class="n">gin</span><span class="o">.</span><span class="n">Context</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">c</span><span class="o">.</span><span class="n">JSON</span><span class="p">(</span><span class="m">200</span><span class="p">,</span> <span class="n">gin</span><span class="o">.</span><span class="n">H</span><span class="p">{</span>
      <span class="s">"message"</span><span class="o">:</span> <span class="s">"pong"</span><span class="p">,</span>
    <span class="p">})</span>
  <span class="p">})</span>

  <span class="k">return</span> <span class="n">r</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="n">r</span> <span class="o">:=</span> <span class="n">setupRouter</span><span class="p">()</span>
  <span class="n">r</span><span class="o">.</span><span class="n">Run</span><span class="p">()</span>
<span class="p">}</span></code></pre></figure>

<h2 id="setting-up-docker">Setting up Docker</h2>

<p>We shall continue by creating a Docker image and a docker-compose file to build
and start the Gin webserver.</p>

<figure class="highlight"><pre><code class="language-dockerfile" data-lang="dockerfile"><span class="c"># Dockerfile</span>
<span class="k">FROM</span><span class="s"> golang:latest</span>

<span class="k">RUN </span><span class="nb">mkdir</span> /app
<span class="k">WORKDIR</span><span class="s"> /app</span>
<span class="k">ADD</span><span class="s"> . /app</span>

<span class="k">RUN </span>go get github.com/gin-gonic/gin
<span class="k">RUN </span>go build main.go
<span class="k">CMD</span><span class="s"> ["./main"]</span></code></pre></figure>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="c1"># docker-compose</span>
<span class="nn">---</span>
<span class="na">version</span><span class="pi">:</span> <span class="s2">"</span><span class="s">3.7"</span>
<span class="na">services</span><span class="pi">:</span>
  <span class="na">app</span><span class="pi">:</span>
    <span class="na">build</span><span class="pi">:</span> <span class="s">.</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">hot-reloading-app</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s2">"</span><span class="s">8080:8080"</span>
    <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">./:/app</span>
    <span class="na">environment</span><span class="pi">:</span>
      <span class="na">PORT</span><span class="pi">:</span> <span class="s2">"</span><span class="s">8080"</span></code></pre></figure>

<p>The webserver can be started as follows:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ docker-compose up
Starting app_1 ... done
Attaching to app_1
app_1  | 2020/02/15 08:20:13 Running build command!
app_1  | 2020/02/15 08:20:13 Build ok.
...
app_1  | 2020/02/15 08:20:13 stdout: [GIN-debug] Listening and serving HTTP on :8080
</code></pre></div></div>

<p>And by sending a <code class="language-plaintext highlighter-rouge">GET /ping</code> request to the webserver we are expecting to
receive a <code class="language-plaintext highlighter-rouge">"pong"</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ curl http://localhost:8080/ping
{"message":"pong"}
</code></pre></div></div>

<p>However, after changing the response string to the <code class="language-plaintext highlighter-rouge">GET /ping</code> request you will
observe that the actual response did not change. This is expected and, because
the Docker image itself did not change, it is still running the most up-to-date
binary that does not include the change made to the response. This effectively
means that the image has to be stopped and rebuilt so that the changes made to
the webserver are applied.</p>

<h2 id="setting-up-docker-with-hot-reloading">Setting up Docker with hot reloading</h2>

<p>Having to manually rebuild the Docker image every time a change is made to the
source code is simply not an option.
<a href="https://github.com/githubnemo/CompileDaemon">CompileDaemon</a>, as the name
implies, is a daemon for Go that watches all <code class="language-plaintext highlighter-rouge">.go</code> files in a given directory
and invokes <code class="language-plaintext highlighter-rouge">go build</code> when a modification is detected.</p>

<p>We shall proceed by changing the <code class="language-plaintext highlighter-rouge">Dockerfile</code> to install the <code class="language-plaintext highlighter-rouge">CompileDaemon</code>
package and use it to build and start the webserver.</p>

<figure class="highlight"><pre><code class="language-dockerfile" data-lang="dockerfile"><span class="c"># Dockerfile</span>
<span class="k">FROM</span><span class="s"> golang:latest</span>

<span class="k">RUN </span><span class="nb">mkdir</span> /app
<span class="k">WORKDIR</span><span class="s"> /app</span>
<span class="k">ADD</span><span class="s"> . /app</span>

<span class="k">RUN </span>go get github.com/githubnemo/CompileDaemon
<span class="k">RUN </span>go get github.com/gin-gonic/gin

<span class="k">ENTRYPOINT</span><span class="s"> CompileDaemon --build="go build main.go" --command=./main</span></code></pre></figure>

<p>The next step is to rebuild the Docker image and start the server using
<code class="language-plaintext highlighter-rouge">docker-compose up</code>. Go ahead and verify that the webserver is running as
described in the first paragraph. Now, modify the response to some other string
and observe the Docker image logs:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>...
api_1  | 2020/02/15 07:24:46 Build ok.
api_1  | 2020/02/15 07:24:46 Hard stopping the current process..
api_1  | 2020/02/15 07:24:46 Restarting the given command.
...
api_1  | 2020/02/15 07:24:46 stdout: [GIN-debug] Listening and serving HTTP on :8080
api_1  | 2020/02/15 07:24:50 stdout: [GIN] 2020/02/15 - 07:24:50 | 200 |       1.015ms |      172.21.0.1 | GET      /ping
</code></pre></div></div>

<p>Finally, by sending a <code class="language-plaintext highlighter-rouge">GET /ping</code> request to the webserver we are expecting
<em>not to</em> receive a <code class="language-plaintext highlighter-rouge">"pong"</code> but rather the updated string.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ curl http://localhost:8080/ping
{"message":"pong pong pong!"}
</code></pre></div></div>

<p>We have now successfully set up Gin webserver in Docker that automatically
reloads changes when files are changed. 🙌</p>

<p>You can grab the final source code from here: <a href="https://gist.github.com/rpereira/c52190d18bc41f2ec016c3f15b059e5f">https://gist.github.com/rpereira/c52190d18bc41f2ec016c3f15b059e5f</a></p>]]></content><author><name>Rui Afonso Pereira</name></author><category term="Programming" /><category term="docker" /><category term="go" /><summary type="html"><![CDATA[For the purpose of this article, we shall consider the following Gin webserver written in Go that responds with { "message": "pong" } for a GET /ping request.]]></summary></entry><entry><title type="html">Rails caching in RSpec</title><link href="https://rpereira.pt/programming/rails-caching-in-rspec/" rel="alternate" type="text/html" title="Rails caching in RSpec" /><published>2019-12-22T12:05:02+00:00</published><updated>2019-12-22T12:05:02+00:00</updated><id>https://rpereira.pt/programming/rails-caching-in-rspec</id><content type="html" xml:base="https://rpereira.pt/programming/rails-caching-in-rspec/"><![CDATA[<p>By default, caching in a Rails application is only enabled in a production
environment. To be able to test application logic for caching there are at least
two immediate solutions, being (1) enable caching for all tests and (2) enable
cache for individual tests. For this article, we shall consider the latter.</p>

<p>Assuming the default caching configuration for test environment</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">config</span><span class="p">.</span><span class="nf">action_controller</span><span class="p">.</span><span class="nf">perform_caching</span> <span class="o">=</span> <span class="kp">false</span>
<span class="n">config</span><span class="p">.</span><span class="nf">cache_store</span> <span class="o">=</span> <span class="ss">:null_store</span></code></pre></figure>

<p>the strategy to enable it for individual tests consists of stubbing
<code class="language-plaintext highlighter-rouge">Rails.cache</code> and clearing it before each example, like so:</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">RSpec</span><span class="p">.</span><span class="nf">describe</span> <span class="no">User</span><span class="p">,</span> <span class="ss">type: :model</span> <span class="k">do</span>
  <span class="n">let</span><span class="p">(</span><span class="ss">:memory_store</span><span class="p">)</span> <span class="p">{</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">Cache</span><span class="p">.</span><span class="nf">lookup_store</span><span class="p">(</span><span class="ss">:memory_store</span><span class="p">)</span> <span class="p">}</span>
  <span class="n">let</span><span class="p">(</span><span class="ss">:cache</span><span class="p">)</span> <span class="p">{</span> <span class="no">Rails</span><span class="p">.</span><span class="nf">cache</span> <span class="p">}</span>

  <span class="n">before</span> <span class="k">do</span>
    <span class="n">allow</span><span class="p">(</span><span class="no">Rails</span><span class="p">).</span><span class="nf">to</span> <span class="n">receive</span><span class="p">(</span><span class="ss">:cache</span><span class="p">).</span><span class="nf">and_return</span><span class="p">(</span><span class="n">memory_store</span><span class="p">)</span>
    <span class="no">Rails</span><span class="p">.</span><span class="nf">cache</span><span class="p">.</span><span class="nf">clear</span>
  <span class="k">end</span>

  <span class="n">it</span> <span class="s1">'fetches last_activity_at from cache'</span> <span class="k">do</span>
    <span class="n">user</span> <span class="o">=</span> <span class="n">create</span><span class="p">(</span><span class="ss">:user</span><span class="p">)</span>
    <span class="n">cache_key</span> <span class="o">=</span> <span class="s2">"user:last_activity_at:</span><span class="si">#{</span><span class="n">user</span><span class="p">.</span><span class="nf">id</span><span class="si">}</span><span class="s2">"</span>

    <span class="n">expect</span><span class="p">(</span><span class="no">Rails</span><span class="p">.</span><span class="nf">cache</span><span class="p">.</span><span class="nf">exist?</span><span class="p">(</span><span class="n">cache_key</span><span class="p">)).</span><span class="nf">to</span> <span class="n">be_falsy</span>

    <span class="n">now</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">now</span>
    <span class="no">Rails</span><span class="p">.</span><span class="nf">cache</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">cache_key</span><span class="p">,</span> <span class="n">now</span><span class="p">)</span>
    <span class="n">expect</span><span class="p">(</span><span class="no">Rails</span><span class="p">.</span><span class="nf">cache</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="n">cache_key</span><span class="p">)).</span><span class="nf">to</span> <span class="n">eq</span><span class="p">(</span><span class="n">now</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span></code></pre></figure>]]></content><author><name>Rui Afonso Pereira</name></author><category term="Programming" /><category term="testing" /><category term="rails" /><category term="rspec" /><summary type="html"><![CDATA[By default, caching in a Rails application is only enabled in a production environment. To be able to test application logic for caching there are at least two immediate solutions, being (1) enable caching for all tests and (2) enable cache for individual tests. For this article, we shall consider the latter.]]></summary></entry><entry><title type="html">has_many, but with limits in Rails</title><link href="https://rpereira.pt/programming/has-many-but-with-limits-in-rails/" rel="alternate" type="text/html" title="has_many, but with limits in Rails" /><published>2019-11-17T08:50:34+00:00</published><updated>2019-11-17T08:50:34+00:00</updated><id>https://rpereira.pt/programming/has-many-but-with-limits-in-rails</id><content type="html" xml:base="https://rpereira.pt/programming/has-many-but-with-limits-in-rails/"><![CDATA[<p>For the purpose of this article, we shall consider a Rails application that
includes a model for organizations and a model for users. Each organization can
have many users. Furthermore, we shall limit the number of users and
organization can have.</p>

<p>There are a handful of strategies to accomplish this, but today let’s explore
how association callbacks can solve the situation described above. These
callbacks hook into the life cycle of Active Record objects, allowing to work
with those objects at various points. More specifically, the <code class="language-plaintext highlighter-rouge">before_add</code>
callback can be used to ensure the number of users in an organization is bellow
the limit, preventing the object from being saved to the database if not.</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
  <span class="n">belongs_to</span> <span class="ss">:organization</span>
<span class="k">end</span></code></pre></figure>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">Organization</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
  <span class="no">MAX_USERS_IN_ORGANIZATION</span> <span class="o">=</span> <span class="mi">10</span>

  <span class="n">has_many</span> <span class="ss">:users</span><span class="p">,</span> <span class="ss">before_add: :check_users_limit</span>

  <span class="kp">private</span>
    <span class="k">def</span> <span class="nf">check_users_limit</span><span class="p">(</span><span class="n">_user</span><span class="p">)</span>
      <span class="k">raise</span> <span class="no">UserLimitExceeded</span> <span class="k">if</span> <span class="n">users</span><span class="p">.</span><span class="nf">size</span> <span class="o">&gt;=</span> <span class="no">MAX_USERS_IN_ORGANIZATION</span>
    <span class="k">end</span>
<span class="k">end</span></code></pre></figure>

<p>By causing the <code class="language-plaintext highlighter-rouge">before_add</code> callback to throw an exception, the user object does
not get added to the collection.</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">OrganizationTest</span> <span class="o">&lt;</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">TestCase</span>
  <span class="nb">test</span> <span class="s1">'user limits for organization'</span> <span class="k">do</span>
    <span class="n">org</span> <span class="o">=</span> <span class="n">create</span><span class="p">(</span><span class="ss">:organisation</span><span class="p">)</span>

    <span class="n">org</span><span class="p">.</span><span class="nf">users</span> <span class="o">=</span> <span class="n">create_list</span><span class="p">(</span><span class="ss">:user</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
    <span class="n">assert_equal</span> <span class="mi">10</span><span class="p">,</span> <span class="n">org</span><span class="p">.</span><span class="nf">users</span><span class="p">.</span><span class="nf">size</span>

    <span class="n">assert_raises</span> <span class="no">UserLimitExceeded</span> <span class="k">do</span>
      <span class="n">org</span><span class="p">.</span><span class="nf">users</span> <span class="o">&lt;&lt;</span> <span class="n">create</span><span class="p">(</span><span class="ss">:user</span><span class="p">)</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span></code></pre></figure>

<p>However, this approach comes with a caveat. As association callbacks are
triggered by events in the life cycle of a collection, these are called only
when the associated objects are added or removed through the association
collection.</p>

<p>The following triggers the <code class="language-plaintext highlighter-rouge">before_add</code> callback:</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">irb</span><span class="p">(</span><span class="n">main</span><span class="p">):</span><span class="mo">001</span><span class="p">:</span><span class="mi">0</span><span class="o">&gt;</span> <span class="n">org</span> <span class="o">=</span> <span class="no">Organization</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span><span class="ss">name: </span><span class="s2">"Example"</span><span class="p">)</span>
<span class="o">=&gt;</span> <span class="c1">#&lt;Organization id: 1, name: "Example", created_at: "2019-11-17 10:33:45", updated_at: "2019-11-17 10:33:45"&gt;</span>
<span class="n">irb</span><span class="p">(</span><span class="n">main</span><span class="p">):</span><span class="mo">002</span><span class="p">:</span><span class="mi">0</span><span class="o">&gt;</span> <span class="mi">11</span><span class="p">.</span><span class="nf">times</span> <span class="p">{</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span> <span class="n">org</span><span class="p">.</span><span class="nf">users</span> <span class="o">&lt;&lt;</span> <span class="no">User</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span><span class="ss">name: </span><span class="s2">"User </span><span class="si">#{</span><span class="n">i</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="p">}</span>
<span class="no">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
        <span class="mi">4</span><span class="p">:</span> <span class="n">from</span> <span class="p">(</span><span class="n">irb</span><span class="p">):</span><span class="mi">3</span>
        <span class="mi">3</span><span class="p">:</span> <span class="n">from</span> <span class="p">(</span><span class="n">irb</span><span class="p">):</span><span class="mi">3</span><span class="ss">:in</span> <span class="sb">`times'
        2: from (irb):3:in `</span><span class="n">block</span> <span class="k">in</span> <span class="n">irb_binding</span><span class="s1">'
        1: from app/models/organization.rb:13:in `check_users_limit'</span>
<span class="no">UserLimitExceeded</span> <span class="p">(</span><span class="no">UserLimitExceeded</span><span class="p">)</span>
<span class="n">irb</span><span class="p">(</span><span class="n">main</span><span class="p">):</span><span class="mo">003</span><span class="p">:</span><span class="mi">0</span><span class="o">&gt;</span> <span class="n">org</span><span class="p">.</span><span class="nf">users</span><span class="p">.</span><span class="nf">size</span>
<span class="o">=&gt;</span> <span class="mi">10</span></code></pre></figure>

<p>On the othet hand, the following does not trigger the <code class="language-plaintext highlighter-rouge">before_add</code> callback:</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">irb</span><span class="p">(</span><span class="n">main</span><span class="p">):</span><span class="mo">004</span><span class="p">:</span><span class="mi">0</span><span class="o">&gt;</span> <span class="no">User</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span><span class="ss">name: </span><span class="s2">"John Doe"</span><span class="p">,</span> <span class="ss">organization: </span><span class="n">org</span><span class="p">)</span>
<span class="o">=&gt;</span> <span class="c1">#&lt;User id: 11, organization_id: 1, name: "John Doe", email: nil, created_at: "2019-11-17 10:37:28", updated_at: "2019-11-17 10:37:28"&gt;</span>
<span class="n">irb</span><span class="p">(</span><span class="n">main</span><span class="p">):</span><span class="mo">005</span><span class="p">:</span><span class="mi">0</span><span class="o">&gt;</span> <span class="n">org</span><span class="p">.</span><span class="nf">reload</span>
<span class="n">irb</span><span class="p">(</span><span class="n">main</span><span class="p">):</span><span class="mo">006</span><span class="p">:</span><span class="mi">0</span><span class="o">&gt;</span> <span class="n">org</span><span class="p">.</span><span class="nf">users</span><span class="p">.</span><span class="nf">size</span>
<span class="o">=&gt;</span> <span class="mi">11</span></code></pre></figure>]]></content><author><name>Rui Afonso Pereira</name></author><category term="Programming" /><category term="rails" /><summary type="html"><![CDATA[For the purpose of this article, we shall consider a Rails application that includes a model for organizations and a model for users. Each organization can have many users. Furthermore, we shall limit the number of users and organization can have.]]></summary></entry><entry><title type="html">Rails multicolumn unique index allowing null or empty values</title><link href="https://rpereira.pt/programming/rails-multicolumn-unique-index-allowing-null-or-empty-values/" rel="alternate" type="text/html" title="Rails multicolumn unique index allowing null or empty values" /><published>2019-09-14T09:01:17+00:00</published><updated>2019-09-14T09:01:17+00:00</updated><id>https://rpereira.pt/programming/rails-multicolumn-unique-index-allowing-null-or-empty-values</id><content type="html" xml:base="https://rpereira.pt/programming/rails-multicolumn-unique-index-allowing-null-or-empty-values/"><![CDATA[<p>A Rails application can make use of <a href="https://guides.rubyonrails.org/active_record_validations.html#uniqueness">uniqueness
validations</a> to detect duplicated records. However, this
is not enough to ensure data integrity. Constraining the values allowed by your
application at the database-level, rather than at the application-level, is a
more robust way of ensuring your data stays sane.</p>

<p>Database indexes can be used to enforce uniqueness of a column’s value, or the
uniqueness of the combined values of more than one column. It might be the case
that the requirements at hand specify that one of the columns can be <code class="language-plaintext highlighter-rouge">null</code> or
an empty string.</p>

<p>In Rails, this can be done as follows:</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">IndexUsersOnEmailAndUserDirectoryId</span>
  <span class="n">add_index</span> <span class="ss">:users</span><span class="p">,</span> <span class="p">[</span><span class="ss">:email</span><span class="p">,</span> <span class="ss">:user_directory_id</span><span class="p">],</span>
                    <span class="ss">unique: </span><span class="kp">true</span><span class="p">,</span>
                    <span class="ss">where: </span><span class="s2">"(email IS NOT NULL) OR (email != '')"</span>
<span class="k">end</span></code></pre></figure>

<p>Note that <code class="language-plaintext highlighter-rouge">null</code> values are not considered equal.</p>]]></content><author><name>Rui Afonso Pereira</name></author><category term="Programming" /><category term="rails" /><category term="postgresql" /><summary type="html"><![CDATA[A Rails application can make use of uniqueness validations to detect duplicated records. However, this is not enough to ensure data integrity. Constraining the values allowed by your application at the database-level, rather than at the application-level, is a more robust way of ensuring your data stays sane.]]></summary></entry><entry><title type="html">Signing your work on Git</title><link href="https://rpereira.pt/workflow/signing-your-work-on-git/" rel="alternate" type="text/html" title="Signing your work on Git" /><published>2016-04-09T09:56:09+00:00</published><updated>2016-04-09T09:56:09+00:00</updated><id>https://rpereira.pt/workflow/signing-your-work-on-git</id><content type="html" xml:base="https://rpereira.pt/workflow/signing-your-work-on-git/"><![CDATA[<h2 id="why-is-it-important">Why Is It Important?</h2>

<p>A strict policy of signing all commits could prevent someone committing as you
(perhaps with <code class="language-plaintext highlighter-rouge">GIT_COMMITTER_NAME</code> and <code class="language-plaintext highlighter-rouge">GIT_COMMITTER_EMAIL</code>) from fully blaming
you for a change. You can verify signatures using <code class="language-plaintext highlighter-rouge">git log</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ git log --show-signature
commit e80c6f611429db4e437a377d7b1b76c167594dcd
gpg: Signature made Sat Apr  9 11:19:58 2016 WEST using RSA key ID 6C1EEE05
gpg: Good signature from "Rui Afonso Pereira &lt;rap_2@fake.com&gt;" [ultimate]
gpg:                 aka "Rui Afonso Pereira &lt;rap@fake.com&gt;" [ultimate]
Author: Rui Afonso Pereira &lt;rap@fake.com&gt;
Date:   Sat Apr 9 11:19:40 2016 +0100
</code></pre></div></div>

<p>This article shows how to use public-key cryptography to sign git commits.</p>

<h2 id="getting-started">Getting Started</h2>

<p>This article relies on GNU Privacy Guard (GnuPG), which is a tool for secure
communication. It is a complete and free implementation of the OpenPGP
standard as defined by <a href="http://www.ietf.org/rfc/rfc4880.txt">RFC4880</a>, also known as PGP — short for <a href="https://en.wikipedia.org/wiki/Pretty_Good_Privacy">Pretty Good
Privacy</a>.</p>

<p>We shall start by installing the GnuPG tool. On macOS, you can grab it using
Homebrew:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew install gpg2
</code></pre></div></div>

<p>Furthermore, this binary can be aliased as <code class="language-plaintext highlighter-rouge">gpg</code>.</p>

<p>In other *NIX systems, either <code class="language-plaintext highlighter-rouge">gpg</code> or <code class="language-plaintext highlighter-rouge">gnupg</code> is likely already installed. From
this point on, due to the different ways that systems refer to the binary, I’m
going to address it as <code class="language-plaintext highlighter-rouge">gpg</code>.</p>

<h2 id="make-your-keys">Make Your Keys</h2>

<p>To use the GnuPG system, you’ll need a public key and a private key, also known
together as a keypair. A public key is available to many, whereas the private
key must be kept secret. <strong>You should never share your private key with anyone,
under any circumstances.</strong></p>

<p>If you don’t have an existing keypair, let’s proceed by generating one.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ gpg --gen-key
Please select what kind of key you want:
  (1) RSA and RSA (default)
  (2) DSA and Elgamal
  (3) DSA (sign only)
  (4) RSA (sign only)
Your selection?
</code></pre></div></div>

<p>Let’s start by pressing <code class="language-plaintext highlighter-rouge">Enter</code> and accepting <code class="language-plaintext highlighter-rouge">(1) RSA and RSA (default)</code> as
default.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>RSA keys may be between 1024 and 8192 bits long.
What keysize do you want? (2048)
</code></pre></div></div>

<p>You should type <code class="language-plaintext highlighter-rouge">4096</code> here.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Requested keysize is 4096 bits
Please specify how long the key should be valid.
        0 = key does not expire
      &lt;n&gt;  = key expires in n days
      &lt;n&gt;w = key expires in n weeks
      &lt;n&gt;m = key expires in n months
      &lt;n&gt;y = key expires in n years
Key is valid for? (0)
</code></pre></div></div>

<p>I do not want to bother with refreshing my key regularly, so mine never expires.</p>

<p>Then, we can move on to the next step.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>GnuPG needs to construct a user ID to identify your key.

Real name: Rui Afonso Pereira
Email address: rap@fake.com
Comment:
You selected this USER-ID:
    "Rui Afonso Pereira &lt;rap@fake.com&gt;"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit?
</code></pre></div></div>

<p>Now take the time to review the information and press <code class="language-plaintext highlighter-rouge">o</code> for <code class="language-plaintext highlighter-rouge">Okay</code>.</p>

<p>Finally, GnuPG needs a passphrase to protect the primary and subordinate private
keys that you keep in your possession.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>You need a Passphrase to protect your private key.

Enter passphrase:
</code></pre></div></div>

<p>A good passphrase is crucial to the secure use of GnuPG so it should be
carefully chosen.</p>

<h2 id="adding-identities">Adding Identities</h2>

<p>We shall consider having multiple email addresses: a work email and a personal
email. It would be tedious if we had to go through this whole process for each.
You can easily edit your key to add another user ID:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ gpg --edit-key rap@fake.com

Secret key is available.

pub  2048R/6C1EEE05  created: 2016-04-08  expires: never       usage: SC
                 trust: ultimate      validity: ultimate
sub  2048R/8C44BD6A  created: 2016-04-08  expires: never       usage: E
[ultimate] (1)  Rui Afonso Pereira &lt;rap@fake.com&gt;

gpg&gt;
</code></pre></div></div>

<p>In this prompt, you can type <code class="language-plaintext highlighter-rouge">help</code> for more information. To add a new user ID,
type <code class="language-plaintext highlighter-rouge">adduid</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gpg&gt; adduid
Real name: Rui Afonso Pereira
Email address: rap_2@fake.com
Comment:
You selected this USER-ID:
    "Rui Afonso Pereira &lt;rap_2@fake.com&gt;"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o

You need a passphrase to unlock the secret key for
user: "Rui Afonso Pereira &lt;rap@fake.com&gt;"
2048-bit RSA key, ID 6C1EEE05, created 2016-04-08


pub  2048R/6C1EEE05  created: 2016-04-08  expires: never       usage: SC
                     trust: ultimate      validity: ultimate
sub  2048R/8C44BD6A  created: 2016-04-08  expires: never       usage: E
[ultimate] (1). Rui Afonso Pereira &lt;rap_2@fake.com&gt;
[ultimate] (2)  Rui Afonso Pereira &lt;rap@fake.com&gt;

gpg&gt; save
</code></pre></div></div>

<p>The key now has two UIDs attached.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ gpg --list-keys 6C1EEE05
pub   2048R/6C1EEE05 2016-04-08
uid       [ultimate] Rui Afonso Pereira &lt;rap_2@fake.com&gt;
uid       [ultimate] Rui Afonso Pereira &lt;rap@fake.com&gt;
sub   2048R/8C44BD6A 2016-04-08
</code></pre></div></div>

<h2 id="exporting-a-public-key">Exporting a Public Key</h2>

<p>To add your public key to your development platform, such as GitHub, you must
first export it. To do so, paste the text below, substituting in the GPG key ID
you’d like to use. In this example, the GPG key ID is <code class="language-plaintext highlighter-rouge">6C1EEE05</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ gpg --armor --export 6C1EEE05
# Prints the GPG key, in ASCII armor format
</code></pre></div></div>

<p>You shall now proceed by <a href="https://help.github.com/articles/adding-a-new-gpg-key-to-your-github-account">adding the GPG key to your GitHub
account</a>.</p>

<h2 id="signing-commits">Signing Commits</h2>

<p>We should now configure git to automatically gpgsign commits. This consists of
pointing git to your signing key ID, and then enabling automatic signing of
git commits.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ git config --global user.signingkey &lt;YOUR-SIGNING-KEY-PUB-ID&gt;
$ git config --global commit.gpgsign true
</code></pre></div></div>

<p>Furthermore, since we are using the <code class="language-plaintext highlighter-rouge">gpg2</code> binary on macOS, we should also tell
this to Git:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ git config --global gpg.program gpg2
</code></pre></div></div>

<p>Finally, from now on, every commit will be <em>automatically</em> signed. However, it
is still required to insert the passphrase every single time.</p>

<h2 id="automatic-commit-signing-on-macos">Automatic Commit Signing on macOS</h2>

<p>For security reasons, GnuPG always requires the passphrase every time it needs
to sign something. This effectively means that the passphrase is required on
every single git commit. If this sounds like a lot of work to you, we can
automate the process, in a clear trade-off between security and convenience.</p>

<p>We shall start by installing the following binaries:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ brew install gpg-agent pinentry-mac
</code></pre></div></div>

<p>Now navigate to your <code class="language-plaintext highlighter-rouge">GPGHOMEDIR</code>, which is <code class="language-plaintext highlighter-rouge">$HOME/.gnupg</code> by default, and add
the following to your <code class="language-plaintext highlighter-rouge">gpg.conf</code> file:</p>

<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Uncomment within config (or add this line)
</span><span class="n">use</span>-<span class="n">agent</span>

<span class="c"># This silences the "you need a passphrase" message once the passphrase
# handling is all set.
</span><span class="n">batch</span>
</code></pre></div></div>

<p>While inside the same directory, let’s configure our <code class="language-plaintext highlighter-rouge">gpg-agent.conf</code>:</p>

<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Enables GPG to find gpg-agent
</span><span class="n">use</span>-<span class="n">standard</span>-<span class="n">socket</span>

<span class="c"># Connects gpg-agent to the macOS keychain via the brew-installed
# pinentry program from GPGtools. This is the macOS magic, allowing
# the gpg key's passphrase to be stored in the login keychain,
# enabling automatic key signing.
</span><span class="n">pinentry</span>-<span class="n">program</span> /<span class="n">usr</span>/<span class="n">local</span>/<span class="n">bin</span>/<span class="n">pinentry</span>-<span class="n">mac</span>
</code></pre></div></div>

<p>Now, it’s time for the real trick in this whole process. For GPG to find the
<code class="language-plaintext highlighter-rouge">gpg-agent</code>, the later must be running, and there must be an environment
variable pointing GPG to its socket. The following will either start <code class="language-plaintext highlighter-rouge">gpg-agent</code>
or set up the <code class="language-plaintext highlighter-rouge">GPG_AGENT_INFO</code> variable if it’s already running. You should add
this script to your <code class="language-plaintext highlighter-rouge">.bash_profile</code> or <code class="language-plaintext highlighter-rouge">.zprofile</code> so that it starts for every
shell.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="o">[[</span> <span class="nt">-f</span> ~/.gnupg/.gpg-agent-info <span class="o">]]</span> <span class="o">&amp;&amp;</span> <span class="o">[[</span> <span class="nt">-n</span> <span class="s2">"</span><span class="si">$(</span>pgrep gpg-agent<span class="si">)</span><span class="s2">"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
  </span><span class="nb">source</span> ~/.gnupg/.gpg-agent-info
  <span class="nb">export </span>GPG_AGENT_INFO
<span class="k">else
  </span><span class="nb">eval</span> <span class="si">$(</span>gpg-agent <span class="nt">--daemon</span> <span class="nt">--write-env-file</span> ~/.gnupg/.gpg-agent-info<span class="si">)</span>
<span class="k">fi</span>
</code></pre></div></div>

<p>The high level diagram for the automatic signing is thus <code class="language-plaintext highlighter-rouge">git -&gt; gpg -&gt;
shell/env variable -&gt; gpg-agent -&gt; pinentry -&gt; keychain</code>.</p>

<p>Automatic commit signing should now be successfully configured.</p>]]></content><author><name>Rui Afonso Pereira</name></author><category term="Workflow" /><category term="git" /><summary type="html"><![CDATA[Why Is It Important?]]></summary></entry><entry><title type="html">Vim Spell-Checking</title><link href="https://rpereira.pt/workflow/vim-spell-checking/" rel="alternate" type="text/html" title="Vim Spell-Checking" /><published>2016-03-25T01:53:33+00:00</published><updated>2016-03-25T01:53:33+00:00</updated><id>https://rpereira.pt/workflow/vim-spell-checking</id><content type="html" xml:base="https://rpereira.pt/workflow/vim-spell-checking/"><![CDATA[<h3 id="spell-check-your-work">Spell check your work</h3>

<p>Since version 7, Vim has the ability to spell check documents on the fly. We can
enable this functionality with the following command:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>:set spell
</code></pre></div></div>

<p>Furthermore, we can also specify a regional variant of a language:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>:set spelllang=en_us
</code></pre></div></div>

<p>The default <code class="language-plaintext highlighter-rouge">spelllang=en</code> will allow a word whose spelling is acceptable in any
English-speaking region.</p>

<h3 id="see-it-in-action">See it in action</h3>

<p>In the following screenshot, the underlined word is considered a misspelling.</p>

<p><img src="/assets/images/vim-spell-checking.png" alt="My helpful screenshot" /></p>

<p>In Normal mode, we can jump backward and forward between misspelled words using
<code class="language-plaintext highlighter-rouge">]s</code> and <code class="language-plaintext highlighter-rouge">]s</code> commands, respectively. Then, issuing the <code class="language-plaintext highlighter-rouge">z=</code> command, we
instruct Vim to suggest a list of correctly spelled words for the word
under/after the cursor. The prompt at the bottom of the screen advises us to
insert the index of the word we want to use in place of the misspelled word.</p>

<h3 id="spell-check-for-file-type">Spell check for file type</h3>

<p>Instead of manually turn on the spell checker each time we need it, we can turn
it on based on the file’s extension. My preferred way of accomplish that is
creating a file whose name is <code class="language-plaintext highlighter-rouge">&lt;filetype&gt;.vim</code> under <code class="language-plaintext highlighter-rouge">~/.vim/ftplugin</code> with
<code class="language-plaintext highlighter-rouge">setlocal spell</code> in its content. You can take a look at my
<a href="https://github.com/rpereira/dotfiles/tree/master/vim/ftplugin">dotfiles</a>, which
contains examples for <code class="language-plaintext highlighter-rouge">gitcommit</code> and <code class="language-plaintext highlighter-rouge">markdown</code> files.</p>

<h3 id="adding-words-to-the-spell-file">Adding words to the spell file</h3>

<p>In Normal model, we can add any word to the <code class="language-plaintext highlighter-rouge">spellfile</code> by cursoring over the
desired word and issuing the command <code class="language-plaintext highlighter-rouge">zg</code>.</p>]]></content><author><name>Rui Afonso Pereira</name></author><category term="Workflow" /><category term="vim" /><summary type="html"><![CDATA[Spell check your work]]></summary></entry></feed>