Nikita Misharin's blogWrite-only tech bloghttps://thesmartnik.com/2019-09-22T20:34:00ZBlog AuthorSolving Puzzles with Ambhttps://thesmartnik.com/solving-pazzles-wth-amb.html2019-09-22T20:34:00Z2019-11-13T13:09:47ZMikita Misharin<p>Ever since an article about fibers, I was curious about an amb operator. There are multiple articles explaining both what it is and how to implement it: <a href="http://www.randomhacks.net/2005/10/11/amb-operator/">Roseta Code</a> and <a href="http://www.randomhacks.net/2005/10/11/amb-operator/">Eric Kidd’s blog</a>. There is even a <a href="https://github.com/chikamichi/amb">gem with two different implementations</a>. What’s left unclear to me, though was how to solve problems with amb.</p>
<p>I believe that the easiest way to understand something is to build it yourself and then try to explain it to others and that’s exactly what I’m doing now :)</p>
<p>Recently, I’ve solved a few classical problems using amb. The result turned out to be short, simple and declarative.</p>
<h2>Disclaimer</h2>
<p>The post turned out to be long, complex and code-heavy, but don’t let it discourage you. Please, try some of the things below. Play around it with it. They are mindblowing. A <del>lisp</del> ruby magic at it’s finest.</p>
<h2>What exactly is amb and how it works</h2>
<p>I really encourage you to read the articles I’ve mentioned above. You will better understand how it works under the hood. However, if you decide not to, here is a basic principle:</p>
<blockquote>
<p>Given a set of constraints and options to choose from, <code>amb</code> returns options satisfied by those constraints.</p>
</blockquote>
<p>And a basic example:</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="nb">require</span> <span class="s1">'amb'</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">amb</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="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span> <span class="c1"># options to choose from</span>
<span class="n">y</span> <span class="o">=</span> <span class="n">amb</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="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span> <span class="c1"># options to choose from</span>
<span class="n">amb</span> <span class="k">unless</span> <span class="n">x</span> <span class="o">*</span> <span class="n">y</span> <span class="o">==</span> <span class="mi">8</span> <span class="c1"># constraint</span>
<span class="nb">puts</span> <span class="s2">"x: </span><span class="si">#{</span><span class="n">x</span><span class="si">}</span><span class="s2">, y: </span><span class="si">#{</span><span class="n">y</span><span class="si">}</span><span class="s2">"</span>
<span class="o">=></span> <span class="ss">x: </span><span class="mi">2</span><span class="p">,</span> <span class="ss">y: </span><span class="mi">4</span>
</code></pre></div>
<p>Here we first specify options for <code>x</code> and <code>y</code>.Then we call <code>amb</code> without parameter until their product equals 8.</p>
<p>Why do we call <code>amb</code> without parameter? It’s a gem API to try other options by using a ruby control-flow structure called <code>continuations</code>. It allows as to easily check possible combinations until we find the one that satisfies the constraints.</p>
<h3>Wait wait wait… What is a continuation?</h3>
<p>I’ve mentioned them <a href="why-on-earth-do-fibers-exist.html#a-bit-of-history">before</a>, but without much explanation. The best way to describe <code>continuations</code> is through metaphor. If a program is a book, continuation would be a bookmark. Think of a goto, that can only go backward. Or using a famous sandwich example:</p>
<blockquote>
<p>Say you’re in the kitchen in front of the refrigerator, thinking about a sandwich. You take a continuation right there and stick it in your pocket. Then you get some turkey and bread out of the refrigerator and make yourself a sandwich, which is now sitting on the counter. You invoke the continuation in your pocket, and you find yourself standing in front of the refrigerator again, thinking about a sandwich. But fortunately, there’s a sandwich on the counter, and all the materials used to make it are gone. So you eat it. :-)</p>
</blockquote>
<h3>Back to amb</h3>
<p>Ok, so what amb gives us? Ability to specify a set of constraints and the ability to go backward in program execution and check different combinations of options until those constraints are met. Let’s see how it works in practice.</p>
<hr>
<h2>Note</h2>
<ol>
<li>In all of the examples, I use gem amb.</li>
<li>Examples don’t work in REPL, because irb/pry doesn’t handle continuations.</li>
</ol>
<h2>Problem 1: SEND + MORE = MONEY</h2>
<p>Here is a classical <a href="https://en.wikipedia.org/wiki/Verbal_arithmetic">cryptarithm</a>. We need to change every single letter in the equation <code>SEND + MORE = MONEY</code> with a number such that it holds true.</p>
<h3>The first naïve solution</h3>
<p>The first thing I tried to do was just declaratively put requirements into code, like so</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="nb">require</span> <span class="s1">'amb'</span>
<span class="kp">include</span> <span class="no">Amb</span><span class="o">::</span><span class="no">Operator</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">amb</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="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">9</span><span class="p">)</span>
<span class="n">e</span> <span class="o">=</span> <span class="n">amb</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="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">n</span> <span class="o">=</span> <span class="n">amb</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="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">d</span> <span class="o">=</span> <span class="n">amb</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="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">m</span> <span class="o">=</span> <span class="n">amb</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="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">9</span><span class="p">)</span>
<span class="n">o</span> <span class="o">=</span> <span class="n">amb</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="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">r</span> <span class="o">=</span> <span class="n">amb</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="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">y</span> <span class="o">=</span> <span class="n">amb</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="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">constraint</span> <span class="o">=</span> <span class="p">[</span><span class="n">s</span><span class="p">,</span> <span class="n">e</span> <span class="p">,</span> <span class="n">n</span> <span class="p">,</span> <span class="n">d</span> <span class="p">,</span> <span class="n">m</span> <span class="p">,</span> <span class="n">o</span> <span class="p">,</span> <span class="n">r</span> <span class="p">,</span> <span class="n">y</span><span class="p">].</span><span class="nf">uniq</span><span class="p">.</span><span class="nf">size</span> <span class="o">==</span> <span class="mi">8</span> <span class="o">&&</span> <span class="s2">"</span><span class="si">#{</span><span class="n">s</span><span class="si">}#{</span><span class="n">e</span><span class="si">}#{</span><span class="n">n</span><span class="si">}#{</span><span class="n">d</span><span class="si">}</span><span class="s2">"</span><span class="p">.</span><span class="nf">to_i</span> <span class="o">+</span> <span class="s2">"</span><span class="si">#{</span><span class="n">m</span><span class="si">}#{</span><span class="n">o</span><span class="si">}#{</span><span class="n">r</span><span class="si">}#{</span><span class="n">e</span><span class="si">}</span><span class="s2">"</span><span class="p">.</span><span class="nf">to_i</span> <span class="o">==</span> <span class="s2">"</span><span class="si">#{</span><span class="n">m</span><span class="si">}#{</span><span class="n">o</span><span class="si">}#{</span><span class="n">n</span><span class="si">}#{</span><span class="n">e</span><span class="si">}#{</span><span class="n">y</span><span class="si">}</span><span class="s2">"</span><span class="p">.</span><span class="nf">to_i</span>
<span class="n">amb</span> <span class="k">unless</span> <span class="n">constraint</span>
</code></pre></div>
<p>As you can see, not very different from the basic example. Here we first specify options for every letter and then check that they are unique and equation holds true. Then we call <code>amb</code> without parameter until constraints are met.</p>
<p>It turned out to work but had a serious problem: speed. The code above took 13 <em>minutes</em> on my machine.</p>
<h3>Optimized solution</h3>
<p>Here is where <a href="https://github.com/flash-gordon">Nikita Shilnikov</a> came to the rescue. He showed, how to specify unique constraints when declaring amb options. This drastically reduced number of combinations we need to check</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="nb">require</span> <span class="s1">'amb'</span>
<span class="kp">include</span> <span class="no">Amb</span><span class="o">::</span><span class="no">Operator</span>
<span class="n">choices</span> <span class="o">=</span> <span class="p">(</span><span class="mi">0</span><span class="o">..</span><span class="mi">9</span><span class="p">).</span><span class="nf">to_a</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">amb</span><span class="p">(</span><span class="o">*</span><span class="n">choices</span> <span class="o">-</span> <span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="n">e</span> <span class="o">=</span> <span class="n">amb</span><span class="p">(</span><span class="o">*</span><span class="n">choices</span> <span class="o">-</span> <span class="p">[</span><span class="n">s</span><span class="p">])</span>
<span class="n">n</span> <span class="o">=</span> <span class="n">amb</span><span class="p">(</span><span class="o">*</span><span class="n">choices</span> <span class="o">-</span> <span class="p">[</span><span class="n">s</span><span class="p">,</span> <span class="n">e</span><span class="p">])</span>
<span class="n">d</span> <span class="o">=</span> <span class="n">amb</span><span class="p">(</span><span class="o">*</span><span class="n">choices</span> <span class="o">-</span> <span class="p">[</span><span class="n">s</span><span class="p">,</span> <span class="n">e</span><span class="p">,</span> <span class="n">n</span><span class="p">])</span>
<span class="n">m</span> <span class="o">=</span> <span class="n">amb</span><span class="p">(</span><span class="o">*</span><span class="n">choices</span> <span class="o">-</span> <span class="p">[</span><span class="n">s</span><span class="p">,</span> <span class="n">e</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="mi">0</span><span class="p">])</span>
<span class="n">o</span> <span class="o">=</span> <span class="n">amb</span><span class="p">(</span><span class="o">*</span><span class="n">choices</span> <span class="o">-</span> <span class="p">[</span><span class="n">s</span><span class="p">,</span> <span class="n">e</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="n">m</span><span class="p">])</span>
<span class="n">r</span> <span class="o">=</span> <span class="n">amb</span><span class="p">(</span><span class="o">*</span><span class="n">choices</span> <span class="o">-</span> <span class="p">[</span><span class="n">s</span><span class="p">,</span> <span class="n">e</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="n">m</span><span class="p">,</span> <span class="n">o</span><span class="p">])</span>
<span class="n">y</span> <span class="o">=</span> <span class="n">amb</span><span class="p">(</span><span class="o">*</span><span class="n">choices</span> <span class="o">-</span> <span class="p">[</span><span class="n">s</span><span class="p">,</span> <span class="n">e</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="n">m</span><span class="p">,</span> <span class="n">o</span><span class="p">,</span> <span class="n">r</span><span class="p">])</span>
<span class="n">constraint</span> <span class="o">=</span> <span class="s2">"</span><span class="si">#{</span><span class="n">s</span><span class="si">}#{</span><span class="n">e</span><span class="si">}#{</span><span class="n">n</span><span class="si">}#{</span><span class="n">d</span><span class="si">}</span><span class="s2">"</span><span class="p">.</span><span class="nf">to_i</span> <span class="o">+</span> <span class="s2">"</span><span class="si">#{</span><span class="n">m</span><span class="si">}#{</span><span class="n">o</span><span class="si">}#{</span><span class="n">r</span><span class="si">}#{</span><span class="n">e</span><span class="si">}</span><span class="s2">"</span><span class="p">.</span><span class="nf">to_i</span> <span class="o">==</span> <span class="s2">"</span><span class="si">#{</span><span class="n">m</span><span class="si">}#{</span><span class="n">o</span><span class="si">}#{</span><span class="n">n</span><span class="si">}#{</span><span class="n">e</span><span class="si">}#{</span><span class="n">y</span><span class="si">}</span><span class="s2">"</span><span class="p">.</span><span class="nf">to_i</span>
<span class="n">amb</span> <span class="k">unless</span> <span class="n">constraint</span>
<span class="nb">puts</span> <span class="s2">"</span><span class="si">#{</span><span class="n">s</span><span class="si">}#{</span><span class="n">e</span><span class="si">}#{</span><span class="n">n</span><span class="si">}#{</span><span class="n">d</span><span class="si">}</span><span class="s2"> + </span><span class="si">#{</span><span class="n">m</span><span class="si">}#{</span><span class="n">o</span><span class="si">}#{</span><span class="n">r</span><span class="si">}#{</span><span class="n">e</span><span class="si">}</span><span class="s2"> = </span><span class="si">#{</span><span class="n">m</span><span class="si">}#{</span><span class="n">o</span><span class="si">}#{</span><span class="n">n</span><span class="si">}#{</span><span class="n">e</span><span class="si">}#{</span><span class="n">y</span><span class="si">}</span><span class="s2">"</span>
</code></pre></div>
<p>The example above takes a couple of seconds and it’s extremely elegant and declarative.</p>
<h2>Problem 2: N-Queens Problem</h2>
<p>The next problem we can solve with amb is harder and more interesting.</p>
<h3>Description</h3>
<p>Given the NxN chessboard, we need to place N queens such that they don’t threaten each other. Here’s a <a href="https://en.wikipedia.org/wiki/Eight_queens_puzzle">wiki</a> if you want to know more.</p>
<p>Let’s think about how we can solve it using amb.</p>
<h3>Notes on implementation</h3>
<ol>
<li>We need to specify the row and the column of the queen. We can use a simple array for that.</li>
<li>Rows and columns are unique.</li>
<li>We need some simple way to check if two queens are on the same diagonal. Quick StackOverflow search suggests that we should check the slope between two queens and if it’s the same, they are on the same diagonal, like so:</li>
</ol>
<div class="highlight"><pre class="highlight plaintext"><code>(queen1X - queen2X).abs == (quuen1Y - queen2Y).abs
</code></pre></div>
<p>That’s enough for us to put together a solution</p>
<h3>Implementation</h3>
<p>First, we specify the number of queens and create them</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="n">n</span> <span class="o">=</span> <span class="mi">8</span>
<span class="n">queens</span> <span class="o">=</span> <span class="no">Array</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">n</span><span class="p">)</span> <span class="p">{</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">]</span> <span class="p">}</span>
</code></pre></div>
<p>We need a unique row, column and diagonal. We could use amb for both row and column, but the result will be super slow and a unique diagonal can be found just by changing column. That’s why we hardcode a row and use amb only for a column. </p>
<div class="highlight"><pre class="highlight ruby"><code><span class="n">choices</span> <span class="o">=</span> <span class="p">[</span><span class="o">*</span><span class="mi">1</span><span class="o">..</span><span class="n">n</span><span class="p">]</span>
<span class="n">n</span><span class="p">.</span><span class="nf">times</span> <span class="k">do</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span>
<span class="n">column</span> <span class="o">=</span> <span class="n">amb</span><span class="p">(</span><span class="o">*</span><span class="n">choices</span> <span class="o">-</span> <span class="n">queens</span><span class="p">[</span><span class="mi">0</span><span class="o">..</span><span class="n">i</span><span class="p">].</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="ss">:first</span><span class="p">))</span>
<span class="n">row</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span>
<span class="n">queens</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">column</span><span class="p">,</span> <span class="n">row</span><span class="p">]</span>
<span class="k">end</span>
</code></pre></div>
<p>We check diagonals using the formula for checking slopes</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="n">different_diags</span> <span class="o">=</span> <span class="n">queens</span><span class="p">.</span><span class="nf">each</span><span class="p">.</span><span class="nf">with_index</span><span class="p">.</span><span class="nf">all?</span> <span class="k">do</span> <span class="o">|</span><span class="n">q1</span><span class="p">,</span> <span class="n">i</span><span class="o">|</span>
<span class="p">(</span><span class="n">queens</span><span class="p">[</span><span class="n">i</span><span class="o">..-</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="p">[</span><span class="n">q1</span><span class="p">]).</span><span class="nf">none?</span> <span class="k">do</span> <span class="o">|</span><span class="n">q2</span><span class="o">|</span>
<span class="p">(</span><span class="n">q1</span><span class="p">.</span><span class="nf">first</span> <span class="o">-</span> <span class="n">q2</span><span class="p">.</span><span class="nf">first</span><span class="p">).</span><span class="nf">abs</span> <span class="o">==</span> <span class="p">(</span><span class="n">q1</span><span class="p">.</span><span class="nf">last</span> <span class="o">-</span> <span class="n">q2</span><span class="p">.</span><span class="nf">last</span><span class="p">).</span><span class="nf">abs</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div>
<p>And try again until the constraint is met</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="n">amb</span> <span class="k">unless</span> <span class="n">different_diags</span>
</code></pre></div>
<h4>Full solution</h4>
<div class="highlight"><pre class="highlight ruby"><code><span class="nb">require</span> <span class="s1">'amb'</span>
<span class="kp">include</span> <span class="no">Amb</span><span class="o">::</span><span class="no">Operator</span>
<span class="n">n</span> <span class="o">=</span> <span class="mi">8</span>
<span class="n">queens</span> <span class="o">=</span> <span class="no">Array</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">n</span><span class="p">)</span> <span class="p">{</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">]</span> <span class="p">}</span>
<span class="n">choices</span> <span class="o">=</span> <span class="p">[</span><span class="o">*</span><span class="mi">1</span><span class="o">..</span><span class="n">n</span><span class="p">]</span>
<span class="n">n</span><span class="p">.</span><span class="nf">times</span> <span class="k">do</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span>
<span class="n">column</span> <span class="o">=</span> <span class="n">amb</span><span class="p">(</span><span class="o">*</span><span class="n">choices</span> <span class="o">-</span> <span class="n">queens</span><span class="p">[</span><span class="mi">0</span><span class="o">..</span><span class="n">i</span><span class="p">].</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="ss">:first</span><span class="p">))</span>
<span class="n">row</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span>
<span class="n">queens</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">column</span><span class="p">,</span> <span class="n">row</span><span class="p">]</span>
<span class="k">end</span>
<span class="n">different_diags</span> <span class="o">=</span> <span class="n">queens</span><span class="p">.</span><span class="nf">each</span><span class="p">.</span><span class="nf">with_index</span><span class="p">.</span><span class="nf">all?</span> <span class="k">do</span> <span class="o">|</span><span class="n">q1</span><span class="p">,</span> <span class="n">i</span><span class="o">|</span>
<span class="p">(</span><span class="n">queens</span><span class="p">[</span><span class="n">i</span><span class="o">..-</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="p">[</span><span class="n">q1</span><span class="p">]).</span><span class="nf">none?</span> <span class="k">do</span> <span class="o">|</span><span class="n">q2</span><span class="o">|</span>
<span class="p">(</span><span class="n">q1</span><span class="p">.</span><span class="nf">first</span> <span class="o">-</span> <span class="n">q2</span><span class="p">.</span><span class="nf">first</span><span class="p">).</span><span class="nf">abs</span> <span class="o">==</span> <span class="p">(</span><span class="n">q1</span><span class="p">.</span><span class="nf">last</span> <span class="o">-</span> <span class="n">q2</span><span class="p">.</span><span class="nf">last</span><span class="p">).</span><span class="nf">abs</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">amb</span> <span class="k">unless</span> <span class="n">different_diags</span>
<span class="nb">puts</span> <span class="n">queens</span><span class="p">.</span><span class="nf">inspect</span>
</code></pre></div>
<p>Again, a declarative solution is a few lines long compared to <a href="https://www.rubyguides.com/2018/08/n-queens/"> a procedural one</a></p>
<h2>Problem 3: 4 Color problem</h2>
<p>In our last problem, we’ll try to color a map of Europe, using only 4 colors such that no neighborhood countries had the same color.</p>
<p>With amb, the solution is rather straightforward.</p>
<h3>Implementation</h3>
<p>We specify possible colors and countries</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="nb">require</span> <span class="s1">'amb'</span>
<span class="kp">include</span> <span class="no">Amb</span><span class="o">::</span><span class="no">Operator</span>
<span class="n">choices</span> <span class="o">=</span> <span class="sx">%i(green yellow red blue)</span>
<span class="n">countries</span> <span class="o">=</span> <span class="p">{</span>
<span class="ss">portugal: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(spain)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">spain: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(portugal andorra france)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">andorra: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(spain france)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">france: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(spain andorra monaco italy switzerland germany luxembourg belgium united_kingdom)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">united_kingdom: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(france belgium netherlands denmark norway iceland ireland)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">ireland: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(united_kingdom iceland)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">monaco: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(france)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">italy: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(france greece albania montenegro croatia slovenia austria switzerland san_marino)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">san_marino: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(italy)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">switzerland: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(france italy austria germany liechtenstein)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">liechtenstein: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(switzerland austria)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">germany: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(france switzerland austria czech_republic poland sweden denmark netherlands belgium luxembourg)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">belgium: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(france luxembourg germany netherlands)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">netherlands: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(belgium germany united_kingdom)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">luxembourg: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(france germany belgium)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">austria: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(italy slovenia hungary slovakia czech_republic germany switzerland liechtenstein)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">slovenia: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(italy croatia hungary austria)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">croatia: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(italy montenegro bosnia serbia hungary slovenia)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">bosnia: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(croatia montenegro serbia)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">montenegro: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(croatia italy albania serbia bosnia)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">albania: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(italy greece macedonia serbia montenegro)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">greece: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(italy cyprus bulgaria macedonia albania)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">cyprus: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(greece)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">macedonia: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(albania greece bulgaria serbia)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">bulgaria: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(macedonia greece romania serbia)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">serbia: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(montenegro albania macedonia bulgaria romania hungary croatia bosnia)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">romania: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(serbia bulgaria hungary moldova)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">hungary: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(slovenia croatia serbia romania slovakia austria ukraine)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">slovakia: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(austria hungary poland czech_republic ukraine)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">czech_republic: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(germany austria slovakia poland)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">poland: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(germany czech_republic slovakia sweden ukraine lithuania belarus)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">denmark: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(united_kingdom germany sweden norway)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">sweden: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(norway denmark germany poland finland)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">norway: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(united_kingdom denmark sweden finland iceland)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">finland: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(sweden norway)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">iceland: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(ireland united_kingdom norway)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">ukraine: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(slovakia moldova poland belarus hungary)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">moldova: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(ukraine romania)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">belarus: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(poland ukraine lithuania latvia)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">lithuania: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(poland belarus latvia)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">estonia: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(latvia)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">latvia: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(estonia belarus lithuania)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="p">}</span>
</code></pre></div>
<p>Specify their possible color</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="n">countries</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">country</span><span class="p">,</span> <span class="n">value</span><span class="o">|</span>
<span class="n">neighborhood_country_colors</span> <span class="o">=</span> <span class="n">countries</span>
<span class="p">.</span><span class="nf">values_at</span><span class="p">(</span><span class="o">*</span><span class="n">value</span><span class="p">[</span><span class="ss">:neighbors</span><span class="p">])</span>
<span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">c</span><span class="o">|</span> <span class="n">c</span><span class="p">[</span><span class="ss">:color</span><span class="p">]</span> <span class="p">}.</span><span class="nf">compact</span><span class="p">.</span><span class="nf">uniq</span>
<span class="n">countries</span><span class="p">[</span><span class="n">country</span><span class="p">][</span><span class="ss">:color</span><span class="p">]</span> <span class="o">=</span> <span class="n">amb</span><span class="p">(</span><span class="o">*</span><span class="n">choices</span> <span class="o">-</span> <span class="n">neighborhood_country_colors</span><span class="p">)</span>
<span class="k">end</span>
</code></pre></div>
<p>Check that they are different</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="n">different_colors</span> <span class="o">=</span> <span class="n">countries</span><span class="p">.</span><span class="nf">none?</span> <span class="k">do</span> <span class="o">|</span><span class="n">country</span><span class="p">,</span> <span class="n">value</span><span class="o">|</span>
<span class="n">countries</span><span class="p">.</span><span class="nf">values_at</span><span class="p">(</span><span class="o">*</span><span class="n">value</span><span class="p">[</span><span class="ss">:neighbors</span><span class="p">])</span>
<span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">c</span><span class="o">|</span> <span class="n">c</span><span class="p">[</span><span class="ss">:color</span><span class="p">]</span> <span class="p">}</span>
<span class="p">.</span><span class="nf">include?</span><span class="p">(</span><span class="n">value</span><span class="p">[</span><span class="ss">:color</span><span class="p">])</span>
<span class="k">end</span>
</code></pre></div>
<p>Try until the constraint is true
<code>ruby
amb unless different_colors
</code></p>
<h4>Full solution</h4>
<div class="highlight"><pre class="highlight ruby"><code><span class="nb">require</span> <span class="s1">'amb'</span>
<span class="kp">include</span> <span class="no">Amb</span><span class="o">::</span><span class="no">Operator</span>
<span class="n">choices</span> <span class="o">=</span> <span class="sx">%i(green yellow red blue)</span>
<span class="n">countries</span> <span class="o">=</span> <span class="p">{</span>
<span class="ss">portugal: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(spain)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">spain: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(portugal andorra france)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">andorra: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(spain france)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">france: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(spain andorra monaco italy switzerland germany luxembourg belgium united_kingdom)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">united_kingdom: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(france belgium netherlands denmark norway iceland ireland)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">ireland: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(united_kingdom iceland)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">monaco: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(france)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">italy: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(france greece albania montenegro croatia slovenia austria switzerland san_marino)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">san_marino: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(italy)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">switzerland: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(france italy austria germany liechtenstein)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">liechtenstein: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(switzerland austria)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">germany: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(france switzerland austria czech_republic poland sweden denmark netherlands belgium luxembourg)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">belgium: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(france luxembourg germany netherlands)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">netherlands: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(belgium germany united_kingdom)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">luxembourg: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(france germany belgium)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">austria: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(italy slovenia hungary slovakia czech_republic germany switzerland liechtenstein)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">slovenia: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(italy croatia hungary austria)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">croatia: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(italy montenegro bosnia serbia hungary slovenia)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">bosnia: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(croatia montenegro serbia)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">montenegro: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(croatia italy albania serbia bosnia)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">albania: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(italy greece macedonia serbia montenegro)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">greece: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(italy cyprus bulgaria macedonia albania)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">cyprus: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(greece)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">macedonia: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(albania greece bulgaria serbia)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">bulgaria: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(macedonia greece romania serbia)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">serbia: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(montenegro albania macedonia bulgaria romania hungary croatia bosnia)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">romania: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(serbia bulgaria hungary moldova)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">hungary: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(slovenia croatia serbia romania slovakia austria ukraine)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">slovakia: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(austria hungary poland czech_republic ukraine)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">czech_republic: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(germany austria slovakia poland)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">poland: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(germany czech_republic slovakia sweden ukraine lithuania belarus)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">denmark: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(united_kingdom germany sweden norway)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">sweden: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(norway denmark germany poland finland)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">norway: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(united_kingdom denmark sweden finland iceland)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">finland: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(sweden norway)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">iceland: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(ireland united_kingdom norway)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">ukraine: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(slovakia moldova poland belarus hungary)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">moldova: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(ukraine romania)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">belarus: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(poland ukraine lithuania latvia)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">lithuania: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(poland belarus latvia)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">estonia: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(latvia)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="ss">latvia: </span><span class="p">{</span> <span class="ss">neighbors: </span><span class="sx">%i(estonia belarus lithuania)</span><span class="p">,</span> <span class="ss">color: </span><span class="kp">nil</span> <span class="p">},</span>
<span class="p">}</span>
<span class="n">countries</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">country</span><span class="p">,</span> <span class="n">value</span><span class="o">|</span>
<span class="n">neighborhood_country_colors</span> <span class="o">=</span> <span class="n">countries</span><span class="p">.</span><span class="nf">values_at</span><span class="p">(</span><span class="o">*</span><span class="n">value</span><span class="p">[</span><span class="ss">:neighbors</span><span class="p">])</span>
<span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">c</span><span class="o">|</span> <span class="n">c</span><span class="p">[</span><span class="ss">:color</span><span class="p">]</span> <span class="p">}</span>
<span class="p">.</span><span class="nf">compact</span>
<span class="p">.</span><span class="nf">uniq</span>
<span class="n">countries</span><span class="p">[</span><span class="n">country</span><span class="p">][</span><span class="ss">:color</span><span class="p">]</span> <span class="o">=</span> <span class="n">amb</span><span class="p">(</span><span class="o">*</span><span class="n">choices</span> <span class="o">-</span> <span class="n">neighborhood_country_colors</span><span class="p">)</span>
<span class="k">end</span>
<span class="n">different_colors</span> <span class="o">=</span> <span class="n">countries</span><span class="p">.</span><span class="nf">none?</span> <span class="k">do</span> <span class="o">|</span><span class="n">country</span><span class="p">,</span> <span class="n">value</span><span class="o">|</span>
<span class="n">countries</span><span class="p">.</span><span class="nf">values_at</span><span class="p">(</span><span class="o">*</span><span class="n">value</span><span class="p">[</span><span class="ss">:neighbors</span><span class="p">])</span>
<span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">c</span><span class="o">|</span> <span class="n">c</span><span class="p">[</span><span class="ss">:color</span><span class="p">]</span> <span class="p">}</span>
<span class="p">.</span><span class="nf">include?</span><span class="p">(</span><span class="n">value</span><span class="p">[</span><span class="ss">:color</span><span class="p">])</span>
<span class="k">end</span>
<span class="n">amb</span> <span class="k">unless</span> <span class="n">different_colors</span>
<span class="n">countries</span><span class="p">.</span><span class="nf">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">c</span><span class="p">,</span> <span class="n">v</span><span class="o">|</span> <span class="nb">puts</span> <span class="s2">"</span><span class="si">#{</span><span class="n">c</span><span class="si">}</span><span class="s2">: </span><span class="si">#{</span><span class="n">v</span><span class="p">[</span><span class="ss">:color</span><span class="p">]</span><span class="si">}</span><span class="s2">"</span> <span class="p">}</span>
</code></pre></div>
<h3>Result</h3>
<p>Here is what we get if we run the code</p>
<div class="highlight"><pre class="highlight plaintext"><code>portugal: green
spain: yellow
andorra: green
france: red
united_kingdom: green
ireland: yellow
monaco: green
italy: green
san_marino: yellow
switzerland: yellow
liechtenstein: green
germany: green
belgium: yellow
netherlands: red
luxembourg: blue
austria: red
slovenia: yellow
croatia: red
bosnia: green
montenegro: yellow
albania: red
greece: yellow
cyprus: green
macedonia: green
bulgaria: red
serbia: blue
romania: yellow
hungary: green
slovakia: yellow
czech_republic: blue
poland: red
denmark: yellow
sweden: blue
norway: red
finland: green
iceland: blue
ukraine: blue
moldova: green
belarus: green
lithuania: yellow
estonia: green
latvia: red
</code></pre></div>
<p>I wasn’t sure that the code worked correctly so I’ve decided to color the map with the colors above. Here is the result</p>
<p><img src="images/color_map.png" alt="4 color map of europe" /></p>
<p>As you can see, all neighborhood countries have different colors.</p>
<h2>Thanks for reading!</h2>
<p>Amb and the puzzles don’t really have any pragmatic value, but I feel that in programming there is always room for things that are just fun and magical. Hope you’ve enjoyed reading the post.</p>
Breaking apart inheritable mattr_accessors in HTTPartyhttps://thesmartnik.com/breaking-apart-inheritable-mattr_accessors-in-httparty.html2019-07-04T20:42:00Z2019-09-11T10:35:25ZMikita Misharin<h1>A land of sharp of knives</h1>
<p>We all know that Ruby provides you with <a href="https://m.signalvnoise.com/provide-sharp-knives/">sharp knives</a>. Used with caution they give you great power, but misuse them and you will be remembered at your job long after.</p>
<p>I like some of those, from outside they look like magic, but inside they often are incomprehensible and hard to debug metaprogramming mess.</p>
<p>And in this post, I want to dig into one of those: inheritable attr_accessors.</p>
<h1>A little background</h1>
<p>A conventional use of <code>HTTParty</code> is to <code>include</code> it in your <code>class</code>. It allows to create nice and simple wrappers around almost any API and perfect for little API gems. Something like.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">ApiClient</span>
<span class="kp">include</span> <span class="no">HTTParty</span>
<span class="n">base_uri</span> <span class="s2">"example.com"</span>
<span class="n">headers</span><span class="p">(</span><span class="ss">content_type: </span><span class="s1">'application/json'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">clients</span>
<span class="n">get</span> <span class="ss">:clients</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div>
<p>But what if wanted to inherit from <code>ApiClient</code>? The obvious behavior would be for a child class to inherit parent’s class-defined options.</p>
<p>As you might have already guessed, there is no straightforward way to do this.</p>
<h1>Here comes the dragon</h1>
<div class="highlight"><pre class="highlight ruby"><code> <span class="k">module</span> <span class="nn">ModuleInheritableAttributes</span>
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">included</span><span class="p">(</span><span class="n">base</span><span class="p">)</span>
<span class="n">base</span><span class="p">.</span><span class="nf">extend</span><span class="p">(</span><span class="no">ClassMethods</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">module</span> <span class="nn">ClassMethods</span>
<span class="k">def</span> <span class="nf">mattr_inheritable</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">)</span>
<span class="vi">@mattr_inheritable_attrs</span> <span class="o">||=</span> <span class="p">[</span><span class="ss">:mattr_inheritable_attrs</span><span class="p">]</span>
<span class="vi">@mattr_inheritable_attrs</span> <span class="o">+=</span> <span class="n">args</span>
<span class="n">args</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">arg</span><span class="o">|</span>
<span class="nb">module_eval</span> <span class="sx">%(class << self; attr_accessor :#{arg} end)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">inherited</span><span class="p">(</span><span class="n">subclass</span><span class="p">)</span>
<span class="k">super</span>
<span class="vi">@mattr_inheritable_attrs</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">inheritable_attribute</span><span class="o">|</span>
<span class="n">ivar</span> <span class="o">=</span> <span class="s2">"@</span><span class="si">#{</span><span class="n">inheritable_attribute</span><span class="si">}</span><span class="s2">"</span>
<span class="n">parent_value</span> <span class="o">=</span> <span class="nb">instance_variable_get</span><span class="p">(</span><span class="n">ivar</span><span class="p">).</span><span class="nf">clone</span>
<span class="n">subclass</span><span class="p">.</span><span class="nf">instance_variable_set</span><span class="p">(</span><span class="n">ivar</span><span class="p">,</span> <span class="n">parent_value</span><span class="p">)</span>
<span class="k">if</span> <span class="n">parent_value</span><span class="p">.</span><span class="nf">respond_to?</span><span class="p">(</span><span class="ss">:merge</span><span class="p">)</span>
<span class="nb">method</span> <span class="o">=</span> <span class="o"><<-</span><span class="no">EOM</span><span class="sh">
def self.</span><span class="si">#{</span><span class="n">inheritable_attribute</span><span class="si">}</span><span class="sh">
</span><span class="si">#{</span><span class="n">ivar</span><span class="si">}</span><span class="sh"> = superclass.</span><span class="si">#{</span><span class="n">inheritable_attribute</span><span class="si">}</span><span class="sh">.merge(Marshal.load(Marshal.dump(</span><span class="si">#{</span><span class="n">ivar</span><span class="si">}</span><span class="sh">)
)
end
</span><span class="no"> EOM</span>
<span class="n">subclass</span><span class="p">.</span><span class="nf">class_eval</span> <span class="nb">method</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div>
<p>I modified a code a bit. Instead of <code>Marshal.load(Marshal.dump()</code> we now use a <code>hash_deep_dup</code> borrowed from <code>ActiveSupport</code>. It makes code even more complex and not relevant to our discussion here, so I replaced it.</p>
<p>It’s then used like so</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="k">module</span> <span class="nn">HTTParty</span>
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">included</span><span class="p">(</span><span class="n">base</span><span class="p">)</span>
<span class="n">base</span><span class="p">.</span><span class="nf">include</span> <span class="no">ModuleInheritableAttributes</span>
<span class="n">base</span><span class="p">.</span><span class="nf">mattr_inheritable</span><span class="p">(</span><span class="n">default_options</span><span class="p">)</span>
<span class="n">base</span><span class="p">.</span><span class="nf">instance_variable_set</span><span class="p">(</span><span class="s2">"@default_options"</span><span class="p">,</span> <span class="p">{})</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div>
<p>As you can see, use of the code is simple and easy. Code that achieves that, however… Well it’s complex, to say the least.</p>
<h1>Let’s take a closer look to see how it works</h1>
<h2>mattr_inheritable</h2>
<div class="highlight"><pre class="highlight ruby"><code><span class="k">def</span> <span class="nf">mattr_inheritable</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">)</span>
<span class="vi">@mattr_inheritable_attrs</span> <span class="o">||=</span> <span class="p">[</span><span class="ss">:mattr_inheritable_attrs</span><span class="p">]</span>
<span class="vi">@mattr_inheritable_attrs</span> <span class="o">+=</span> <span class="n">args</span>
<span class="n">args</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">arg</span><span class="o">|</span>
<span class="nb">module_eval</span> <span class="sx">%(class << self; attr_accessor :#{arg} end)</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div>
<ol>
<li>Initializing a <em>class instance variable</em> to hold our inheritable attributes including the variable itself.</li>
<li>Adding our new attribute to the array</li>
<li>Adding accesor to our <strong>module</strong>. Notice <code>module_eval</code> here, we evaluate our code in the context of the module. So we are adding accessor not to an instance, but to a module</li>
</ol>
<h2>inherited</h2>
<div class="highlight"><pre class="highlight ruby"><code><span class="k">def</span> <span class="nf">inherited</span><span class="p">(</span><span class="n">subclass</span><span class="p">)</span>
<span class="k">super</span>
<span class="vi">@mattr_inheritable_attrs</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">inheritable_attribute</span><span class="o">|</span>
<span class="n">ivar</span> <span class="o">=</span> <span class="s2">"@</span><span class="si">#{</span><span class="n">inheritable_attribute</span><span class="si">}</span><span class="s2">"</span>
<span class="n">parent_value</span> <span class="o">=</span> <span class="nb">instance_variable_get</span><span class="p">(</span><span class="n">ivar</span><span class="p">).</span><span class="nf">clone</span>
<span class="n">subclass</span><span class="p">.</span><span class="nf">instance_variable_set</span><span class="p">(</span><span class="n">ivar</span><span class="p">,</span> <span class="n">parent_value</span><span class="p">)</span>
<span class="k">if</span> <span class="n">parent_value</span><span class="p">.</span><span class="nf">respond_to?</span><span class="p">(</span><span class="ss">:merge</span><span class="p">)</span>
<span class="nb">method</span> <span class="o">=</span> <span class="o"><<-</span><span class="no">EOM</span><span class="sh">
def self.</span><span class="si">#{</span><span class="n">inheritable_attribute</span><span class="si">}</span><span class="sh">
</span><span class="si">#{</span><span class="n">ivar</span><span class="si">}</span><span class="sh"> = superclass.</span><span class="si">#{</span><span class="n">inheritable_attribute</span><span class="si">}</span><span class="sh">.merge(Marshal.load(Marshal.dump(</span><span class="si">#{</span><span class="n">ivar</span><span class="si">}</span><span class="sh">)
)
end
</span><span class="no"> EOM</span>
<span class="n">subclass</span><span class="p">.</span><span class="nf">class_eval</span> <span class="nb">method</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div>
<ol>
<li>Starting to iterate over our inheritable attributes</li>
<li>Getting variable value from parent</li>
<li>Setting the value to child </li>
<li>Checking the value respond to <code>#merge</code>.</li>
<li>Here, with <code>class_eval</code> we redefine our attr reader to always get parent values and then merge with them with values of a subclass. It’s needed for cases when parent options were changed <em>after</em> our child class was evaluated.</li>
</ol>
<p>That’s it 😊</p>
<h1>A land of magic</h1>
<p>While reading this you might have thought: “God, what a mess!”. And that’s true, but it also allows for some clean code on the user’s side.</p>
<p>Whether “magic” is always bad is a controversial topic. Rails have been criticized for years because of it. However, it is also the reason why Rails became so popular in the first place. It’s the reason why many of us fell in love with Ruby.</p>
<p>So, as with every other decision in programming, there is always a trade-off one has to make. When you create a library your objective is to create an easy to use tool and for that, a little magic is sometimes necessary.</p>
<h1>Update 11.09.2019</h1>
<p><a href="https://github.com/janko">Janko Marohnić</a> kindly <a href="https://www.reddit.com/r/ruby/comments/chiz4j/breaking_apart_inheritable_mattr_accessors_in/">suggested</a> a refactored, simplified version of the code using <a href="https://dejimata.com/2017/5/20/the-ruby-module-builder-pattern">module builder pattern</a></p>
<div class="highlight"><pre class="highlight ruby"><code> <span class="k">class</span> <span class="nc">MattrInheritable</span> <span class="o"><</span> <span class="no">Module</span> <span class="c1">#:nodoc:</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="o">*</span><span class="n">names</span><span class="p">)</span>
<span class="nb">attr_accessor</span> <span class="o">*</span><span class="n">names</span>
<span class="n">define_method</span> <span class="ss">:inherited</span> <span class="k">do</span> <span class="o">|</span><span class="n">subclass</span><span class="o">|</span>
<span class="n">names</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="nb">name</span><span class="o">|</span>
<span class="k">super</span><span class="p">(</span><span class="n">subclass</span><span class="p">)</span>
<span class="n">subclass</span><span class="p">.</span><span class="nf">send</span><span class="p">(</span><span class="ss">:"</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="ss">="</span><span class="p">,</span> <span class="nb">send</span><span class="p">(</span><span class="nb">name</span><span class="p">))</span>
<span class="n">subclass</span><span class="p">.</span><span class="nf">class_eval</span> <span class="o"><<-</span><span class="no">RUBY</span><span class="sh">
def self.</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="sh">
@</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="sh"> = superclass.</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="sh">.merge(MattrInheritable.hash_deep_dup(@</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="sh">))
end
</span><span class="no"> RUBY</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div>
<p>That can be used later used like so</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="n">base</span><span class="p">.</span><span class="nf">extend</span> <span class="no">MattrInheritable</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="ss">:default_options</span><span class="p">)</span>
</code></pre></div>About Ruby Certificationhttps://thesmartnik.com/about-ruby-certification.html2019-02-28T13:21:00Z2019-07-17T03:53:17ZMikita Misharin<h1>Disclaimer</h1>
<p>One reddit user <a href="https://www.reddit.com/r/ruby/comments/b0n18j/about_ruby_certification/eifqnth/">suggested</a> that it’s an advertisement blogpost. It’s not. I just wanted to provide some information for those who are interested and also raise general awareness about the topic.</p>
<h1>Intro</h1>
<p>Ruby has an official certification provided by Ruby Association, chairman of which is Yukihiro Matsumoto.
I can’t say for Japan, but in the rest of the world, it’s not very popular. Either it’s because Ruby is a community-driven language,
where the knowledge and achievements of an individual programmer are characterized by his or her contributions to Open Source and work experience. Maybe it’s because Ruby Association doesn’t do anything to promote it. Most of the pages on the official website are in Japanese and you can hardly find any resources on the web on how or why you should take it.</p>
<p>A few months ago, inspired by Egor Bugayenko’s <a href="https://www.yegor256.com/2014/10/29/how-much-do-you-cost.html#certifications">article</a> and <a href="https://www.youtube.com/watch?v=SXd_Ccta1c8">talk</a> about programmers worth <em>(a provoking theme and title for sure, but it’s nevertheless very useful)</em>, I’ve decided to take the ruby certification. The following article is a summary of my experience.</p>
<h1>About the Certification</h1>
<p>Currently, Ruby certification consists of two tests: silver and gold. Another one, platinum is still in development. You’ll have 90 minutes to answer 50 multiple choice questions. A passing score is 75% and wrong answers don’t affect it, which means you have to answer correctly at least 38 questions.</p>
<p>Tests cost $150 each and are taken in Prometric test centers. They are located all around the world, so there shouldn’t be any problem to find one in your area. Right after the completion, you’ll get the results and in a week a digital certificate will be emailed to you.</p>
<p>Here is how certificates look: <a href="https://www.credential.net/k6zuqudp">Silver</a>/<a href="https://www.credential.net/s0bd99gu">Gold</a></p>
<h1>Hey, why do I need to take the certification, again?</h1>
<p>Before taking the certification I was quite suspicious about it and had a few questions:</p>
<h3>Can it really tell if I know Ruby well?</h3>
<p>I’d say that both tests are good at what they aim to do. As you’ll see later, questions aren’t simple. They test variety of different aspects of ruby both in wide and depth.</p>
<h3>Aren’t it’s just an enterprise thing to make your CV look shiny?</h3>
<p>Well, your CV will look shinier. And you’ll be also able to say that you’re certified ruby developer.</p>
<h3>But will anyone actually care?</h3>
<p>I don’t know. Probably not, but I learned a lot during preparation. So, I’d say it was worthwhile.</p>
<h1>Preparation</h1>
<h2>Silver</h2>
<p>Silver test covers the basics of ruby. Some basic edge cases of syntax. And not so well known methods of most popular classes. They may not appear in your day to day work, but you’ll definitely encounter them sooner or later.</p>
<p>I can’t really write the exact questions, but here is an example to give you an idea:</p>
<blockquote>
<p>Let’s suppose you’ve defined a constant, what will happen if you try to redefine it?</p>
</blockquote>
<p>In real life, you won’t ever need to change constant <em>(I mean, that what constant means, right?)</em>. In ruby, however, you <strong>can</strong> change it. So, even though it’s not very useful, it’s a good thing to know how your tools work and behave.</p>
<p><a href="https://www.ruby.or.jp/en/certification/examination/">Ruby certification site</a> has a list of topics that tests cover. In my opinion, It’s rather broad and unclear. So for actual questions, you need to look at a prep test.</p>
<p>There are two prep tests you can use. One can be found on <a href="https://www.ruby.or.jp/assets/images/en/certification/exam_prep_en.pdf">Ruby Association website</a> <em>(it’s an old version with only 45 questions, but is still helpful)</em> and one on <a href="https://github.com/ruby-association/prep-test/blob/master/silver.md">github</a>. These tests are really good, they provide an explanation to answers and show you <strong>exactly</strong> what would you expect during an actual test.</p>
<p>I suggest you to take prep tests and if you find out that you scored 85-90%, you are all set and ready for the test. During the certification, my score was just a little below the prep one <em>(5-7%)</em>.</p>
<h3>What if you don’t want to take the test?</h3>
<p>If you don’t believe in certifications or just don’t want to spend $150, I still encourage you to take a prep test. It’s a great learning material. It will show you the gaps in your knowledge and generally a really fun thing to do.</p>
<p>One more thing. If you conduct an interview and not really sure what questions to ask. Pick a few from the tests. They are good 👌</p>
<h2>Gold</h2>
<p>That’s a trickier one. It has lot’s of questions on completely different topics: <a href="why-on-earth-do-fibers-exist.html">Fibers</a>, meta programming, <a href="what-protected-actually-does.html">method visibility modifiers</a>, exceptions, exception ancestry chain, catch/throw, use of Enumerable and Comparable, Refinements.</p>
<p>A few books I’ve read previously that were of great help during the test are <a href="http://exceptionalruby.com/">Exceptional Ruby</a>, <a href="http://www.confidentruby.com/">Confident Ruby</a>, <a href="https://pragprog.com/book/ppmetr2/metaprogramming-ruby-2">Metaprogramming Ruby</a>. If you haven’t read these, I encourage you to. Even if you don’t plan to take the test. These are generally really good Ruby books that will boost your understanding and knowledge of the language.</p>
<p>As a silver test, gold one also has <a href="https://github.com/ruby-association/prep-test/blob/master/gold.md">a prep test</a>, but it doesn’t contain the full range of questions you’ll face during an official examination. So take prep test, see where your gaps are and study them carefully.</p>
<p>Overall, Gold Certification is a lot more fun, challenging and rewarding experience.</p>
<h1>Registration</h1>
<p>When you’re ready to take a test. Time for registration. Just go to a <a href="https://www.prometric.com/en-us/clients/ruby/Pages/landing.aspx">Prometric website</a>, choose the certification you want, and book your time and place.</p>
<p>And that’s it. All you’ll need now is to arrive at the test center at a chosen date and take the test.</p>
<h1>Conclusion</h1>
<p>In the end, I’m glad I took the certification. I learned a lot along the way and it’s so fun to discover new things in tools you’ve known for years. Some things I’ve learned I’ll only need in job interviews, but some are very helpful and I started to use in my code right away.</p>
Why on earth do fibers exist?https://thesmartnik.com/why-on-earth-do-fibers-exist.html2018-11-23T14:11:00Z2019-10-02T01:41:07ZMikita Misharin<p>First, let’s answer the question right away. Fibers were created so one could implement generator pattern which in ruby is incorporated in <code>Enumerator</code>. That’s it.</p>
<p>To quote <a href="https://www.amazon.com/dp/0596516177">one book:</a></p>
<blockquote>
<p>Fibers are an advanced and relatively obscure control structure; the majority of Ruby programmers will never need to use the Fiber class directly</p>
</blockquote>
<p>Now that you know the truth. Let’s start from the beginning.</p>
<h2><a name="a-bit-of-history"></a> A bit of history</h2>
<p>Back in ruby 1.8 <a href="https://github.com/ruby/ruby/blob/ruby_1_8_7/lib/generator.rb">generator was implemented through continuations</a>, little-known control structure inspired by Lisp. Continuations main use is again to make generators and programs that needed <a href="https://en.wikipedia.org/wiki/Backtracking">backtracking:</a> <a href="http://www.randomhacks.net/2005/10/11/amb-operator/">ambiguous operator</a>, for example. It has bugs, unpredictable behavior and implemented only in CRuby. Very few understand what it is for and even fewer actually tried to use it.</p>
<p>Then, ruby 1.9 with YARV came along. Continuations became to be <a href="http://www.atdot.net/~ko1/pub/ContinuationFest-ruby.pdf">harmful</a> and were moved to the standard library. <code>Enumerator</code> was rewritten in C using fibers.</p>
<h2>Fibers</h2>
<p>In other languages, fiber is the name for a lightweight thread. In ruby, it is a coroutine. Why it’s not named coroutine then, you might ask? Well, because fiber sounds better <a href="http://www.atdot.net/~ko1/pub/ContinuationFest-ruby.pdf">apparently</a>(Page 20).</p>
<p>There are two types of coroutines: semicouroutune and coroutine. They only differ in the way they transfer control.</p>
<h3>Semicouroutune a.k.a Asymmetric Coroutines</h3>
<p>These coroutines called asmymetric, because there is fundamental asymmetry between caller and coroutine. <em>Resumed</em> by the caller, coroutine <strong>can’t</strong> transfer control to any other coroutine, only to suspend itself and <em>yield</em> control back to the caller.</p>
<p>This is the default mode for ruby fibers.</p>
<h3>Symmetric Coroutines</h3>
<p>If you <code>require 'fiber'</code>, though. <code>Fiber</code> becomes symmetric coroutine, which basically means that now, fibers can transfer control between one another <em>(there are limitations in ruby, though. But here is the basic idea)</em></p>
<p>Okay, now when we become familiar with fibers and coroutines. Let’s look at the thing you can build with them.</p>
<h2>Generator</h2>
<h3>Why would I need generators, again?</h3>
<p>I’ll be completely honest. You probably don’t.</p>
<p>Having said that, they are pretty cool. They are generally useful for partial computations or laziness. In ruby, there are two options to make something lazy: through <code>Enumerator#next</code>(which uses fibers) and through <code>Enumerator::Lazy</code>(which doesn’t)</p>
<h3>Partial computations</h3>
<p>Here is the basic generator’s algorithm:</p>
<ol>
<li>Compute a partial result</li>
<li>Return the result back to the caller</li>
<li>Save the state</li>
<li>If needed, resume the generator to get the next result</li>
</ol>
<p>Let’s see an example with enumerator:</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="n">enumerator</span> <span class="o">=</span> <span class="no">Enumerator</span><span class="p">.</span><span class="nf">new</span> <span class="k">do</span> <span class="o">|</span><span class="n">yielder</span><span class="o">|</span>
<span class="n">i</span> <span class="o">=</span> <span class="mi">0</span>
<span class="kp">loop</span> <span class="k">do</span>
<span class="n">i</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">yielder</span> <span class="o"><<</span> <span class="n">i</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="c1"># => #<Enumerator: #<Enumerator::Generator:0x00007fe6ac9d41e0>:each></span>
<span class="n">enumerator</span><span class="p">.</span><span class="nf">next</span> <span class="c1"># => 1</span>
<span class="n">enumerator</span><span class="p">.</span><span class="nf">next</span> <span class="c1"># => 2</span>
<span class="n">enumerator</span><span class="p">.</span><span class="nf">next</span> <span class="c1"># => 3</span>
<span class="n">enumerator</span><span class="p">.</span><span class="nf">rewind</span>
<span class="n">enumerator</span><span class="p">.</span><span class="nf">next</span> <span class="c1"># => 1</span>
</code></pre></div>
<p>Yeah, I know. That example probably doesn’t raise your eyebrows, except for the syntax, of course. I mean use <code><<</code> to return control, really?
By the way, there is an even more confusing alias for that – <code>yield</code>. It corresponds to <code>Fiber.yield</code>, but has nothing to do with ruby keyword <code>yield</code>.</p>
<p>Anyway, let’s see how it works.</p>
<ol>
<li>You pass a block, where you do computation</li>
<li>On <code>#next</code> the block is called from the beginning or where it left of</li>
<li>You return from the function with a result using <code><<</code></li>
<li>Go back to step .2</li>
</ol>
<p><code>Enumerator</code> allows you to do two types of iterations: internal and external.
In daily life, we usually see enumerators as internal iterators. It is returned everytime you call <code>Enumerable</code> methods without any arguments and it allows you to chain those methods together: <code>each.with_index</code> etc.</p>
<p>Additionally, you can use enumerator as an external iterator as shown in the example above. This construct might be useful for heavy computations, where saving the previous state would save a lot of time and doing so with <code>Enumerator</code> would also be more readable than saving the previous state yourself. Unfortunately, it’s not a very popular method <em>(as laziness in ruby in general)</em> and I couldn’t find any good examples of it in the wild 😞</p>
<h2>The Fun Part</h2>
<p>You’ve read a lot of theory, not it’s time for <em>the fun part</em>. The best way to learn something is to build it, right? Also, it’s probably the only time you’ll ever actually use fibers, so let’s get to it.</p>
<h3>Generator</h3>
<p>Ok, let’s look at the <code>Enumerator</code> example again. What we can say about it?</p>
<ol>
<li>Enumerator accepts block</li>
<li>The block is being called with an argument</li>
<li>The argument has a method <code><<</code> that returns computation result</li>
</ol>
<p>That’s pretty much all we need to be able to construct a simple abstraction over fiber. We’ll call it <code>Generator</code> because that’s what it is.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">Generator</span>
<span class="k">class</span> <span class="nc">Yielder</span>
<span class="k">def</span> <span class="nf"><<</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="c1"># Argument that accepts <<</span>
<span class="no">Fiber</span><span class="p">.</span><span class="nf">yield</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="c1"># And returns computation result</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="o">&</span><span class="n">block</span><span class="p">)</span> <span class="c1"># Accepts block</span>
<span class="vi">@block</span> <span class="o">=</span> <span class="n">block</span>
<span class="vi">@fiber</span> <span class="o">=</span> <span class="n">create_fiber</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">next</span>
<span class="vi">@fiber</span><span class="p">.</span><span class="nf">resume</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">rewind</span>
<span class="vi">@fiber</span> <span class="o">=</span> <span class="n">create_fiber</span>
<span class="k">end</span>
<span class="kp">private</span>
<span class="k">def</span> <span class="nf">create_fiber</span>
<span class="no">Fiber</span><span class="p">.</span><span class="nf">new</span> <span class="k">do</span>
<span class="vi">@block</span><span class="p">.</span><span class="nf">call</span><span class="p">(</span><span class="no">Yielder</span><span class="p">.</span><span class="nf">new</span><span class="p">)</span> <span class="c1"># Block being call with an argument</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div>
<p>That’s it. Here is the tiniest possible version of a generator.</p>
<p>Let’s test that it works as expected. We’ll use <code>Enumerable</code> this time.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="n">generator</span> <span class="o">=</span> <span class="no">Generator</span><span class="p">.</span><span class="nf">new</span> <span class="k">do</span> <span class="o">|</span><span class="n">yielder</span><span class="o">|</span>
<span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="no">Float</span><span class="o">::</span><span class="no">INFINITY</span><span class="p">).</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span>
<span class="n">yielder</span> <span class="o"><<</span> <span class="n">i</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">generator</span><span class="p">.</span><span class="nf">next</span> <span class="c1"># => 1</span>
<span class="n">generator</span><span class="p">.</span><span class="nf">next</span> <span class="c1"># => 2</span>
<span class="n">generator</span><span class="p">.</span><span class="nf">next</span> <span class="c1"># => 3</span>
<span class="n">generator</span><span class="p">.</span><span class="nf">rewind</span>
<span class="n">generator</span><span class="p">.</span><span class="nf">next</span> <span class="c1"># => 1</span>
</code></pre></div>
<h2>Summary</h2>
<p>For a long time, I couldn’t understand why fibers exist. I thought there was something special about them, something that I just couldn’t grasp. Turned out there wasn’t. They are very specific things, created for a very specific task.</p>
<p>I hope that this article gave you a better understanding of what <code>Fiber</code> is, what it’s not and how it’s used.</p>
<h2>Update about concurrency</h2>
<p>Remember that fibers in ruby have two mods? I haven’t really talked about fibers as symmetric coroutines and one redditor <a href="https://www.reddit.com/r/ruby/comments/a0ivny/why_on_earth_do_fibers_exist/eai2k67/">pointed that out</a>.</p>
<p>Given an ability to transfer control between one another, fibers can be used to write asynchronous concurrent code. To do this you’ll need a library that implements event loop such as <a href="https://github.com/socketry/async">async</a> or <a href="https://github.com/eventmachine/eventmachine">eventmachine</a> or you can even build your own simple reactor with ruby <code>io/nonblock</code>.</p>
<p>When it is useful? In tasks that require a lot of io such as web requests. There are two great examples of this: <a href="https://github.com/postrank-labs/goliath">goliath</a> webserver that uses <code>eventmachine</code> and <a href="https://github.com/socketry/falcon">falcon</a> build on top of the <code>async</code>.</p>
<p>I tried to give a brief explanation here, so nothing was left out and you had a complete picture. While it’s not in depth look at fibers as means for concurrency, I hope you now better understand how they can be utilized.</p>
Debugging Adventures: #1https://thesmartnik.com/debugging-adventures-1.html2018-10-24T13:21:00Z2018-11-15T13:25:11ZMikita Misharin<p>I love debugging. You know, the one that makes you dig into the source code of a library or a language. When everything should work just fine, but it doesn’t. When you have no idea what to do and desperately executing same commands hoping for a different result. And it is an adventure really: you kinda know the path, but have no idea where it will lead you.</p>
<p>Anyway, a few months ago I’ve been looking through <a href="https://ruby-doc.org/core-2.5.1/Array.html#method-i-concat">an official ruby documentation</a> and I had noticed that links to <code>Array#+</code> are broken. Immediately after that, encouraged by the thought, that soon I’ll be a ruby contributor <em>(no one need to know it’s been just documentation, right?)</em> I opened up a source code and… Nothing. Documentation to a method looked just fine.</p>
<p>And at that point, I knew it won’t be as easy as I initially thought.</p>
<p>I wasn’t really sure what to do at first. Then, a small detail caught my attention: a sidebar. A sidebar on the left of the page, where a link to <code>#+</code> worked just fine. </p>
<p>Well, that looks great, I thought. I’ll just copy the link from sidebar and change documentation to something like.</p>
<div class="highlight"><pre class="highlight plaintext"><code>/*
* ...
* See also {Array#+}[#method-i-2B].
*/
</code></pre></div>
<p>I had my doubts, of course. I wasn’t sure how <code>i-2B</code> is generated and can get be changed by some unforeseen events. I did a bit of digging into rdoc’s source code and <a href="https://github.com/ruby/rdoc/blob/b7449e4b2d71c16e58f5a5db859867e8c90246ac/lib/rdoc/method_attr.rb#L291-L295">it seemed to be fine</a>. So I’ve decided that’ll do.</p>
<p>Reading this, you probably saying to yourself: “Wait a minute, this just looks like a hack. Why do you have to hard code a link? Why can’t it be generated automatically?”.</p>
<p>Well, that was my thought exactly. I tried to convince myself that the solution above is good enough and working link better, than not a working one etc. But I couldn’t really do it, so I decided to take a quick look. Maybe it would be a quick fix, who knows.</p>
<p>So, a few moments after, I was staring at Rdoc’s source code. And I continued doing so for six straight hours. I don’t know if you aware, but Rdoc code isn’t <a href="https://github.com/ruby/rdoc/blob/b7449e4b2d71c16e58f5a5db859867e8c90246ac/lib/rdoc/markup/to_html_crossref.rb#L147-L154">the most readable</a>. I mean it does <a href="https://github.com/TheSmartnik/rdoc/blob/0e655546b4d3f9d00982f12f87f968dd5b96103a/lib/rdoc/cross_reference.rb#L31-L73">raise questions</a>.</p>
<p>And in the end? Well the whole fix, just took <a href="https://github.com/ruby/rdoc/pull/632">one line of code</a>.</p>
<p>Why am I telling this? Well, I feel like debugging is often daunting and frustrating: nothing is working and you don’t know why. But if you look at this from another perspective, it also can be fun and adventurous. Your princes will probably be in another castle most of the time, but it should never stop you from trying to save her 😁</p>
Ruby Memory Profiling in Practicehttps://thesmartnik.com/ruby-memory-profiling-in-practice.html2018-08-19T14:09:00Z2018-08-20T15:02:02ZMikita Misharin<p>When I only started programming, I loved tasks related to profiling and optimization.
However, my knowledge on this subject was very limited and I desperately searched for articles with some tips and tricks on how to profile properly.
I thought there were some secrets or techniques, that I should know. A few years forward, I can say there a none, really 🤷♂️.</p>
<p>But here are some tips to give you confidence.</p>
<h2>Basic steps</h2>
<p>Profiling itself is very easy and consists of four basic steps</p>
<ol>
<li><p>Profile</p></li>
<li><p>Find bottleneck</p></li>
<li><p>Fix bottleneck</p></li>
<li><p>Profile again </p></li>
</ol>
<h2>How to profile</h2>
<p>Ruby has a rich set of decent profiling tools. Some would disagree, but in my opinion, they are good enough.</p>
<p>Depending on a type of problem you have, you’ll need a different type of profiler. We are talking about memory here, so we’ll take <a href="https://github.com/SamSaffron/memory_profiler">memory_profiler</a>.</p>
<h3>Splitting the work</h3>
<p>Profiling itself requires a lot of memory. Therefore, if you start to profile the whole process, you’ll probably run out of memory pretty quick.</p>
<p>There is a general solution to this. Divide process into few major parts and profile each of them one by one.</p>
<p><em>It may seem obvious, but I feel like it’s an important thing to note.</em></p>
<h2>How to find and fix a bottleneck</h2>
<ol>
<li><p>Look at the report.</p></li>
<li><p>Find the code that takes the most memory.</p></li>
<li><p>Look at the code. Does it create any unnecessary objects? Can you rewrite it to allocate less memory?</p></li>
<li><p>Optimize it if it’s possible. If not, go to the next piece of memory-heavy code in the report.</p></li>
</ol>
<h3>Any unnecessary objects?</h3>
<p>Here is a couple of examples to illustrate what I mean. I recently had a problem with middleman. It constantly used more memory than my 512 MB dyno allowed. So I had to find a solution. </p>
<h4>Middleman</h4>
<p>Middleman created extra array each time I ignored the object.</p>
<div class="highlight"><pre class="highlight diff"><code><span class="gd">- resources.map do |r|
</span><span class="gi">+ resources.each do |r|
</span> if ignored?(r.normalized_path)
r.ignore!
elsif !r.is_a?(ProxyResource) && r.file_descriptor && ignored?(r.file_descriptor.normalized_relative_path)
r.ignore!
end
<span class="gd">- r
</span>end
</code></pre></div>
<p>Seems like a very small thing. However, I had thousands of ignored objects. And those little arrays accounted for 10% of memory used during initialization.
Here is <a href="https://github.com/middleman/middleman/pull/2183">a link to pr</a>.</p>
<p>Usually, this is a small win and it doesn’t help much, what you’re looking for is a big win, like the one below.</p>
<h4>Middleman S3 Sync</h4>
<p>Gem <code>middleman_s3_sync</code> first created sync objects and then ignored ones that don’t need to be synced. The strategy is ok most of the time, but not in my case of hundreds or even thousands ignored resources. It’s very unwise use of resources.</p>
<p><strong>Before</strong></p>
<div class="highlight"><pre class="highlight ruby"><code><span class="k">def</span> <span class="nf">manipulate_resource_list</span><span class="p">(</span><span class="n">mm_resources</span><span class="p">)</span>
<span class="o">::</span><span class="no">Middleman</span><span class="o">::</span><span class="no">S3Sync</span><span class="p">.</span><span class="nf">mm_resources</span> <span class="o">=</span> <span class="n">mm_resources</span>
<span class="k">end</span>
</code></pre></div>
<p><strong>After</strong></p>
<div class="highlight"><pre class="highlight ruby"><code><span class="k">def</span> <span class="nf">manipulate_resource_list</span><span class="p">(</span><span class="n">resources</span><span class="p">)</span>
<span class="o">::</span><span class="no">Middleman</span><span class="o">::</span><span class="no">S3Sync</span><span class="p">.</span><span class="nf">mm_resources</span> <span class="o">=</span> <span class="n">resources</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">resource</span><span class="p">,</span> <span class="n">list</span><span class="o">|</span>
<span class="k">next</span> <span class="k">if</span> <span class="n">resource</span><span class="p">.</span><span class="nf">ignored?</span>
<span class="n">list</span> <span class="o"><<</span> <span class="n">resource</span>
<span class="n">list</span> <span class="o"><<</span> <span class="n">resource</span><span class="p">.</span><span class="nf">target_resource</span> <span class="k">if</span> <span class="n">resource</span><span class="p">.</span><span class="nf">respond_to?</span><span class="p">(</span><span class="ss">:target_resource</span><span class="p">)</span>
<span class="k">end</span>
<span class="n">resources</span>
<span class="k">end</span>
</code></pre></div>
<p>This 8 lines of code freed up 200MB of memory. Here is <a href="https://github.com/fredjean/middleman-s3_sync/pull/155">a link to this little pr</a></p>
<h3>In a report everything is fine. What should I do?</h3>
<p>These a couple of tips that really helped me during profiling.</p>
<ol>
<li><p>If the report is fine. Double check that data you profile with is the same used in production.</p></li>
<li><p>Try to disable parts of the code in staging and check if used memory dropped significantly.</p></li>
</ol>
<hr>
<h2>No bottlenecks. My code is perfect. Third-party code is perfect. But it takes SO MUCH MEMORY.</h2>
<p>Well, if you can’t find a room for optimization and everything is fine. The only solution is to rethink the whole approach. Find a different solution to the same problem and rewrite this functionality altogether.</p>
<p>Many people jump to above method without attempting to find an actual problem in their code. I don’t really like to do so. Profiling isn’t hard and once you find a problem it’s usually easy to fix it.</p>
<p>Rewrites are usually taking so much more time. And when you don’t try to find problems in your code, you’re bound to make the same mistakes again. Optimization tasks are a great way to learn and grow as an engineer.</p>
ActiveJob In Retrospecthttps://thesmartnik.com/active-job-in-retrospect.html2018-07-12T14:11:00Z2018-08-20T15:05:40ZMikita Misharin<p>Recently, I replaced <code>ActiveJob</code> with <code>Resque</code> <em>(why it’s resque and not sidekiq is a whole other matter)</em>, which got me into thinking of ActiveJob usefulness as a whole.</p>
<p>Creating <code>ActiveRecord</code> was a very reasonable thing to do. It’s enough to look at connection adapters for <a href="https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb">postgres</a> and <a href="https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb">mysql</a> to see why.</p>
<ol>
<li><p>Lots of methods</p></li>
<li><p>Quite a different implementation</p></li>
<li><p>Hard to remember</p></li>
<li><p>Hard to switch to projects, where a database is not the one you’re used to.</p></li>
<li><p>And of course, it gives an ability to switch databases without changing code (unless you use db specific features as most of us do)</p></li>
</ol>
<p>Now let’s look at ActiveJob’s adapters.</p>
<h4>Sidekiq</h4>
<div class="highlight"><pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">SidekiqAdapter</span>
<span class="k">def</span> <span class="nf">enqueue</span><span class="p">(</span><span class="n">job</span><span class="p">)</span> <span class="c1">#:nodoc:</span>
<span class="n">job</span><span class="p">.</span><span class="nf">provider_job_id</span> <span class="o">=</span> <span class="no">Sidekiq</span><span class="o">::</span><span class="no">Client</span><span class="p">.</span><span class="nf">push</span><span class="p">(</span>
<span class="s2">"class"</span> <span class="o">=></span> <span class="no">JobWrapper</span><span class="p">,</span> <span class="s2">"wrapped"</span> <span class="o">=></span> <span class="n">job</span><span class="p">.</span><span class="nf">class</span><span class="p">.</span><span class="nf">to_s</span><span class="p">,</span>
<span class="s2">"queue"</span> <span class="o">=></span> <span class="n">job</span><span class="p">.</span><span class="nf">queue_name</span><span class="p">,</span> <span class="s2">"args"</span> <span class="o">=></span> <span class="p">[</span> <span class="n">job</span><span class="p">.</span><span class="nf">serialize</span> <span class="p">]</span>
<span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">enqueue_at</span><span class="p">(</span><span class="n">job</span><span class="p">,</span> <span class="n">timestamp</span><span class="p">)</span> <span class="c1">#:nodoc:</span>
<span class="n">job</span><span class="p">.</span><span class="nf">provider_job_id</span> <span class="o">=</span> <span class="no">Sidekiq</span><span class="o">::</span><span class="no">Client</span><span class="p">.</span><span class="nf">push</span><span class="p">(</span>
<span class="s2">"class"</span> <span class="o">=></span> <span class="no">JobWrapper</span><span class="p">,</span> <span class="s2">"wrapped"</span> <span class="o">=></span> <span class="n">job</span><span class="p">.</span><span class="nf">class</span><span class="p">.</span><span class="nf">to_s</span><span class="p">,</span> <span class="s2">"queue"</span> <span class="o">=></span> <span class="n">job</span><span class="p">.</span><span class="nf">queue_name</span><span class="p">,</span>
<span class="s2">"args"</span> <span class="o">=></span> <span class="p">[</span> <span class="n">job</span><span class="p">.</span><span class="nf">serialize</span> <span class="p">],</span><span class="s2">"at"</span> <span class="o">=></span> <span class="n">timestamp</span>
<span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div>
<h4>Resque</h4>
<div class="highlight"><pre class="highlight ruby"><code> <span class="k">class</span> <span class="nc">ResqueAdapter</span>
<span class="k">def</span> <span class="nf">enqueue</span><span class="p">(</span><span class="n">job</span><span class="p">)</span> <span class="c1">#:nodoc:</span>
<span class="no">JobWrapper</span><span class="p">.</span><span class="nf">instance_variable_set</span><span class="p">(</span><span class="ss">:@queue</span><span class="p">,</span> <span class="n">job</span><span class="p">.</span><span class="nf">queue_name</span><span class="p">)</span>
<span class="no">Resque</span><span class="p">.</span><span class="nf">enqueue_to</span> <span class="n">job</span><span class="p">.</span><span class="nf">queue_name</span><span class="p">,</span> <span class="no">JobWrapper</span><span class="p">,</span> <span class="n">job</span><span class="p">.</span><span class="nf">serialize</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">enqueue_at</span><span class="p">(</span><span class="n">job</span><span class="p">,</span> <span class="n">timestamp</span><span class="p">)</span> <span class="c1">#:nodoc:</span>
<span class="k">unless</span> <span class="no">Resque</span><span class="p">.</span><span class="nf">respond_to?</span><span class="p">(</span><span class="ss">:enqueue_at_with_queue</span><span class="p">)</span>
<span class="k">raise</span> <span class="no">NotImplementedError</span><span class="p">,</span> <span class="s2">"To be able to schedule jobs with Resque you need the "</span> <span class="p">\</span>
<span class="s2">"resque-scheduler gem. Please add it to your Gemfile and run bundle install"</span>
<span class="k">end</span>
<span class="no">Resque</span><span class="p">.</span><span class="nf">enqueue_at_with_queue</span> <span class="n">job</span><span class="p">.</span><span class="nf">queue_name</span><span class="p">,</span> <span class="n">timestamp</span><span class="p">,</span> <span class="no">JobWrapper</span><span class="p">,</span> <span class="n">job</span><span class="p">.</span><span class="nf">serialize</span>
</code></pre></div>
<p>And that’s it, just two methods. Do we really need an abstraction layer for just two methods?</p>
<p>Sometimes yes, but do we really need an abstraction layer that prevents us from using <a href="https://github.com/lantins/resque-retry/issues/140">features</a> without monkey patching <em>(not to say it’s an absolute evil, but it rather error prone)</em> or adds unnecessary <a href="https://github.com/mperham/sidekiq/wiki/Active-Job#performance">performance overhead</a>?</p>
<hr>
<p>Rails is very good at tools that, provided out of the box, just work. Minimum if any configuration and you all set. However, setting up any background job framework is simple. Just five lines of code for sidekiq and a little more for resque.</p>
<p>Another feature that <code>ActiveJob</code> provides is an ability to change background processing backend with one line of code. Although, I think it’s a rare use case. I used it once to switch from resque to sidekiq to resque again. And it wasn’t one line of code. With <code>ActiveJob</code> the only thing I avoided is a few global ‘Find And Replace’ over the codebase.</p>
<p>When something is a default and works out of the box, there is a lot of inner resistance in replacing it. <code>ActiveJob</code> brings little agility, gives another <em>default</em> interface to learn how to work with and how to test properly. It also adds the possibility that some external gems won’t work and you would have to somehow monkey patch this gem to make it work or get rid of <code>ActiveJob</code> altogether. All this and not much profit in return.</p>
Generating pdf with wickedpdf in sidekiqhttps://thesmartnik.com/generating-pdf-with-wickedpdf-in-sidekiq.html2018-06-14T14:11:00Z2019-07-04T06:37:13ZMikita Misharin<p>Recently, I had to generate invoice pdf in a background job.
There is a <a href="https://github.com/mileszs/wicked_pdf/wiki/Background-PDF-creation-via-delayed_job-gem">documentation</a> for how to do so, but it seemes overly complicated.</p>
<p>Here is a shorter, more readable version and without unnecessary <code>class_eval</code></p>
<div class="highlight"><pre class="highlight ruby"><code><span class="n">view</span> <span class="o">=</span> <span class="no">ActionView</span><span class="o">::</span><span class="no">Base</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s1">'app/views/resources/'</span><span class="p">,</span> <span class="p">{},</span> <span class="no">ActionController</span><span class="o">::</span><span class="no">Base</span><span class="p">.</span><span class="nf">new</span><span class="p">)</span>
<span class="n">html</span> <span class="o">=</span> <span class="n">view</span><span class="p">.</span><span class="nf">render</span><span class="p">(</span><span class="ss">file: </span><span class="s1">'show.html.slim'</span><span class="p">,</span> <span class="ss">locals: </span><span class="p">{</span> <span class="ss">resource: </span><span class="n">resource</span> <span class="p">})</span>
<span class="no">WickedPdf</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">pdf_from_string</span><span class="p">(</span><span class="n">html</span><span class="p">)</span>
</code></pre></div>
<p>Returned pdf you can then <strong>upload to S3</strong> or **store in a file.</p>
What protected actually does?https://thesmartnik.com/what-protected-actually-does.html2017-11-25T14:11:00Z2018-08-19T14:04:10ZMikita Misharin<p><code>protected</code> visibility isn’t popular, nor should it be. Having surprising and little known behaviour, that will confuse future maintainers, there are only few possible and even fewer justified use cases for it.</p>
<h2>Behaviour</h2>
<p><code>protected</code> methods can be explicitly called on objects, if caller is an <strong>instance</strong> of the same or descendant class .</p>
<p>Here is an example with comparison of <code>public</code>, <code>protected</code> and <code>private</code> visibility modifiers.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">Foo</span>
<span class="k">def</span> <span class="nf">public_method</span>
<span class="nb">puts</span> <span class="s1">'public'</span>
<span class="k">end</span>
<span class="kp">protected</span> <span class="k">def</span> <span class="nf">protected_method</span>
<span class="nb">puts</span> <span class="s1">'protected'</span>
<span class="k">end</span>
<span class="kp">private</span> <span class="k">def</span> <span class="nf">private_method</span>
<span class="nb">puts</span> <span class="s1">'private'</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">FooBar</span> <span class="o"><</span> <span class="no">Foo</span>
<span class="k">def</span> <span class="nf">bar</span><span class="p">(</span><span class="n">foobar</span><span class="p">)</span>
<span class="n">foobar</span><span class="p">.</span><span class="nf">public_method</span> <span class="c1">#> public</span>
<span class="n">foobar</span><span class="p">.</span><span class="nf">protected_method</span> <span class="c1">#> protected</span>
<span class="n">foobar</span><span class="p">.</span><span class="nf">private_method</span> <span class="c1">#> NoMethodError</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">Baz</span>
<span class="k">def</span> <span class="nf">bar</span><span class="p">(</span><span class="n">foobar</span><span class="p">)</span>
<span class="n">foobar</span><span class="p">.</span><span class="nf">public_method</span> <span class="c1">#> public</span>
<span class="n">foobar</span><span class="p">.</span><span class="nf">protected_method</span> <span class="c1">#> NoMethodError</span>
<span class="n">foobar</span><span class="p">.</span><span class="nf">private_method</span> <span class="c1">#> NoMethodError</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div>
<p>As you can see from example above, <code>public</code> can be called from anywhere. <code>protected</code> from and instance of the same or descendant class . And <code>private</code> can’t be called on instance at all.</p>
<h3>respond_to?</h3>
<p>One important thing to note.
<code>respond_to?</code> doesn’t include protected and private methods in search. You should pass second boolean argument to enable this feature.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">FooBar</span> <span class="o"><</span> <span class="no">Foo</span>
<span class="k">def</span> <span class="nf">bar</span><span class="p">(</span><span class="n">foobar</span><span class="p">)</span>
<span class="nb">puts</span> <span class="n">foobar</span><span class="p">.</span><span class="nf">respond_to?</span><span class="p">(</span><span class="ss">:protected_method</span><span class="p">)</span> <span class="c1">#> false</span>
<span class="nb">puts</span> <span class="n">foobar</span><span class="p">.</span><span class="nf">respond_to?</span><span class="p">(</span><span class="ss">:protected_method</span><span class="p">)</span> <span class="c1">#> true</span>
<span class="n">foobar</span><span class="p">.</span><span class="nf">protected_method</span> <span class="c1">#> protected</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div>
<h2>In the wild</h2>
<h3>Huginn. The good.</h3>
<p>Other design choices aside. The following snippet is a simplified extract from <a href="https://github.com/huginn/huginn/blob/master/lib/location.rb">Huginn source code</a>. And <code>protected</code> here is a rare example of correct usage.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">Location</span> <span class="o"><</span> <span class="no">Struct</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="ss">:latitude</span><span class="p">,</span> <span class="ss">:longitude</span><span class="p">)</span>
<span class="kp">protected</span> <span class="ss">:[]=</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">data</span> <span class="o">=</span> <span class="p">[])</span>
<span class="k">super</span><span class="p">()</span>
<span class="nb">self</span><span class="p">.</span><span class="nf">latitude</span><span class="p">,</span> <span class="nb">self</span><span class="p">.</span> <span class="nf">longitude</span> <span class="o">=</span> <span class="n">data</span> <span class="k">if</span> <span class="n">data</span><span class="p">.</span><span class="nf">size</span> <span class="o">==</span> <span class="mi">2</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">latitude</span><span class="o">=</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="nb">self</span><span class="p">[</span><span class="ss">:latitude</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span> <span class="k">if</span> <span class="n">value</span><span class="p">.</span><span class="nf">abs</span> <span class="o"><=</span> <span class="mi">90</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">longitude</span><span class="o">=</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="nb">self</span><span class="p">[</span><span class="ss">:longitude</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span> <span class="k">if</span> <span class="n">value</span><span class="p">.</span><span class="nf">abs</span> <span class="o"><=</span> <span class="mi">180</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div>
<p>Struct descendant’s can assign variables both through <code>[]=</code> method and through explicit setters. Here, as you can see, <code>[]=</code> is protected to prevent assigning instance attributes with anything, but redefined setters.</p>
<div class="highlight"><pre class="highlight ruby"><code><span class="n">location</span> <span class="o">=</span> <span class="no">Location</span><span class="p">.</span><span class="nf">new</span>
<span class="o">=></span> <span class="c1">#<struct Location latitude=nil, longitude=nil></span>
<span class="n">location</span><span class="p">.</span><span class="nf">latitude</span> <span class="o">=</span> <span class="mf">53.1242</span>
<span class="o">=></span> <span class="mf">53.1242</span>
<span class="n">location</span><span class="p">[</span><span class="ss">:longitude</span><span class="p">]</span> <span class="o">=</span> <span class="mf">42.2124</span>
<span class="no">NoMethodError</span><span class="p">:</span> <span class="kp">protected</span> <span class="nb">method</span> <span class="sb">`[]=' called for #<struct Location latitude=53.1242, longitude=nil>
</span></code></pre></div>
<h3>Grape & Devise. The Bad</h3>
<h4>Grape</h4>
<p>Let’s look at an example use of <code>protected</code> in a simple coercion class. I’ll provide code, without comments, full source code <a href="https://github.com/ruby-grape/grape/blob/220c345dff9602e431ac780abcb98dbb24293395/lib/grape/validations/types/json.rb#L33-L41">here</a></p>
<div class="highlight"><pre class="highlight ruby"><code><span class="k">module</span> <span class="nn">Grape</span>
<span class="k">module</span> <span class="nn">Validations</span>
<span class="k">module</span> <span class="nn">Types</span>
<span class="k">class</span> <span class="nc">Json</span> <span class="o"><</span> <span class="no">Virtus</span><span class="o">::</span><span class="no">Attribute</span>
<span class="k">def</span> <span class="nf">coerce</span><span class="p">(</span><span class="n">input</span><span class="p">)</span>
<span class="k">return</span> <span class="k">if</span> <span class="n">input</span><span class="p">.</span><span class="nf">nil?</span> <span class="o">||</span> <span class="n">input</span> <span class="o">=~</span> <span class="sr">/^\\s*$/</span>
<span class="no">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">input</span><span class="p">,</span> <span class="ss">symbolize_names: </span><span class="kp">true</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">value_coerced?</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="n">value</span><span class="p">.</span><span class="nf">is_a?</span><span class="p">(</span><span class="o">::</span><span class="no">Hash</span><span class="p">)</span> <span class="o">||</span> <span class="n">coerced_collection?</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="k">end</span>
<span class="kp">protected</span>
<span class="k">def</span> <span class="nf">coerced_collection?</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="n">value</span><span class="p">.</span><span class="nf">is_a?</span><span class="p">(</span><span class="o">::</span><span class="no">Array</span><span class="p">)</span> <span class="o">&&</span> <span class="n">value</span><span class="p">.</span><span class="nf">all?</span> <span class="p">{</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span> <span class="n">i</span><span class="p">.</span><span class="nf">is_a?</span> <span class="o">::</span><span class="no">Hash</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">JsonArray</span> <span class="o"><</span> <span class="no">Json</span>
<span class="k">def</span> <span class="nf">coerce</span><span class="p">(</span><span class="n">input</span><span class="p">)</span>
<span class="n">json</span> <span class="o">=</span> <span class="k">super</span>
<span class="no">Array</span><span class="p">.</span><span class="nf">wrap</span><span class="p">(</span><span class="n">json</span><span class="p">)</span> <span class="k">unless</span> <span class="n">json</span><span class="p">.</span><span class="nf">nil?</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">value_coerced?</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="n">coerced_collection?</span> <span class="n">value</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div>
<p>Here you can see an example of very common misconception that <code>private</code> methods can’t be called inside child classes and <code>protected</code> methods should be called instead <em>(As in Java</em>C)/. It is wrong. You don’t need <code>protected</code> here, If we replace it with <code>private</code> the code will work just fine. And it would be better, because it will be less confusing for a reader.</p>
<h4>Devise</h4>
<p>Repeats the same mistake Grape does. It uses <code>protected</code> in modules, that are intended be included in user generated models and controllers. So, <code>protected</code> in this case can be replaced with <code>private</code> as those methods are never called on instance</p>
<h3>Rails. The Ugly</h3>
<p>First, in <code>ActionDispatch::Routing::UrlFor</code> module we define a protected method <code>optimize_routes_generation?</code></p>
<div class="highlight"><pre class="highlight ruby"><code><span class="kp">protected</span>
<span class="k">def</span> <span class="nf">optimize_routes_generation?</span>
<span class="n">_routes</span><span class="p">.</span><span class="nf">optimize_routes_generation?</span> <span class="o">&&</span> <span class="n">default_url_options</span><span class="p">.</span><span class="nf">empty?</span>
<span class="k">end</span>
</code></pre></div>
<p>And then later in code call it via <code>send</code> from <code>ActionDispatch::Routing::RouteSet::NamedRouteCollection::UrlHelper::OptimizedUrlHelper</code></p>
<div class="highlight"><pre class="highlight ruby"><code><span class="k">def</span> <span class="nf">optimize_routes_generation?</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
<span class="n">t</span><span class="p">.</span><span class="nf">send</span><span class="p">(</span><span class="ss">:optimize_routes_generation?</span><span class="p">)</span>
<span class="k">end</span>
</code></pre></div>
<p>The main point, of <code>protected</code> is to allow instances of the same and descendant classes to call interior methods, that no one else outside have no need to know about. If some outside instance needs to call <code>protected</code> method, just leave it <code>public</code></p>