<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://dmitrytsepelev.dev/feed.xml" rel="self" type="application/atom+xml" /><link href="https://dmitrytsepelev.dev/" rel="alternate" type="text/html" /><updated>2026-04-25T11:59:10+00:00</updated><id>https://dmitrytsepelev.dev/feed.xml</id><title type="html">dmitrytsepelev.dev</title><subtitle>A home for my blog posts</subtitle><entry><title type="html">Terminal–based game in 150 lines</title><link href="https://dmitrytsepelev.dev/terminal-game" rel="alternate" type="text/html" title="Terminal–based game in 150 lines" /><published>2024-09-24T06:00:00+00:00</published><updated>2024-09-24T06:00:00+00:00</updated><id>https://dmitrytsepelev.dev/terminal-game</id><content type="html" xml:base="https://dmitrytsepelev.dev/terminal-game"><![CDATA[<p>In this article we will write a terminal–based real–time dungeon crawler. I will to keep it under 150 lines with idiomatic (no code golfing!) Ruby code.</p>

<p><img src="/assets/game.gif" alt="Result game" /></p>

<h1 id="main-game-loop">Main game loop</h1>

<p>I want to start with something basic. We need a class that will contain our game state and draw the screen. Let’s create a <code class="language-plaintext highlighter-rouge">game.rb</code> with the following content:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Game</span>
  <span class="no">SLEEP_INTERVAL</span> <span class="o">=</span> <span class="mf">0.2</span>

  <span class="k">def</span> <span class="nf">run</span>
    <span class="kp">loop</span> <span class="k">do</span>
      <span class="n">draw_screen</span>
      <span class="nb">sleep</span> <span class="no">SLEEP_INTERVAL</span>
    <span class="k">end</span>
  <span class="k">end</span>

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

  <span class="k">def</span> <span class="nf">draw_screen</span>
    <span class="nb">system</span> <span class="s2">"clear"</span>

    <span class="nb">puts</span> <span class="no">Time</span><span class="p">.</span><span class="nf">now</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="no">Game</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">run</span>
</code></pre></div></div>

<blockquote>
  <p>Source is <a href="https://gist.github.com/DmitryTsepelev/1e9d73db26aa19d4ad2bd6ddbd67f045#file-1_game-rb">here</a></p>
</blockquote>

<p>Run it (using <code class="language-plaintext highlighter-rouge">ruby ./game.rb</code>) and you see that it updates the current time every <code class="language-plaintext highlighter-rouge">SLEEP_INTERVAL</code> seconds. Nothing fancy yet!</p>

<h1 id="rendering-the-map">Rendering the map</h1>

<p>Our next goal is to render a level map. Let’s keep the map in the file called <code class="language-plaintext highlighter-rouge">map.txt</code>, which will be in the same directory as a <code class="language-plaintext highlighter-rouge">game.rb</code>. We need five types of objects:</p>

<ul>
  <li>an empty place (<code class="language-plaintext highlighter-rouge">s</code>);</li>
  <li>a start point—a place where player appears (<code class="language-plaintext highlighter-rouge">p</code>);</li>
  <li>a final point—an exit from the level (<code class="language-plaintext highlighter-rouge">d</code>);</li>
  <li>a tree—the cell that could not be entered (<code class="language-plaintext highlighter-rouge">t</code>);</li>
  <li>an enemy—something that kills the player when they appear on the same place (<code class="language-plaintext highlighter-rouge">e</code>).</li>
</ul>

<p>Feel free to design you own map, or use mine:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>tsssstsdst
tsssssesst
tstssssset
tstsssssst
tsstssssst
tssssstsst
tstssstsst
tpstsstsst
</code></pre></div></div>

<p>Here is a class that parses the file and renders trees and empty spaces:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">TREE</span><span class="p">,</span> <span class="no">SPACE</span> <span class="o">=</span> <span class="s1">'🌲'</span><span class="p">,</span> <span class="s2">"・"</span>

<span class="no">Level</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">:map</span><span class="p">,</span> <span class="ss">:enemies</span><span class="p">,</span> <span class="ss">:player</span><span class="p">,</span> <span class="ss">:door</span><span class="p">)</span>

<span class="k">class</span> <span class="nc">LevelBuilder</span>
  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">filepath</span><span class="p">)</span>
    <span class="vi">@filepath</span> <span class="o">=</span> <span class="n">filepath</span>
  <span class="k">end</span>

  <span class="no">MAPPING</span> <span class="o">=</span> <span class="p">{</span> <span class="s1">'t'</span> <span class="o">=&gt;</span> <span class="no">TREE</span><span class="p">,</span> <span class="s1">'s'</span> <span class="o">=&gt;</span> <span class="no">SPACE</span> <span class="p">}</span>

  <span class="k">def</span> <span class="nf">build</span>
    <span class="no">Level</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">tap</span> <span class="k">do</span> <span class="o">|</span><span class="n">level</span><span class="o">|</span>
      <span class="n">level</span><span class="p">.</span><span class="nf">enemies</span> <span class="o">=</span> <span class="p">[]</span>

      <span class="n">level</span><span class="p">.</span><span class="nf">map</span> <span class="o">=</span> <span class="no">File</span><span class="p">.</span><span class="nf">readlines</span><span class="p">(</span><span class="vi">@filepath</span><span class="p">).</span><span class="nf">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">line</span><span class="o">|</span>
        <span class="n">line</span><span class="p">.</span><span class="nf">sub</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">,</span> <span class="s2">""</span><span class="p">).</span><span class="nf">chars</span><span class="p">.</span><span class="nf">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">c</span><span class="o">|</span>
          <span class="no">MAPPING</span><span class="p">[</span><span class="n">c</span><span class="p">]</span> <span class="o">||</span> <span class="no">SPACE</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></div>

<p>Note that we do not handle start location, final location and enemies yet. Instead, we treat them as empty spaces: since we will have a specific logic tied to them, we will add it later.</p>

<p>Now we need to update our game class to draw (and redraw) the field:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Game</span>
  <span class="no">SLEEP_INTERVAL</span> <span class="o">=</span> <span class="mf">0.2</span>

  <span class="k">def</span> <span class="nf">run</span>
    <span class="vi">@level</span> <span class="o">=</span> <span class="no">LevelBuilder</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s2">"./map.txt"</span><span class="p">).</span><span class="nf">build</span>

    <span class="kp">loop</span> <span class="k">do</span>
      <span class="n">draw_screen</span>
      <span class="nb">sleep</span> <span class="no">SLEEP_INTERVAL</span>
    <span class="k">end</span>
  <span class="k">end</span>

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

  <span class="k">def</span> <span class="nf">draw_screen</span>
    <span class="nb">system</span> <span class="s2">"clear"</span>

    <span class="vi">@level</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">row</span><span class="o">|</span>
      <span class="n">row</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">cell</span><span class="o">|</span>
        <span class="nb">print</span> <span class="n">cell</span>
      <span class="k">end</span>
      <span class="nb">puts</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>The only thing it does is just prints whatever we have in in the <code class="language-plaintext highlighter-rouge">map</code> field. The only thing that might be unfamiliar to you is <code class="language-plaintext highlighter-rouge">system "clear"</code>, which just cleans up the terminal.</p>

<blockquote>
  <p>Source is <a href="https://gist.github.com/DmitryTsepelev/1e9d73db26aa19d4ad2bd6ddbd67f045#file-2_game-rb">here</a></p>
</blockquote>

<h1 id="adding-complex-objects">Adding complex objects</h1>

<p>Now we need to render player, enemies and door. Let’s extend our <code class="language-plaintext highlighter-rouge">Level</code> class definition to support them and decide how we will draw them on the screen:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">PLAYER</span><span class="p">,</span> <span class="no">ENEMY</span><span class="p">,</span> <span class="no">DOOR</span><span class="p">,</span> <span class="no">TREE</span><span class="p">,</span> <span class="no">SPACE</span> <span class="o">=</span> <span class="s1">'🧙'</span><span class="p">,</span> <span class="s1">'👻'</span><span class="p">,</span> <span class="s1">'🚪'</span><span class="p">,</span> <span class="s1">'🌲'</span><span class="p">,</span> <span class="s2">"・"</span>

<span class="no">Level</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">:map</span><span class="p">,</span> <span class="ss">:enemies</span><span class="p">,</span> <span class="ss">:player</span><span class="p">,</span> <span class="ss">:door</span><span class="p">)</span>
</code></pre></div></div>

<p>After that, let’s define another struct called <code class="language-plaintext highlighter-rouge">DynamicObject</code> to represent them:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">DynamicObject</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">:row_idx</span><span class="p">,</span> <span class="ss">:col_idx</span><span class="p">,</span> <span class="ss">:kind</span><span class="p">)</span>
</code></pre></div></div>

<p>Now we need to adjust our <code class="language-plaintext highlighter-rouge">LevelBuilder</code> to handle new objects. Note that when we find one of those we assign them to separate fields and treat them as empty spaces on the map, because they can change their location:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">LevelBuilder</span>
  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">filepath</span><span class="p">)</span>
    <span class="vi">@filepath</span> <span class="o">=</span> <span class="n">filepath</span>
  <span class="k">end</span>

  <span class="no">MAPPING</span> <span class="o">=</span> <span class="p">{</span> <span class="s1">'t'</span> <span class="o">=&gt;</span> <span class="no">TREE</span><span class="p">,</span> <span class="s1">'s'</span> <span class="o">=&gt;</span> <span class="no">SPACE</span> <span class="p">}</span>

  <span class="k">def</span> <span class="nf">build</span>
    <span class="no">Level</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">tap</span> <span class="k">do</span> <span class="o">|</span><span class="n">level</span><span class="o">|</span>
      <span class="n">level</span><span class="p">.</span><span class="nf">enemies</span> <span class="o">=</span> <span class="p">[]</span>

      <span class="n">level</span><span class="p">.</span><span class="nf">map</span> <span class="o">=</span> <span class="no">File</span><span class="p">.</span><span class="nf">readlines</span><span class="p">(</span><span class="vi">@filepath</span><span class="p">).</span><span class="nf">map</span><span class="p">.</span><span class="nf">with_index</span> <span class="k">do</span> <span class="o">|</span><span class="n">line</span><span class="p">,</span> <span class="n">row_idx</span><span class="o">|</span>
        <span class="n">line</span><span class="p">.</span><span class="nf">strip</span><span class="p">.</span><span class="nf">chars</span><span class="p">.</span><span class="nf">map</span><span class="p">.</span><span class="nf">with_index</span> <span class="k">do</span> <span class="o">|</span><span class="n">c</span><span class="p">,</span> <span class="n">col_idx</span><span class="o">|</span>
          <span class="k">case</span> <span class="n">c</span>
          <span class="k">when</span> <span class="s1">'e'</span>
            <span class="n">level</span><span class="p">.</span><span class="nf">enemies</span> <span class="o">&lt;&lt;</span> <span class="no">DynamicObject</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">row_idx</span><span class="p">,</span> <span class="n">col_idx</span><span class="p">,</span> <span class="ss">:enemy</span><span class="p">)</span>
            <span class="no">SPACE</span>
          <span class="k">when</span> <span class="s1">'p'</span>
            <span class="n">level</span><span class="p">.</span><span class="nf">player</span> <span class="o">=</span> <span class="no">DynamicObject</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">row_idx</span><span class="p">,</span> <span class="n">col_idx</span><span class="p">,</span> <span class="ss">:player</span><span class="p">)</span>
            <span class="no">SPACE</span>
          <span class="k">when</span> <span class="s1">'d'</span>
            <span class="n">level</span><span class="p">.</span><span class="nf">door</span> <span class="o">=</span> <span class="no">DynamicObject</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">row_idx</span><span class="p">,</span> <span class="n">col_idx</span><span class="p">,</span> <span class="ss">:door</span><span class="p">)</span>
            <span class="no">SPACE</span>
          <span class="k">else</span>
            <span class="no">MAPPING</span><span class="p">[</span><span class="n">c</span><span class="p">]</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>
<span class="k">end</span>
</code></pre></div></div>

<p>Finally, we need to update <code class="language-plaintext highlighter-rouge">Game#draw_screen</code> to support our new primitives:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">draw_screen</span>
  <span class="nb">system</span> <span class="s2">"clear"</span>

  <span class="vi">@level</span><span class="p">.</span><span class="nf">map</span><span class="p">.</span><span class="nf">each_with_index</span> <span class="k">do</span> <span class="o">|</span><span class="n">row</span><span class="p">,</span> <span class="n">row_idx</span><span class="o">|</span>
    <span class="n">row</span><span class="p">.</span><span class="nf">each_with_index</span> <span class="k">do</span> <span class="o">|</span><span class="n">cell</span><span class="p">,</span> <span class="n">col_idx</span><span class="o">|</span>
      <span class="k">if</span> <span class="vi">@level</span><span class="p">.</span><span class="nf">player</span><span class="p">.</span><span class="nf">row_idx</span> <span class="o">==</span> <span class="n">row_idx</span> <span class="o">&amp;&amp;</span> <span class="vi">@level</span><span class="p">.</span><span class="nf">player</span><span class="p">.</span><span class="nf">col_idx</span> <span class="o">==</span> <span class="n">col_idx</span>
        <span class="nb">print</span> <span class="no">PLAYER</span>
      <span class="k">elsif</span> <span class="vi">@level</span><span class="p">.</span><span class="nf">door</span><span class="p">.</span><span class="nf">row_idx</span> <span class="o">==</span> <span class="n">row_idx</span> <span class="o">&amp;&amp;</span> <span class="vi">@level</span><span class="p">.</span><span class="nf">door</span><span class="p">.</span><span class="nf">col_idx</span> <span class="o">==</span> <span class="n">col_idx</span>
        <span class="nb">print</span> <span class="no">DOOR</span>
      <span class="k">elsif</span> <span class="vi">@level</span><span class="p">.</span><span class="nf">enemies</span><span class="p">.</span><span class="nf">find</span> <span class="p">{</span> <span class="o">|</span><span class="n">enemy</span><span class="o">|</span> <span class="n">enemy</span><span class="p">.</span><span class="nf">row_idx</span> <span class="o">==</span> <span class="n">row_idx</span> <span class="o">&amp;&amp;</span> <span class="n">enemy</span><span class="p">.</span><span class="nf">col_idx</span> <span class="o">==</span> <span class="n">col_idx</span> <span class="p">}</span>
        <span class="nb">print</span> <span class="no">ENEMY</span>
      <span class="k">else</span>
        <span class="nb">print</span> <span class="n">cell</span>
      <span class="k">end</span>
    <span class="k">end</span>
    <span class="nb">puts</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">"</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<blockquote>
  <p>You can find the whole snippet <a href="https://gist.github.com/DmitryTsepelev/1e9d73db26aa19d4ad2bd6ddbd67f045#file-3_game-rb">here</a></p>
</blockquote>

<h1 id="how-to-read-user-input">How to read user input</h1>

<p>Our next goal is to find a way to control the character. We want to be able to move it up, down, left and right. Let’s start with the code that checks if any key is pressed right now.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Game</span>
  <span class="c1"># ...</span>

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

  <span class="k">def</span> <span class="nf">get_pressed_key</span>
    <span class="k">begin</span>
      <span class="nb">system</span><span class="p">(</span><span class="s1">'stty raw -echo'</span><span class="p">)</span>
      <span class="p">(</span><span class="no">STDIN</span><span class="p">.</span><span class="nf">read_nonblock</span><span class="p">(</span><span class="mi">4</span><span class="p">).</span><span class="nf">ord</span> <span class="k">rescue</span> <span class="kp">nil</span><span class="p">)</span>
    <span class="k">ensure</span>
      <span class="nb">system</span><span class="p">(</span><span class="s1">'stty -raw echo'</span><span class="p">)</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>This looks like a bit cryptic, so we will examine it line by line:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">stty raw -echo</code> turns off echoing of input keystrokes;</li>
  <li><code class="language-plaintext highlighter-rouge">STDIN.read_nonblock(4)</code> reads 4 bytes from user input, and <code class="language-plaintext highlighter-rouge">.ord</code> returns the integer representation of the entered character (e.g., <code class="language-plaintext highlighter-rouge">'w'.ord == 19</code>);</li>
  <li><code class="language-plaintext highlighter-rouge">stty -raw echo</code> turns echoing back on.</li>
</ul>

<p>Let’s call this method inside the game loop and use the value to update player’s position:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Game</span>
  <span class="c1"># ...</span>

  <span class="k">def</span> <span class="nf">run</span>
    <span class="vi">@level</span> <span class="o">=</span> <span class="no">LevelBuilder</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s2">"./map.txt"</span><span class="p">).</span><span class="nf">build</span>

    <span class="kp">loop</span> <span class="k">do</span>
      <span class="n">draw_screen</span>

      <span class="n">new_player_position</span> <span class="o">=</span> <span class="no">DynamicObject</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="vi">@level</span><span class="p">.</span><span class="nf">player</span><span class="p">.</span><span class="nf">row_idx</span><span class="p">,</span> <span class="vi">@level</span><span class="p">.</span><span class="nf">player</span><span class="p">.</span><span class="nf">col_idx</span><span class="p">,</span> <span class="ss">:player</span><span class="p">)</span>
      <span class="n">new_player_position</span><span class="p">.</span><span class="nf">move</span><span class="p">(</span><span class="n">get_pressed_key</span><span class="p">)</span>

      <span class="vi">@level</span><span class="p">.</span><span class="nf">player</span> <span class="o">=</span> <span class="n">new_player_position</span>

      <span class="nb">sleep</span> <span class="no">SLEEP_INTERVAL</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="c1"># ...</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Finally, we have to implement <code class="language-plaintext highlighter-rouge">DynamicObject#move</code>. Let’s use <code class="language-plaintext highlighter-rouge">WASD</code> keys to control movements:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">UP</span><span class="p">,</span> <span class="no">DOWN</span><span class="p">,</span> <span class="no">RIGHT</span><span class="p">,</span> <span class="no">LEFT</span> <span class="o">=</span> <span class="mi">119</span><span class="p">,</span> <span class="mi">115</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">97</span>

<span class="no">DynamicObject</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">:row_idx</span><span class="p">,</span> <span class="ss">:col_idx</span><span class="p">,</span> <span class="ss">:kind</span><span class="p">)</span> <span class="k">do</span>
  <span class="k">def</span> <span class="nf">move</span><span class="p">(</span><span class="n">dir</span><span class="p">)</span>
    <span class="k">case</span> <span class="n">dir</span>
    <span class="k">when</span> <span class="no">RIGHT</span> <span class="k">then</span> <span class="nb">self</span><span class="p">.</span><span class="nf">col_idx</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="k">when</span> <span class="no">LEFT</span> <span class="k">then</span> <span class="nb">self</span><span class="p">.</span><span class="nf">col_idx</span> <span class="o">-=</span> <span class="mi">1</span>
    <span class="k">when</span> <span class="no">UP</span> <span class="k">then</span> <span class="nb">self</span><span class="p">.</span><span class="nf">row_idx</span> <span class="o">-=</span> <span class="mi">1</span>
    <span class="k">when</span> <span class="no">DOWN</span> <span class="k">then</span> <span class="nb">self</span><span class="p">.</span><span class="nf">row_idx</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<blockquote>
  <p>Source is <a href="https://gist.github.com/DmitryTsepelev/1e9d73db26aa19d4ad2bd6ddbd67f045#file-4_game-rb">here</a></p>
</blockquote>

<p>Try it out! You can see that player position is updated on the screen.</p>

<blockquote>
  <p>If game feels a bit slow to you—try to play with <code class="language-plaintext highlighter-rouge">SLEEP_INTERVAL</code> value.</p>
</blockquote>

<h1 id="collision-handling">Collision handling</h1>

<p>If you ran the previous example, you might have noticed that player can step on trees, ghosts, door and even leave the screen. The reason is that we update the position regardless of what is placed under it. Let’s fix that!</p>

<p>First of all, we need to add a method that will check new position and tell us what will happen if we move object there. Note that we will make this method generic: it will be able to check if passed object collides with any of objects we pass. We will use it to handle other collisions later.</p>

<p>There can be multiple outcomes:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">:out_of_border</code> is returned when object attempts to leave the border of the level;</li>
  <li><code class="language-plaintext highlighter-rouge">:tree</code>, <code class="language-plaintext highlighter-rouge">:ghost</code>, <code class="language-plaintext highlighter-rouge">:door</code> or <code class="language-plaintext highlighter-rouge">:player</code> is returned when object bumps into the corresponding object.</li>
</ul>

<p>This is how <code class="language-plaintext highlighter-rouge">Game#run</code> looks like:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Game</span>
  <span class="c1"># ...</span>

  <span class="k">def</span> <span class="nf">run</span>
    <span class="vi">@level</span> <span class="o">=</span> <span class="no">LevelBuilder</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s2">"./map.txt"</span><span class="p">).</span><span class="nf">build</span>

    <span class="kp">loop</span> <span class="k">do</span>
      <span class="n">draw_screen</span>

      <span class="n">new_player_position</span> <span class="o">=</span> <span class="no">DynamicObject</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="vi">@level</span><span class="p">.</span><span class="nf">player</span><span class="p">.</span><span class="nf">row_idx</span><span class="p">,</span> <span class="vi">@level</span><span class="p">.</span><span class="nf">player</span><span class="p">.</span><span class="nf">col_idx</span><span class="p">,</span> <span class="ss">:player</span><span class="p">)</span>
      <span class="n">new_player_position</span><span class="p">.</span><span class="nf">move</span><span class="p">(</span><span class="n">get_pressed_key</span><span class="p">)</span>

      <span class="k">case</span> <span class="n">check_collision</span><span class="p">(</span><span class="n">new_player_position</span><span class="p">.</span><span class="nf">row_idx</span><span class="p">,</span> <span class="n">new_player_position</span><span class="p">.</span><span class="nf">col_idx</span><span class="p">,</span> <span class="vi">@level</span><span class="p">.</span><span class="nf">enemies</span> <span class="o">+</span> <span class="p">[</span><span class="vi">@level</span><span class="p">.</span><span class="nf">door</span><span class="p">])</span>
      <span class="k">when</span> <span class="ss">:door</span>
        <span class="nb">puts</span> <span class="s2">"🎉 Level passed 🎉"</span>
        <span class="k">break</span>
      <span class="k">when</span> <span class="ss">:enemy</span>
        <span class="nb">puts</span> <span class="s2">"☠️ You died ☠️"</span>
        <span class="k">break</span>
      <span class="k">when</span> <span class="kp">nil</span>
        <span class="vi">@level</span><span class="p">.</span><span class="nf">player</span> <span class="o">=</span> <span class="n">new_player_position</span>
      <span class="k">end</span>

      <span class="nb">sleep</span> <span class="no">SLEEP_INTERVAL</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="c1"># ...</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Now we should implement <code class="language-plaintext highlighter-rouge">Game#check_collision</code>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Game</span>
  <span class="c1"># ...</span>

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

  <span class="k">def</span> <span class="nf">check_collision</span><span class="p">(</span><span class="n">row_idx</span><span class="p">,</span> <span class="n">col_idx</span><span class="p">,</span> <span class="n">objects</span><span class="p">)</span>
    <span class="k">return</span> <span class="ss">:out_of_border</span> <span class="k">if</span> <span class="n">row_idx</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">row_idx</span> <span class="o">&gt;=</span> <span class="vi">@level</span><span class="p">.</span><span class="nf">map</span><span class="p">.</span><span class="nf">length</span> <span class="o">||</span> <span class="n">col_idx</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">col_idx</span> <span class="o">&gt;=</span> <span class="vi">@level</span><span class="p">.</span><span class="nf">map</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">length</span>
    <span class="k">return</span> <span class="ss">:tree</span> <span class="k">if</span> <span class="vi">@level</span><span class="p">.</span><span class="nf">map</span><span class="p">[</span><span class="n">row_idx</span><span class="p">][</span><span class="n">col_idx</span><span class="p">]</span> <span class="o">==</span> <span class="no">TREE</span>
    <span class="n">objects</span><span class="p">.</span><span class="nf">find</span> <span class="p">{</span> <span class="n">_1</span><span class="p">.</span><span class="nf">row_idx</span> <span class="o">==</span> <span class="n">row_idx</span> <span class="o">&amp;&amp;</span> <span class="n">_1</span><span class="p">.</span><span class="nf">col_idx</span> <span class="o">==</span> <span class="n">col_idx</span> <span class="p">}</span><span class="o">&amp;</span><span class="p">.</span><span class="nf">kind</span>
  <span class="k">end</span>
  <span class="c1"># ...</span>
<span class="k">end</span>
</code></pre></div></div>

<blockquote>
  <p>Source is <a href="https://gist.github.com/DmitryTsepelev/1e9d73db26aa19d4ad2bd6ddbd67f045#file-5_game-rb">here</a></p>
</blockquote>

<p>Try to run the game and see that now we handle collisions properly: there’s no way to cross the border, step on the tree and game ends when you bump into the ghost or door.</p>

<h1 id="adding-fancy-ai-to-our-ghosts">Adding fancy AI to our ghosts</h1>

<p>Our ghosts are not dangerous at all, because they can’t move, let’s address that! First of all, we need to change our main loop to trigger movement. Moreover, we should check the collisions after that to make sure that ghost did not bump into the player:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Game</span>
  <span class="c1"># ...</span>

  <span class="k">def</span> <span class="nf">run</span>
    <span class="vi">@level</span> <span class="o">=</span> <span class="no">LevelBuilder</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s2">"./map.txt"</span><span class="p">).</span><span class="nf">build</span>

    <span class="kp">loop</span> <span class="k">do</span>
      <span class="n">move_enemies</span>
      <span class="n">draw_screen</span>

      <span class="k">case</span> <span class="n">check_collision</span><span class="p">(</span><span class="vi">@level</span><span class="p">.</span><span class="nf">player</span><span class="p">.</span><span class="nf">row_idx</span><span class="p">,</span> <span class="vi">@level</span><span class="p">.</span><span class="nf">player</span><span class="p">.</span><span class="nf">col_idx</span><span class="p">,</span> <span class="vi">@level</span><span class="p">.</span><span class="nf">enemies</span><span class="p">)</span>
      <span class="k">when</span> <span class="ss">:enemy</span>
        <span class="nb">puts</span> <span class="s2">"☠️ You died ☠️"</span>
        <span class="k">break</span>
      <span class="k">end</span>

      <span class="n">new_player_position</span> <span class="o">=</span> <span class="no">DynamicObject</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="vi">@level</span><span class="p">.</span><span class="nf">player</span><span class="p">.</span><span class="nf">row_idx</span><span class="p">,</span> <span class="vi">@level</span><span class="p">.</span><span class="nf">player</span><span class="p">.</span><span class="nf">col_idx</span><span class="p">,</span> <span class="ss">:player</span><span class="p">)</span>
      <span class="n">new_player_position</span><span class="p">.</span><span class="nf">move</span><span class="p">(</span><span class="n">get_pressed_key</span><span class="p">)</span>

      <span class="k">case</span> <span class="n">check_collision</span><span class="p">(</span><span class="n">new_player_position</span><span class="p">.</span><span class="nf">row_idx</span><span class="p">,</span> <span class="n">new_player_position</span><span class="p">.</span><span class="nf">col_idx</span><span class="p">,</span> <span class="vi">@level</span><span class="p">.</span><span class="nf">enemies</span> <span class="o">+</span> <span class="p">[</span><span class="vi">@level</span><span class="p">.</span><span class="nf">door</span><span class="p">])</span>
      <span class="k">when</span> <span class="ss">:door</span>
        <span class="nb">puts</span> <span class="s2">"🎉 Level passed 🎉"</span>
        <span class="k">break</span>
      <span class="k">when</span> <span class="ss">:enemy</span>
        <span class="nb">puts</span> <span class="s2">"☠️ You died ☠️"</span>
        <span class="k">break</span>
      <span class="k">when</span> <span class="kp">nil</span>
        <span class="vi">@level</span><span class="p">.</span><span class="nf">player</span> <span class="o">=</span> <span class="n">new_player_position</span>
      <span class="k">end</span>

      <span class="nb">sleep</span> <span class="no">SLEEP_INTERVAL</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="c1"># ...</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Now we can implement the smart AI–powered algorithm that makes decision on enemy movement:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Game</span>
  <span class="c1"># ...</span>

  <span class="k">def</span> <span class="nf">move_enemies</span>
    <span class="vi">@level</span><span class="p">.</span><span class="nf">enemies</span><span class="p">.</span><span class="nf">each_with_index</span> <span class="k">do</span> <span class="o">|</span><span class="n">enemy</span><span class="p">,</span> <span class="n">idx</span><span class="o">|</span>
      <span class="k">next</span> <span class="k">if</span> <span class="nb">rand</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mf">0.8</span>

      <span class="n">new_enemy</span> <span class="o">=</span> <span class="no">DynamicObject</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">enemy</span><span class="p">.</span><span class="nf">row_idx</span><span class="p">,</span> <span class="n">enemy</span><span class="p">.</span><span class="nf">col_idx</span><span class="p">,</span> <span class="ss">:enemy</span><span class="p">)</span>
      <span class="n">new_enemy</span><span class="p">.</span><span class="nf">move</span><span class="p">([</span><span class="no">RIGHT</span><span class="p">,</span> <span class="no">LEFT</span><span class="p">,</span> <span class="no">UP</span><span class="p">,</span> <span class="no">DOWN</span><span class="p">].</span><span class="nf">sample</span><span class="p">)</span>
      <span class="vi">@level</span><span class="p">.</span><span class="nf">enemies</span><span class="p">[</span><span class="n">idx</span><span class="p">]</span> <span class="o">=</span> <span class="n">new_enemy</span> <span class="k">if</span> <span class="n">check_collision</span><span class="p">(</span><span class="n">new_enemy</span><span class="p">.</span><span class="nf">row_idx</span><span class="p">,</span> <span class="n">new_enemy</span><span class="p">.</span><span class="nf">col_idx</span><span class="p">,</span> <span class="p">[</span><span class="vi">@level</span><span class="p">.</span><span class="nf">door</span><span class="p">]).</span><span class="nf">nil?</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="c1"># ...</span>
</code></pre></div></div>

<blockquote>
  <p>Source is <a href="https://gist.github.com/DmitryTsepelev/1e9d73db26aa19d4ad2bd6ddbd67f045#file-6_game-rb">here</a></p>
</blockquote>

<h1 id="extracting-the-rendering-logic">Extracting the rendering logic</h1>

<p>I have a feeling that our <code class="language-plaintext highlighter-rouge">Game</code> class becomes some kind of a <a href="https://en.wikipedia.org/wiki/God_object">God object</a>: it handles user input, screen rendering and movement logic. Let’s try to extract all rendering–related code to the separate class:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Screen</span>
  <span class="k">def</span> <span class="nf">render_level</span><span class="p">(</span><span class="n">level</span><span class="p">)</span>
    <span class="nb">system</span> <span class="s2">"clear"</span>

    <span class="n">level</span><span class="p">.</span><span class="nf">map</span><span class="p">.</span><span class="nf">each_with_index</span> <span class="k">do</span> <span class="o">|</span><span class="n">row</span><span class="p">,</span> <span class="n">row_idx</span><span class="o">|</span>
      <span class="n">row</span><span class="p">.</span><span class="nf">each_with_index</span> <span class="k">do</span> <span class="o">|</span><span class="n">cell</span><span class="p">,</span> <span class="n">col_idx</span><span class="o">|</span>
        <span class="k">if</span> <span class="n">level</span><span class="p">.</span><span class="nf">player</span><span class="p">.</span><span class="nf">row_idx</span> <span class="o">==</span> <span class="n">row_idx</span> <span class="o">&amp;&amp;</span> <span class="n">level</span><span class="p">.</span><span class="nf">player</span><span class="p">.</span><span class="nf">col_idx</span> <span class="o">==</span> <span class="n">col_idx</span>
          <span class="nb">print</span> <span class="no">PLAYER</span>
        <span class="k">elsif</span> <span class="n">level</span><span class="p">.</span><span class="nf">door</span><span class="p">.</span><span class="nf">row_idx</span> <span class="o">==</span> <span class="n">row_idx</span> <span class="o">&amp;&amp;</span> <span class="n">level</span><span class="p">.</span><span class="nf">door</span><span class="p">.</span><span class="nf">col_idx</span> <span class="o">==</span> <span class="n">col_idx</span>
          <span class="nb">print</span> <span class="no">DOOR</span>
        <span class="k">elsif</span> <span class="n">level</span><span class="p">.</span><span class="nf">enemies</span><span class="p">.</span><span class="nf">find</span> <span class="p">{</span> <span class="o">|</span><span class="n">enemy</span><span class="o">|</span> <span class="n">enemy</span><span class="p">.</span><span class="nf">row_idx</span> <span class="o">==</span> <span class="n">row_idx</span> <span class="o">&amp;&amp;</span> <span class="n">enemy</span><span class="p">.</span><span class="nf">col_idx</span> <span class="o">==</span> <span class="n">col_idx</span> <span class="p">}</span>
          <span class="nb">print</span> <span class="no">ENEMY</span>
        <span class="k">else</span>
          <span class="nb">print</span> <span class="n">cell</span>
        <span class="k">end</span>
      <span class="k">end</span>
      <span class="nb">puts</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">"</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">render_death_message</span> <span class="o">=</span> <span class="nb">puts</span> <span class="s2">"☠️ You died ☠️"</span>
  <span class="k">def</span> <span class="nf">render_level_passed_message</span> <span class="o">=</span> <span class="nb">puts</span> <span class="s2">"🎉 Level passed 🎉"</span>
<span class="k">end</span>
</code></pre></div></div>

<p>With this change we can easily replace terminal with a web page or even print game state to the paper!</p>

<p>Now we can change the main loop <code class="language-plaintext highlighter-rouge">Game</code> class and drop <code class="language-plaintext highlighter-rouge">#draw_screen</code> method:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Game</span>
  <span class="c1"># ...</span>

  <span class="k">def</span> <span class="nf">run</span>
    <span class="n">screen</span> <span class="o">=</span> <span class="no">Screen</span><span class="p">.</span><span class="nf">new</span>

    <span class="vi">@level</span> <span class="o">=</span> <span class="no">LevelBuilder</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s2">"./map.txt"</span><span class="p">).</span><span class="nf">build</span>

    <span class="kp">loop</span> <span class="k">do</span>
      <span class="n">move_enemies</span>
      <span class="n">screen</span><span class="p">.</span><span class="nf">render_level</span><span class="p">(</span><span class="vi">@level</span><span class="p">)</span>

      <span class="k">case</span> <span class="n">check_collision</span><span class="p">(</span><span class="vi">@level</span><span class="p">.</span><span class="nf">player</span><span class="p">.</span><span class="nf">row_idx</span><span class="p">,</span> <span class="vi">@level</span><span class="p">.</span><span class="nf">player</span><span class="p">.</span><span class="nf">col_idx</span><span class="p">,</span> <span class="vi">@level</span><span class="p">.</span><span class="nf">enemies</span><span class="p">)</span>
      <span class="k">when</span> <span class="ss">:enemy</span>
        <span class="n">screen</span><span class="p">.</span><span class="nf">render_death_message</span>
        <span class="k">break</span>
      <span class="k">end</span>

      <span class="n">new_player_position</span> <span class="o">=</span> <span class="no">DynamicObject</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="vi">@level</span><span class="p">.</span><span class="nf">player</span><span class="p">.</span><span class="nf">row_idx</span><span class="p">,</span> <span class="vi">@level</span><span class="p">.</span><span class="nf">player</span><span class="p">.</span><span class="nf">col_idx</span><span class="p">,</span> <span class="ss">:player</span><span class="p">)</span>
      <span class="n">new_player_position</span><span class="p">.</span><span class="nf">move</span><span class="p">(</span><span class="n">get_pressed_key</span><span class="p">)</span>

      <span class="k">case</span> <span class="n">check_collision</span><span class="p">(</span><span class="n">new_player_position</span><span class="p">.</span><span class="nf">row_idx</span><span class="p">,</span> <span class="n">new_player_position</span><span class="p">.</span><span class="nf">col_idx</span><span class="p">,</span> <span class="vi">@level</span><span class="p">.</span><span class="nf">enemies</span> <span class="o">+</span> <span class="p">[</span><span class="vi">@level</span><span class="p">.</span><span class="nf">door</span><span class="p">])</span>
      <span class="k">when</span> <span class="ss">:door</span>
        <span class="n">screen</span><span class="p">.</span><span class="nf">render_level_passed_message</span>
        <span class="k">break</span>
      <span class="k">when</span> <span class="ss">:enemy</span>
        <span class="n">screen</span><span class="p">.</span><span class="nf">render_death_message</span>
        <span class="k">break</span>
      <span class="k">when</span> <span class="kp">nil</span>
        <span class="vi">@level</span><span class="p">.</span><span class="nf">player</span> <span class="o">=</span> <span class="n">new_player_position</span>
      <span class="k">end</span>

      <span class="nb">sleep</span> <span class="no">SLEEP_INTERVAL</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="c1"># ...</span>
<span class="k">end</span>
</code></pre></div></div>

<blockquote>
  <p>Final code is <a href="https://gist.github.com/DmitryTsepelev/1e9d73db26aa19d4ad2bd6ddbd67f045#file-7_game-rb">here</a></p>
</blockquote>

<hr />

<p>Let’s take a break here: we built more or less playable game in 138 lines. I have some more ideas for you to practice:</p>

<ul>
  <li>try to add fireballs that are created when player presses <code class="language-plaintext highlighter-rouge">SPACE</code> and which should eliminate ghosts;</li>
  <li>try to add more static objects (like houses 🏠) to the level—you will probably need to refactor <code class="language-plaintext highlighter-rouge">LevelBuilder#build</code> to some kind of DSL;</li>
  <li>add a way to have more than one level (probably you will need to turn <code class="language-plaintext highlighter-rouge">Game</code> class into the state machine);</li>
  <li>build up level generator to make the game endless.</li>
</ul>]]></content><author><name></name></author><category term="ruby" /><summary type="html"><![CDATA[Terminal–based game written in pure Ruby using less then 150 lines of code]]></summary></entry><entry><title type="html">How to test graphql-ruby responses</title><link href="https://dmitrytsepelev.dev/how-to-test-graphql-ruby-responses" rel="alternate" type="text/html" title="How to test graphql-ruby responses" /><published>2024-08-27T06:00:00+00:00</published><updated>2024-08-27T06:00:00+00:00</updated><id>https://dmitrytsepelev.dev/how-to-test-graphql-ruby-responses</id><content type="html" xml:base="https://dmitrytsepelev.dev/how-to-test-graphql-ruby-responses"><![CDATA[<p>Most of software engineers write tests. Some people use Ruby. Some of them use GraphQL. While testing queries and mutations has little differences compared to controller specs (at the end of the day—they both use HTTP requests), subscriptions are a bit more tricky. In this short post I’ll show you how I test my GraphQL backends.</p>

<p>I will use <a href="https://rspec.info">RSpec</a>, but you can easily port these approaches to any other testing framework you prefer.</p>

<h1 id="queries-and-types">Queries and types</h1>

<p>There are two approaches of testing data fetch: you can check either requests or types. In case of requests, you can have a single spec file that fetches a lot of data. If you decide to go with types—you should have a separate spec per type.</p>

<p>I personally prefer the second approach: it’s more verbose (and you end up with more files) but also gives you more granular control over the spec and makes it harder to miss something.</p>

<blockquote>
  <p>You can even set up Rubocop to enforce every type have a corresponding spec file</p>
</blockquote>

<p>What is the responsibility of the GraphQL type? Technically speaking, it’s a serializer: you give it some data, it transforms the data and returns the result. You can even extract the transformation to the separate PORO class and test it separately.</p>

<p>Here is an example of the spec I usually create:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">describe</span> <span class="no">Types</span><span class="o">::</span><span class="no">PostType</span> <span class="k">do</span>
  <span class="c1"># you can configure RSpec to add this to all specs</span>
  <span class="c1"># inside spec/graphql folder</span>
  <span class="kp">include</span> <span class="no">GraphqlHelpers</span>

  <span class="n">let</span><span class="p">(</span><span class="ss">:query</span><span class="p">)</span> <span class="k">do</span>
    <span class="o">&lt;&lt;~</span><span class="no">GRAPHQL</span><span class="sh">
      query Post($postId: ID!) {
        post(postId: $postId) {
          id
          title
          content
          author {
            id
          }
        }
      }
</span><span class="no">    GRAPHQL</span>
  <span class="k">end</span>

  <span class="n">let</span><span class="p">(</span><span class="ss">:post</span><span class="p">)</span> <span class="p">{</span> <span class="n">create</span> <span class="ss">:post</span> <span class="p">}</span>
  <span class="n">let</span><span class="p">(</span><span class="ss">:variables</span><span class="p">)</span> <span class="p">{</span> <span class="p">{</span> <span class="ss">postId: </span><span class="n">post</span><span class="p">.</span><span class="nf">id</span> <span class="p">}</span> <span class="p">}</span>

  <span class="n">specify</span> <span class="k">do</span>
    <span class="n">perform_request</span>

    <span class="n">expect</span><span class="p">(</span><span class="n">gql_errors</span><span class="p">).</span><span class="nf">to</span> <span class="n">eq</span><span class="p">(</span><span class="kp">nil</span><span class="p">)</span>

    <span class="n">resolved_object</span> <span class="o">=</span> <span class="n">gql_data</span><span class="p">.</span><span class="nf">dig</span><span class="p">(</span><span class="s2">"post"</span><span class="p">)</span>

    <span class="c1"># Ideally you should also make sure that you fetch all fields that are</span>
    <span class="c1"># resolved by this type</span>
    <span class="n">expect</span><span class="p">(</span><span class="n">resolved_object</span><span class="p">).</span><span class="nf">to</span> <span class="n">match</span><span class="p">(</span>
      <span class="s2">"id"</span> <span class="o">=&gt;</span> <span class="n">post</span><span class="p">.</span><span class="nf">id</span><span class="p">.</span><span class="nf">to_s</span><span class="p">,</span>
      <span class="s2">"title"</span> <span class="o">=&gt;</span> <span class="n">post</span><span class="p">.</span><span class="nf">title</span><span class="p">,</span>
      <span class="s2">"content"</span> <span class="o">=&gt;</span> <span class="n">post</span><span class="p">.</span><span class="nf">content</span><span class="p">,</span>
      <span class="s2">"author"</span> <span class="o">=&gt;</span> <span class="p">{</span>
        <span class="s2">"id"</span> <span class="o">=&gt;</span> <span class="n">post</span><span class="p">.</span><span class="nf">author</span><span class="p">.</span><span class="nf">id</span><span class="p">.</span><span class="nf">to_s</span>
        <span class="c1"># note that we do not check contents of the author field, because</span>
        <span class="c1"># it's more straightforward to test Author separately</span>
      <span class="p">}</span>
    <span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>There are three things that I care about:</p>

<ul>
  <li>HTTP request is performed;</li>
  <li>there are no errors (or there are expected errors, in rare cases);</li>
  <li>there is some data returned and contents make sense.</li>
</ul>

<blockquote>
  <p>Do not confuse top–level errors with data–level errors—check out my huge <a href="./errors-in-graphql">post</a> on that topic</p>
</blockquote>

<p>If you do have some complex logic inside the type—you can also add some checks to this spec.</p>

<p>Now let’s inspect the <code class="language-plaintext highlighter-rouge">GraphqlHelpers</code> module I used:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">module</span> <span class="nn">GraphqlHelpers</span>
  <span class="kp">extend</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">Concern</span>

  <span class="n">included</span> <span class="k">do</span>
    <span class="n">let</span><span class="p">(</span><span class="ss">:variables</span><span class="p">)</span> <span class="p">{</span> <span class="p">{</span> <span class="ss">input: </span><span class="n">input</span> <span class="p">}</span> <span class="p">}</span>
    <span class="n">let</span><span class="p">(</span><span class="ss">:input</span><span class="p">)</span> <span class="p">{</span> <span class="p">{}</span> <span class="p">}</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">perform_request</span>
    <span class="c1"># you will probably handle auth here</span>
    <span class="n">post</span> <span class="s2">"/graphql"</span><span class="p">,</span> <span class="c1"># replace with your endpoint</span>
      <span class="ss">params: </span><span class="no">JSON</span><span class="p">.</span><span class="nf">dump</span><span class="p">(</span><span class="n">request_params</span><span class="p">),</span>
      <span class="ss">xhr: </span><span class="kp">true</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">json_response_body</span>
    <span class="no">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="nf">body</span><span class="p">)</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">gql_data</span>
    <span class="n">json_response_body</span><span class="p">[</span><span class="s2">"data"</span><span class="p">]</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">gql_errors</span>
    <span class="n">json_response_body</span><span class="p">[</span><span class="s2">"errors"</span><span class="p">]</span>
  <span class="k">end</span>

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

  <span class="k">def</span> <span class="nf">request_params</span>
    <span class="p">{</span> <span class="ss">query: </span><span class="n">query</span> <span class="p">}.</span><span class="nf">tap</span> <span class="k">do</span> <span class="o">|</span><span class="n">params</span><span class="o">|</span>
      <span class="n">params</span><span class="p">[</span><span class="ss">:variables</span><span class="p">]</span> <span class="o">=</span> <span class="n">variables</span> <span class="k">if</span> <span class="k">defined?</span><span class="p">(</span><span class="n">variables</span><span class="p">)</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>This module encapsulates the boilerplate logic of performing HTTP request and parsing the response, which should be common for all your type specs.</p>

<h1 id="mutations">Mutations</h1>

<p>Imagine that we have all types covered with specs, and now we want to test mutations. What is mutation in GraphQL? It’s the same thing as query, but we also expect some changes in the state.</p>

<p>As a result, we only care about three facts:</p>

<ul>
  <li>data was updated;</li>
  <li>mutation returns some data (but we don’t need to test the whole type!);</li>
  <li>no errors were returned.</li>
</ul>

<p>For example:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">describe</span> <span class="no">Mutations</span><span class="o">::</span><span class="no">CreatePost</span> <span class="k">do</span>
  <span class="c1"># you can configure RSpec to add this to all specs</span>
  <span class="c1"># inside spec/graphql folder</span>
  <span class="kp">include</span> <span class="no">GraphqlHelpers</span>

  <span class="n">let</span><span class="p">(</span><span class="ss">:query</span><span class="p">)</span> <span class="k">do</span>
    <span class="o">&lt;&lt;~</span><span class="no">GRAPHQL</span><span class="sh">
      mutation CreatePost($title: String!, $content: String!) {
        createPost(title: $title, content: $content) {
          id
        }
      }
</span><span class="no">    GRAPHQL</span>
  <span class="k">end</span>

  <span class="n">let</span><span class="p">(</span><span class="ss">:title</span><span class="p">)</span> <span class="p">{</span> <span class="s2">"First post"</span> <span class="p">}</span>
  <span class="n">let</span><span class="p">(</span><span class="ss">:content</span><span class="p">)</span> <span class="p">{</span> <span class="s2">"Bla bla bla"</span> <span class="p">}</span>
  <span class="n">let</span><span class="p">(</span><span class="ss">:variables</span><span class="p">)</span> <span class="p">{</span> <span class="p">{</span> <span class="n">title</span><span class="p">:,</span> <span class="ss">content: </span><span class="p">}</span> <span class="p">}</span>

  <span class="n">specify</span> <span class="k">do</span>
    <span class="n">expect</span> <span class="p">{</span> <span class="n">perform_request</span> <span class="p">}.</span><span class="nf">to</span> <span class="n">change</span><span class="p">(</span><span class="no">Post</span><span class="p">,</span> <span class="ss">:count</span><span class="p">).</span><span class="nf">by</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>

    <span class="no">Post</span><span class="p">.</span><span class="nf">last</span><span class="p">.</span><span class="nf">tap</span> <span class="k">do</span> <span class="o">|</span><span class="n">created_post</span><span class="o">|</span>
      <span class="n">expect</span><span class="p">(</span><span class="n">created_post</span><span class="p">).</span><span class="nf">to</span> <span class="n">have_attributes</span><span class="p">(</span><span class="n">title</span><span class="p">:,</span> <span class="n">content</span><span class="p">:,</span> <span class="ss">author: </span><span class="n">current_user</span><span class="p">)</span>

      <span class="n">expect</span><span class="p">(</span><span class="n">gql_errors</span><span class="p">).</span><span class="nf">to</span> <span class="n">eq</span><span class="p">(</span><span class="kp">nil</span><span class="p">)</span>

      <span class="n">resolved_object</span> <span class="o">=</span> <span class="n">gql_data</span><span class="p">.</span><span class="nf">dig</span><span class="p">(</span><span class="s2">"post"</span><span class="p">)</span>

      <span class="n">expect</span><span class="p">(</span><span class="n">resolved_object</span><span class="p">).</span><span class="nf">to</span> <span class="n">match</span><span class="p">(</span>
        <span class="s2">"id"</span> <span class="o">=&gt;</span> <span class="n">created_post</span><span class="p">.</span><span class="nf">id</span><span class="p">.</span><span class="nf">to_s</span>
      <span class="p">)</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h1 id="subscriptions">Subscriptions</h1>

<p>Subscriptions are a bit more tricky: the problem is that they can return data two times: first time when client is subscribed (via <code class="language-plaintext highlighter-rouge">def subscribe</code>) and second time when subscription is triggered. The first case is simple: you just need to perform the query and check the response, like we did for types.</p>

<p>However, how can we test the payload that comes when subscription is triggered?</p>

<p>Let’s start with the spec file:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">describe</span> <span class="no">Api</span><span class="o">::</span><span class="no">Types</span><span class="o">::</span><span class="no">DailyGoal</span><span class="o">::</span><span class="no">EarnTicketEventType</span> <span class="k">do</span>
  <span class="kp">include</span> <span class="no">GraphqlSubscriptionHelpers</span>

  <span class="n">let</span><span class="p">(</span><span class="ss">:query</span><span class="p">)</span> <span class="p">{</span> <span class="o">&lt;&lt;~</span><span class="no">GQL</span> <span class="p">}</span><span class="sh">
    subscription {
      postCreated {
        post {
          id
        }
      }
    }
</span><span class="no">  GQL</span>

  <span class="n">let</span><span class="p">(</span><span class="ss">:post</span><span class="p">)</span> <span class="p">{</span> <span class="n">create</span> <span class="ss">:post</span> <span class="p">}</span>
  <span class="n">let</span><span class="p">(</span><span class="ss">:current_user_id</span><span class="p">)</span> <span class="p">{</span> <span class="mi">42</span> <span class="p">}</span>
  <span class="n">let</span><span class="p">(</span><span class="ss">:context</span><span class="p">)</span> <span class="p">{</span> <span class="p">{</span> <span class="ss">channel: </span><span class="n">mock_channel</span><span class="p">,</span> <span class="ss">current_user_id: </span><span class="n">current_user_id</span> <span class="p">}</span> <span class="p">}</span>

  <span class="n">specify</span> <span class="k">do</span>
    <span class="n">subscribe</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">context</span><span class="p">:)</span>
    <span class="n">trigger_subscription</span><span class="p">(</span><span class="ss">:post_created</span><span class="p">,</span> <span class="ss">payload: </span><span class="p">{</span> <span class="ss">post: </span><span class="p">},</span> <span class="ss">scope: </span><span class="n">current_user_id</span><span class="p">)</span>

    <span class="n">event</span> <span class="o">=</span> <span class="n">mock_channel</span><span class="p">.</span><span class="nf">mock_broadcasted_messages</span><span class="p">.</span><span class="nf">first</span><span class="p">.</span><span class="nf">dig</span><span class="p">(</span><span class="s2">"data"</span><span class="p">,</span> <span class="s2">"post"</span><span class="p">)</span>
    <span class="n">expect</span><span class="p">(</span><span class="n">event</span><span class="p">).</span><span class="nf">to</span> <span class="n">match</span><span class="p">(</span><span class="s2">"id"</span> <span class="o">=&gt;</span> <span class="n">post</span><span class="p">.</span><span class="nf">id</span><span class="p">.</span><span class="nf">to_s</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>We expect the event that was sent to the user to be inside the message queue of the <code class="language-plaintext highlighter-rouge">mock_channel</code>. In order to get it we need to subscribe to the subscription and emit the event.</p>

<p>All the magic sits in the <code class="language-plaintext highlighter-rouge">GraphqlSubscriptionHelpers</code>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">module</span> <span class="nn">GraphqlSubscriptionHelpers</span>
  <span class="kp">extend</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">Concern</span>

  <span class="n">included</span> <span class="k">do</span>
    <span class="n">let</span><span class="p">(</span><span class="ss">:mock_channel</span><span class="p">)</span> <span class="p">{</span> <span class="no">MockSubscriptionCable</span><span class="p">.</span><span class="nf">fetch_mock_channel</span> <span class="p">}</span>

    <span class="c1"># we want queue to be empty when individual example is run</span>
    <span class="n">before</span> <span class="p">{</span> <span class="no">MockSubscriptionCable</span><span class="p">.</span><span class="nf">clear_mocks</span> <span class="p">}</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">subscribe</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">context</span><span class="p">:)</span>
    <span class="no">MockSubscriptionCable</span><span class="o">::</span><span class="no">GraphqlSchema</span><span class="p">.</span><span class="nf">execute</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">context</span><span class="p">:)</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">trigger_subscription</span><span class="p">(</span><span class="n">subscription</span><span class="p">,</span> <span class="n">payload</span><span class="p">:,</span> <span class="n">scope</span><span class="p">:)</span>
    <span class="no">MockSubscriptionCable</span><span class="o">::</span><span class="no">GraphqlSchema</span><span class="p">.</span><span class="nf">subscriptions</span><span class="p">.</span><span class="nf">trigger</span><span class="p">(</span><span class="ss">:post_created</span><span class="p">,</span> <span class="p">{},</span> <span class="p">,</span> <span class="n">scope</span><span class="p">:)</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>We have an accessor to get the mocked channel, and we have two methods—<code class="language-plaintext highlighter-rouge">subscribe</code> and <code class="language-plaintext highlighter-rouge">trigger_subscription</code> that imitate the real subscription flow.</p>

<p>Now we need to implement that <code class="language-plaintext highlighter-rouge">MockSubscriptionCable</code> module. Turns out, that <a href="https://github.com/rmosolgo/graphql-ruby">graphql-ruby gem</a> has the code we need right in the repo—they need to test subscription <a href="https://github.com/rmosolgo/graphql-ruby/blob/f06879943cd5916bafcdc44b58c7e28d24b2626f/spec/graphql/subscriptions/action_cable_subscriptions_spec.rb">as well</a>. Now we can build our own version inspired by their implementation of the mock subscription queue:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">MockSubscriptionCable</span>
  <span class="k">class</span> <span class="nc">MockChannel</span>
    <span class="nb">attr_reader</span> <span class="ss">:mock_broadcasted_messages</span>

    <span class="k">def</span> <span class="nf">initialize</span>
      <span class="vi">@mock_broadcasted_messages</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="nf">stream_from</span><span class="p">(</span><span class="n">stream_name</span><span class="p">,</span> <span class="ss">coder: </span><span class="kp">nil</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span><span class="p">)</span>
      <span class="n">block</span> <span class="o">||=</span> <span class="o">-&gt;</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span> <span class="p">{</span> <span class="vi">@mock_broadcasted_messages</span> <span class="o">&lt;&lt;</span> <span class="n">msg</span><span class="p">[</span><span class="ss">:result</span><span class="p">]</span> <span class="p">}</span>
      <span class="no">MockSubscriptionCable</span><span class="p">.</span><span class="nf">mock_stream_for</span><span class="p">(</span><span class="n">stream_name</span><span class="p">).</span><span class="nf">add_mock_channel</span><span class="p">(</span><span class="nb">self</span><span class="p">,</span> <span class="n">block</span><span class="p">)</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="k">class</span> <span class="nc">MockStream</span>
    <span class="k">def</span> <span class="nf">initialize</span>
      <span class="vi">@mock_channels</span> <span class="o">=</span> <span class="p">{}</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="nf">add_mock_channel</span><span class="p">(</span><span class="n">channel</span><span class="p">,</span> <span class="n">handler</span><span class="p">)</span>
      <span class="vi">@mock_channels</span><span class="p">[</span><span class="n">channel</span><span class="p">]</span> <span class="o">=</span> <span class="n">handler</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="nf">mock_broadcast</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
      <span class="vi">@mock_channels</span><span class="p">.</span><span class="nf">each_value</span> <span class="p">{</span> <span class="o">|</span><span class="n">handler</span><span class="o">|</span> <span class="n">handler</span><span class="o">&amp;</span><span class="p">.</span><span class="nf">call</span><span class="p">(</span><span class="n">message</span><span class="p">)</span> <span class="p">}</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="k">class</span> <span class="o">&lt;&lt;</span> <span class="nb">self</span>
    <span class="k">def</span> <span class="nf">clear_mocks</span>
      <span class="vi">@mock_streams</span> <span class="o">=</span> <span class="p">{}</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="nf">server</span>
      <span class="nb">self</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="nf">broadcast</span><span class="p">(</span><span class="n">stream_name</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
      <span class="n">stream</span> <span class="o">=</span> <span class="vi">@mock_streams</span><span class="p">[</span><span class="n">stream_name</span><span class="p">]</span>
      <span class="n">stream</span><span class="o">&amp;</span><span class="p">.</span><span class="nf">mock_broadcast</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="nf">mock_stream_for</span><span class="p">(</span><span class="n">stream_name</span><span class="p">)</span>
      <span class="vi">@mock_streams</span><span class="p">[</span><span class="n">stream_name</span><span class="p">]</span> <span class="o">||=</span> <span class="no">MockStream</span><span class="p">.</span><span class="nf">new</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="nf">fetch_mock_channel</span>
      <span class="no">MockChannel</span><span class="p">.</span><span class="nf">new</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="nf">mock_stream_names</span>
      <span class="vi">@mock_streams</span><span class="p">.</span><span class="nf">keys</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="c1"># you can inherit from your app's schema or just build a new one</span>
  <span class="k">class</span> <span class="nc">GraphqlSchema</span> <span class="o">&lt;</span> <span class="o">::</span><span class="no">GraphqlSchema</span>
    <span class="n">use</span> <span class="no">GraphQL</span><span class="o">::</span><span class="no">Subscriptions</span><span class="o">::</span><span class="no">ActionCableSubscriptions</span><span class="p">,</span>
      <span class="ss">action_cable: </span><span class="no">MockSubscriptionCable</span><span class="p">,</span>
      <span class="ss">action_cable_coder: </span><span class="no">JSON</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<hr />

<p>In this post we learned how to test various responses from your Ruby backend powered by GraphQL Ruby.</p>

<hr />

<p>Building a GraphQL API in Rails and want it done right? <a href="/consulting/">I offer GraphQL design and architecture consulting.</a></p>]]></content><author><name></name></author><category term="ruby" /><category term="rails" /><category term="graphql" /><summary type="html"><![CDATA[How to test graphql-ruby responses, coming from types, mutations and subscriptions]]></summary></entry><entry><title type="html">Service objects in Rails: how to find a mess</title><link href="https://dmitrytsepelev.dev/service-objects-anti-patterns" rel="alternate" type="text/html" title="Service objects in Rails: how to find a mess" /><published>2023-10-10T06:00:00+00:00</published><updated>2023-10-10T06:00:00+00:00</updated><id>https://dmitrytsepelev.dev/service-objects</id><content type="html" xml:base="https://dmitrytsepelev.dev/service-objects-anti-patterns"><![CDATA[<p>Service objects are hard. Back in days, the goal was to extract business logic from controllers and models, and, in some cases, turned out to a black hole inside <code class="language-plaintext highlighter-rouge">app/services</code> folder.</p>

<p>In this post we will discuss two things that are often missing: contracts and composability. After that I will share a list of  anti–patterns I found while working on dozens of Rails applications. We will examine more and less popular gems to see if they help us to avoid these bad practices. The final part is all about diagnosing your code for these problems and fixing them quickly.</p>

<p>Bear in mind that we will talk about pretty standard Rails monoliths with the relational database, key–value storage, background jobs, mailers etc. If you have something complex (e.g., message queues or search engines)—you will be probably able to extend these cases to handle these interactions as well. Also, sometimes service objects are also called interactors or (business) actions—all issues are applicable to anything in this list.</p>

<hr />

<h2 id="contracts">Contracts</h2>

<p>Ruby does not have explicit type annotations out of the box. In simple cases it’s easy to guess: what class will be used if the instance called <code class="language-plaintext highlighter-rouge">user</code>?</p>

<p>However, what if you want to <em>compose</em> your services? In that case you will have to dig up some code or tests to understand types and make sure you handled all possible cases.</p>

<blockquote>
  <p>You might be using <a href="https://github.com/ruby/rbs">rbs</a> or <a href="https://sorbet.org">Sorbet</a> to get some types. If you use them—let me know if missing contracts are still an issue in your project.</p>
</blockquote>

<p>Ideally service objects should somehow specify their input and output types explicitly. Also, it would be really helpful to have the validation of the <em>returned value</em> to make sure that there are no hidden scenarios that return something different.</p>

<p>There is a number of ways to add validation for input parameters, for instance using <a href="https://api.rubyonrails.org/classes/ActiveRecord/Attributes/ClassMethods.html">Rails Attributes API</a>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">SignUp</span>
  <span class="kp">include</span> <span class="no">ActiveModel</span><span class="o">::</span><span class="no">Model</span>
  <span class="kp">include</span> <span class="no">ActiveModel</span><span class="o">::</span><span class="no">Attributes</span>

  <span class="n">attribute</span> <span class="ss">:birthday</span><span class="p">,</span> <span class="ss">:date</span>
  <span class="n">validates</span> <span class="ss">:birthday</span><span class="p">,</span> <span class="ss">presence: </span><span class="kp">true</span>
<span class="k">end</span>
</code></pre></div></div>

<p>You can also take a look at <a href="https://github.com/dry-rb/dry-initializer">dry-initializer</a>.</p>

<p>Output validation is harder, and it’s more dangerous. Take a look at this code:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">result</span> <span class="o">=</span> <span class="no">PlaceOrder</span><span class="p">.</span><span class="nf">call</span><span class="p">(</span><span class="n">user</span><span class="p">:)</span>
</code></pre></div></div>

<p>What is <code class="language-plaintext highlighter-rouge">result</code>? Can this method raise exceptions? We can open up the source code and check, but what if it calls other classes? We will have to reach the maximum depth to get the list of possible results and exceptions. And we will do it every time for every class, because this knowledge is not written anywhere.</p>

<blockquote>
  <p>As a homework exercise, try to estimate how much time you team could waste while figuring out these missing contracts.</p>
</blockquote>

<h2 id="service-composition">Service composition</h2>

<p>In functional programming composition is <em>an act or mechanism to combine simple functions to build more complicated ones</em>. In other words, you can compose two functions into a new one that runs the first function on the input and passes the value to the second one. You get the same thing as you would get if you call two functions manually.</p>

<p>In object–oriented programming composition means the ability to use one object from another. They say that there is a <em>has–a assoctiation</em> between them.</p>

<blockquote>
  <p>Do not confuse it with <code class="language-plaintext highlighter-rouge">has_one</code> from ActiveRecord.</p>
</blockquote>

<p>You can call one service object from another, i.e., <em>compose</em> them like this:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">AddItemToCart</span>
  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">user</span><span class="p">:,</span> <span class="n">item</span><span class="p">:)</span>
    <span class="vi">@user</span> <span class="o">=</span> <span class="n">user</span>
    <span class="vi">@item</span> <span class="o">=</span> <span class="n">item</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">call</span>
    <span class="n">user</span><span class="p">.</span><span class="nf">with_lock</span> <span class="k">do</span>
      <span class="c1"># this is composition of services:</span>
      <span class="n">cart</span> <span class="o">=</span> <span class="no">FindOrCreateCart</span><span class="p">.</span><span class="nf">call</span><span class="p">(</span><span class="ss">user: </span><span class="vi">@user</span><span class="p">)</span>

      <span class="n">cart_item</span> <span class="o">=</span> <span class="n">cart</span><span class="p">.</span><span class="nf">cart_items</span>
        <span class="p">.</span><span class="nf">create_with</span><span class="p">(</span><span class="ss">quantity: </span><span class="mi">0</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">find_or_initialize_by</span><span class="p">(</span><span class="ss">item: </span><span class="vi">@item</span><span class="p">)</span>
      <span class="n">cart_item</span><span class="p">.</span><span class="nf">quantity</span> <span class="o">+=</span> <span class="mi">1</span>
      <span class="n">cart_item</span><span class="p">.</span><span class="nf">save!</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h3 id="objectrelational-impedance-mismatch">Object–relational impedance mismatch</h3>

<p><a href="https://en.wikipedia.org/wiki/Object–relational_impedance_mismatch">Object–relational impedance mismatch</a> is a set of concepts that are similar between object–oriented languages and relational databases, but they work in completely different ways. For instance, objects and tuples both can hold data, but databases do not have encapsulation.</p>

<p>The most important thing for us is the difference in error handling. In the program you can write some logic, raise exceptions and handle them, but in the database there is an additional level—<em>transactions</em>. When you start transaction, you can make some changes in the database, and commit or rollback them altogether. This is called <em>atomicity</em>, which is represented by <strong>A</strong> in <a href="https://en.wikipedia.org/wiki/ACID">ACID</a>).</p>

<p>Depending on the <a href="https://www.postgresql.org/docs/current/transaction-iso.html">isolation level</a>, database can behave differently when you make queries. For instance, default isolation level <code class="language-plaintext highlighter-rouge">READ COMMITTED</code> can return different data when you make the same query two times if something had changed in between, but <code class="language-plaintext highlighter-rouge">REPEATABLE READ</code> will make queries return the same data until the commit.</p>

<h3 id="nonatomic-actions">Non–atomic actions</h3>

<p>Database is not the only thing we use when we write logic: we can also change other data stores (e.g., Redis or Elasticsearch), perform HTTP requests, work with file system and so on. These actions are not managed by transaction—after the rollback, these changes will stay unless we handle it in some way.</p>

<h3 id="whats-wrong-with-composition">What’s wrong with composition</h3>

<p>Take a look again at the example above. Do you think it works fine? Maybe, depends on the implementation of the <code class="language-plaintext highlighter-rouge">FindOrCreateCart</code>. For instance, this is how it could look like:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">FindOrCreateCart</span>
  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">user</span><span class="p">:)</span>
    <span class="vi">@user</span> <span class="o">=</span> <span class="n">user</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">call</span>
    <span class="n">user</span><span class="p">.</span><span class="nf">with_lock</span> <span class="k">do</span>
      <span class="n">cart</span> <span class="o">=</span> <span class="n">user</span><span class="p">.</span><span class="nf">orders</span><span class="p">.</span><span class="nf">find_or_create_by</span><span class="p">(</span><span class="ss">status: :cart</span><span class="p">)</span>
    <span class="k">end</span>

    <span class="no">Redis</span><span class="p">.</span><span class="nf">instance</span><span class="p">.</span><span class="nf">incr</span><span class="p">(</span><span class="s2">"carts"</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Imagine that we use this service directly as well. We need the lock and transaction to make sure that user has only one cart, and we need to increment counter (just for the demonstration purposes).</p>

<p>This service object looks fine as well, this is how it works:</p>

<ul>
  <li>transaction opens;</li>
  <li>cart item is looked up:
    <ul>
      <li>if it’s found—transaction commits—Redis counter is incremented;</li>
      <li>if it’s not found—insert is attempted:
        <ul>
          <li>if insert succeeds then transaction commits—Redis counter is incremented;</li>
          <li>otherwise—rollback error is thrown, transaction is rolled back and exception prevents counter from the incrementation.</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<p>However, when it’s called as a part of <code class="language-plaintext highlighter-rouge">AddItemToCart</code>, two bad things can happen.</p>

<p>First one appears when <code class="language-plaintext highlighter-rouge">FindOrCreateCart</code> goes to one of scenarios when counter is incremented, but <code class="language-plaintext highlighter-rouge">FindOrCreateCart</code> raises a <code class="language-plaintext highlighter-rouge">Rollback</code> error after: in this case cart creation will be rolled back, but <em>counter increment will still happen</em>.</p>

<p>The second one happens when something causes <code class="language-plaintext highlighter-rouge">user.orders.find_or_create_by(status: :cart)</code> to raise <code class="language-plaintext highlighter-rouge">Rollback</code>: in this case the nested <code class="language-plaintext highlighter-rouge">with_lock</code> block will catch the exception, but <em>won’t rollback the transaction</em> because it’s not the place where it was open. As a result, everything will be commited!</p>

<blockquote>
  <p>Read more about nested transactions issue <a href="https://dev.to/gerardosandoval/understanding-activerecord-nested-transactions-3hee">here</a></p>
</blockquote>

<p>How could we avoid these issues? Well, for the second one we could add <code class="language-plaintext highlighter-rouge">requires_new: true</code> to make sure a nested transaction will use the <code class="language-plaintext highlighter-rouge">SAVEPOINT</code>. The first issue is not trivial: nested service has no way to know that parent transaction was rolled back; parent service has no way to know if nested action needs a specific rollback.</p>

<p>This is the perfect example of services that cannot be composed. One way to fix this issue is to not reuse the code at all: for instance, you can keep everything in controller actions, which will make transaction usage clearer, but I believe that it will lead us to the unmaintainable mess.</p>

<p>Another way is to split service objects into groups: first is used only on top level (opens up transactions, sets locks etc.) and cannot be called from other services; second contains only database actions; third contains only non–atomic actions.</p>

<p>As a summary of this section we can conclude that <em>services should either be fully composable or composition should be prohibited at all</em>. In the next section we will discuss more service object anti–patterns, some of them cause actions to be non—composable.</p>

<h2 id="service-implementation-antipatterns">Service implementation anti–patterns</h2>

<h3 id="nested-transactions">Nested transactions</h3>

<p>Nested transactions were already illustrated above: this problem happens when one service object opens up the transaction and calls another service that tries to open up transaction as well. This can lead to the situation when nested rollback is just thrown away.</p>

<h3 id="behavior-is-different-depending-on-how-service-was-called">Behavior is different depending on how service was called</h3>

<p>This one was also illustrated above. The symptom of the problem is that action behaves differently depending on whether it was called directly or from another service object.</p>

<h3 id="incompatible-transaction-levels">Incompatible transaction levels</h3>

<p>This is kinda a sub–problem of nested transactions: if some service needs a higher isolation level that a default one and it’s called from another action that did not request this isolation level, it will either behave in a wrong way or database error will happen.</p>

<p>I might be wrong, but all modern databases I know does not allow it. However, even if it was possible, imagine the following situation:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">ParentService</span>
  <span class="k">def</span> <span class="nf">call</span>
    <span class="no">ApplicationRecord</span><span class="p">.</span><span class="nf">transaction</span> <span class="k">do</span>
      <span class="no">ChildService</span><span class="p">.</span><span class="nf">call</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="k">class</span> <span class="nc">ChildService</span>
  <span class="k">def</span> <span class="nf">call</span>
    <span class="no">ApplicationRecord</span><span class="p">.</span><span class="nf">transaction</span><span class="p">(</span><span class="ss">isolation: :repeatable_read</span><span class="p">)</span> <span class="k">do</span>
      <span class="c1"># logic</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">ChildService</code> works fine when called directly, but when it’s called inside <code class="language-plaintext highlighter-rouge">READ COMMITTED</code>—it will behave in a wrong way.</p>

<h3 id="nonatomic-action-inside-transactions">Non–atomic action inside transactions</h3>

<p>Non–atomic action is literally anything that changes a state of anything except the database that runs the transaction. When something fails, transaction will be rolled back, but change made by non–atomic action will stay.</p>

<p>For example, take a look at the following service:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">RegisterUser</span>
  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">email</span><span class="p">:)</span>
    <span class="vi">@email</span> <span class="o">=</span> <span class="n">email</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">call</span>
    <span class="no">ApplicationRecord</span><span class="p">.</span><span class="nf">transaction</span> <span class="k">do</span>
      <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">create!</span><span class="p">(</span><span class="ss">email: </span><span class="vi">@email</span><span class="p">)</span>
      <span class="no">UserMailer</span><span class="p">.</span><span class="nf">welcome</span><span class="p">(</span><span class="n">user</span><span class="p">).</span><span class="nf">deliver_now</span>
      <span class="no">IssueDiscount</span><span class="p">.</span><span class="nf">perform_later</span><span class="p">(</span><span class="n">user</span><span class="p">:)</span>

      <span class="no">CreateCart</span><span class="p">.</span><span class="nf">call</span><span class="p">(</span><span class="n">user</span><span class="p">:)</span>
    <span class="k">end</span>
  <span class="k">rescue</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">RecordNotUnique</span>
    <span class="c1"># user already exists</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>This looks kinda fine: if user already exists than we won’t sent mail and issue discount. However, what if <code class="language-plaintext highlighter-rouge">CreateCart</code> fails? In this case user record will not be committed, but email will be sent and job will be enqueued.</p>

<p>After that, later job will raise an error because it won’t be able to load (or <em>deserialize</em>) user from the database. Moreover, even if transaction will commit, there is a huge chance that background job processor will pick up the job <em>before</em> the commit, leading to the same error. Of course, it will restart and load the user, so job will succeed but there will be a reported error. Pretty nasty bug to investigate later, right?</p>

<p>It worth mentioning that this issue can be obscure when transaction is opened on the upper level. In this case you won’t notice the issue unless you examine the whole call stack. One possible sign is that you have atomic and non–atomic action in the same class: unless there’s an implicit transaction—there is something going wrong here.</p>

<h3 id="io-actions-inside-transaction">IO actions inside transaction</h3>

<p>This section is dedicated to another similar pattern, but causing different sympthoms is IO actions inside transactions. Imagine a following service:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">CheckPayment</span>
  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">order</span><span class="p">:)</span>
    <span class="vi">@order</span> <span class="o">=</span> <span class="n">order</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">call</span>
    <span class="vi">@order</span><span class="p">.</span><span class="nf">with_lock</span> <span class="k">do</span> <span class="c1"># lock will force DB snapshot to be taken</span>
      <span class="n">response</span> <span class="o">=</span> <span class="no">PaymentProviderClient</span><span class="p">.</span><span class="nf">check_payment</span><span class="p">(</span><span class="ss">order_id: </span><span class="n">order</span><span class="p">.</span><span class="nf">external_id</span><span class="p">)</span>

      <span class="n">order</span><span class="p">.</span><span class="nf">update!</span><span class="p">(</span><span class="ss">payment_status: :processed</span><span class="p">)</span> <span class="k">if</span> <span class="n">response</span><span class="p">[</span><span class="ss">:status</span><span class="p">]</span> <span class="o">==</span> <span class="ss">:processed</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>From the business logic perspective it looks fine: we fetch the status from the external API and update our database based on the response. However, what if that API is down?</p>

<blockquote>
  <p>Make sure to not make ANY IO actions in the main (i.e., web server) thread cause it can consume all workers and application will be down. This is the anti–pattern itself, not related to the place where you keep the business logic. Prefer background jobs for such things instead.</p>
</blockquote>

<p>That will make our transaction longer. Moreover, locks will be held while application waits from the response, preventing other transactions from finishing.</p>

<h3 id="two-transactions-for-single-controller-action">Two transactions for single controller action</h3>

<p>I know there might be exceptions, but most of the time when you have two transactions (not nested) inside the single action it means that something is wrong. What if the second one fails? Should we revert a first one? How?</p>

<p>For instance, imagine the situation, when you need to do 3 things:</p>

<ol>
  <li>change database state (transaction 1);</li>
  <li>fetch some data from HTTP (we already know it should be outside the transaction);</li>
  <li>change database state one more time depending on the response (transaction 2).</li>
</ol>

<p>The best way to do that is to run background job after the first transaction and keep steps 2 and 3 there—it will either complete or we will get the exception to our error tracker, fix the problem and re–run the job.</p>

<h2 id="im-using-a-gem-for-service-objects">I’m using a gem for service objects!</h2>

<p>In this section we will discuss ways how to implement service objects using existing popular gems and approaches. As you might have noticed, all these anti–patterns can be more or less easily refactored, but can we prevent them by design? Does gem design help to achieve that?</p>

<p>Linters seem to be helpless here because usually they can check only a single file in isolation, there is no way to see if transaction was opened around. As mentioned earlier, we can mitigate these patterns if we do not have any composition, but that might be a bad solution in terms of maintenance.</p>

<h3 id="interactor">interactor</h3>

<p>One of most popular solutions is <a href="https://github.com/collectiveidea/interactor">interactor</a>.</p>

<blockquote>
  <p>Do not confuse it with Interactor pattern coming from the Clean architecture. By definition from the internet it means “little, reusable chunks of code that abstract logic from presenters while simplifying your app and making future changes effortless”. Not related to our topic at all.</p>
</blockquote>

<p>A very first thing that’s mentioned in the README at the moment of writing is <em>context</em>. Effectively it’s just a hash that contains all passed arguments, and you can add more if you want. When service runs successfully you get the whole context back, otherwise you get <code class="language-plaintext highlighter-rouge">Interactor::Failure</code>. You can also use <code class="language-plaintext highlighter-rouge">context.fail!</code> to halt and cause service to return a failure object.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">AuthenticateUser</span>
  <span class="kp">include</span> <span class="no">Interactor</span>

  <span class="k">def</span> <span class="nf">call</span>
    <span class="k">if</span> <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">authenticate</span><span class="p">(</span><span class="n">context</span><span class="p">.</span><span class="nf">email</span><span class="p">,</span> <span class="n">context</span><span class="p">.</span><span class="nf">password</span><span class="p">)</span>
      <span class="c1"># adding two more variables to the context</span>
      <span class="n">context</span><span class="p">.</span><span class="nf">user</span> <span class="o">=</span> <span class="n">user</span>
      <span class="n">context</span><span class="p">.</span><span class="nf">token</span> <span class="o">=</span> <span class="n">user</span><span class="p">.</span><span class="nf">secret_token</span>
    <span class="k">else</span>
      <span class="n">context</span><span class="p">.</span><span class="nf">fail!</span><span class="p">(</span><span class="ss">message: </span><span class="s2">"authenticate_user.failure"</span><span class="p">)</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="c1"># args will go to context</span>
<span class="no">AuthenticateUser</span><span class="p">.</span><span class="nf">call</span><span class="p">(</span><span class="n">email</span><span class="p">:,</span> <span class="n">password</span><span class="p">:)</span>
</code></pre></div></div>

<p>The context itself feels a bit dangerous, because it can be used as a replacement of instance variables, which sounds like a breach of encapsulation. Are we sure someone even wants to read these new variables?</p>

<p>Frankly speaking, it took me a second to understand that we need to pass <code class="language-plaintext highlighter-rouge">email</code> and <code class="language-plaintext highlighter-rouge">password</code> to make this call. However, just to make things even more hard, let’s take a look at <a href="https://github.com/collectiveidea/interactor#organizers">organizers</a>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">PlaceOrder</span>
  <span class="kp">include</span> <span class="no">Interactor</span><span class="o">::</span><span class="no">Organizer</span>

  <span class="n">organize</span> <span class="no">CreateOrder</span><span class="p">,</span> <span class="no">ChargeCard</span><span class="p">,</span> <span class="no">SendThankYou</span>
<span class="k">end</span>
</code></pre></div></div>

<p>What does it accept and return? You will have to go and read specs or code of all three services. Understanding the <em>contract</em> becomes a really hard job.</p>

<p>Let’s see what we got for transactions and non–atomic actions. There is an around hook you can use:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">PlaceOrder</span>
  <span class="kp">include</span> <span class="no">Interactor</span><span class="o">::</span><span class="no">Organizer</span>

  <span class="n">organize</span> <span class="no">CreateOrder</span><span class="p">,</span> <span class="no">ChargeCard</span><span class="p">,</span> <span class="no">SendThankYou</span>

  <span class="n">around</span> <span class="k">do</span> <span class="o">|</span><span class="n">interactor</span><span class="o">|</span>
    <span class="no">ApplicationRecord</span><span class="p">.</span><span class="nf">transaction</span> <span class="p">{</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">call</span> <span class="p">}</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>I bet <code class="language-plaintext highlighter-rouge">SendThankYou</code> contains some non–atomic logic. Can we pull it away from the transaction?</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">PlaceOrder</span>
  <span class="kp">include</span> <span class="no">Interactor</span><span class="o">::</span><span class="no">Organizer</span>

  <span class="n">organize</span> <span class="no">CreateOrder</span><span class="p">,</span> <span class="no">ChargeCard</span>

  <span class="n">around</span> <span class="k">do</span> <span class="o">|</span><span class="n">interactor</span><span class="o">|</span>
    <span class="no">ApplicationRecord</span><span class="p">.</span><span class="nf">transaction</span> <span class="p">{</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">call</span> <span class="p">}</span>

    <span class="no">SendThankYou</span><span class="p">.</span><span class="nf">call</span><span class="p">(</span><span class="n">interactor</span><span class="p">.</span><span class="nf">context</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>That kinda works, but what if we need to do something non–atomic inside the service in the middle of the chain? That would be really hard because you will have to not forget to pull it away as well. Moreover, in this case <code class="language-plaintext highlighter-rouge">CreateOrder</code> and <code class="language-plaintext highlighter-rouge">ChargeCard</code> cannot be used directly without wrapping them into the transaction.</p>

<blockquote>
  <p>It worth mentioning, that there is a <code class="language-plaintext highlighter-rouge">rollback</code> method that can help us to do some cleanup at least.</p>
</blockquote>

<p>As a result, <em>composition</em> does not play well here too: we have to prohibit a direct usage of services that not open the transaction for themselves. Also, we should always remember to not add anything non–transactional to these services and have separate services for that.</p>

<h3 id="active_interaction">active_interaction</h3>

<p>The next stop is <a href="https://github.com/AaronLasseigne/active_interaction">active_interaction</a>. Let’s rewrite our previous example:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">AuthenticateUser</span> <span class="o">&lt;</span> <span class="no">ActiveInteraction</span><span class="o">::</span><span class="no">Base</span>
  <span class="n">string</span> <span class="ss">:email</span>
  <span class="n">string</span> <span class="ss">:password</span>

  <span class="k">def</span> <span class="nf">execute</span>
    <span class="k">if</span> <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">authenticate</span><span class="p">(</span><span class="n">email</span><span class="p">,</span> <span class="n">password</span><span class="p">)</span>
      <span class="p">{</span> <span class="n">user</span><span class="p">:,</span> <span class="ss">token: </span><span class="n">user</span><span class="p">.</span><span class="nf">secret_token</span> <span class="p">}</span>
    <span class="k">else</span>
      <span class="nb">fail</span> <span class="no">AuthenticationFailed</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>This looks a bit better in terms of contract: we can see that there are two strings expected. The returned value (<code class="language-plaintext highlighter-rouge">{ user:, token: user.secret_token }</code>) will be placed to the <code class="language-plaintext highlighter-rouge">result</code> object:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">outcome</span> <span class="o">=</span> <span class="no">AuthenticateUser</span><span class="p">.</span><span class="nf">run</span><span class="p">(</span><span class="n">email</span><span class="p">:,</span> <span class="n">password</span><span class="p">:)</span>
<span class="n">outcome</span><span class="p">.</span><span class="nf">valid?</span> <span class="c1"># =&gt; true</span>
<span class="n">outcome</span><span class="p">.</span><span class="nf">result</span> <span class="c1"># =&gt; { user:, token: user.secret_token }</span>
</code></pre></div></div>

<p>We still need to read the code to understand, what is returned. Note that there is no validation to make sure that result is always the same. For instance, in some cases we might return only <code class="language-plaintext highlighter-rouge">user</code>, and the code that uses this service should be aware of it.</p>

<p>What about the composition? According to the README, we can call another service using <code class="language-plaintext highlighter-rouge">compose</code> and pass arguments explicitly:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">PlaceOrder</span> <span class="o">&lt;</span> <span class="no">ActiveInteraction</span><span class="o">::</span><span class="no">Base</span>
  <span class="n">object</span> <span class="ss">:user</span>

  <span class="k">def</span> <span class="nf">execute</span>
    <span class="no">ApplicationRecord</span><span class="p">.</span><span class="nf">transaction</span> <span class="k">do</span>
      <span class="n">order</span> <span class="o">=</span> <span class="n">compose</span><span class="p">(</span><span class="no">CreateOrder</span><span class="p">,</span> <span class="n">user</span><span class="p">:)</span>
      <span class="n">compose</span><span class="p">(</span><span class="no">ChargeCard</span><span class="p">,</span> <span class="n">user</span><span class="p">:,</span> <span class="n">order</span><span class="p">:)</span>
    <span class="k">end</span>

    <span class="n">compose</span><span class="p">(</span><span class="no">SendThankYou</span><span class="p">,</span> <span class="n">user</span><span class="p">:)</span>
    <span class="n">order</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>It’s better than implicit context and organizer we saw in <code class="language-plaintext highlighter-rouge">interactor</code>! However, all other problems are still here: we have to split services into ones that can be directly used and manage transactions by ourselves.</p>

<h3 id="mutations">mutations</h3>

<p>Let’s take a quick look at <a href="https://github.com/cypriss/mutations">mutations</a>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">AuthenticateUser</span> <span class="o">&lt;</span> <span class="no">Mutations</span><span class="o">::</span><span class="no">Command</span>
  <span class="n">required</span> <span class="k">do</span>
    <span class="n">string</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">matches: </span><span class="no">EMAIL_REGEX</span>
    <span class="n">string</span> <span class="ss">:password</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">execute</span>
    <span class="k">if</span> <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">authenticate</span><span class="p">(</span><span class="n">email</span><span class="p">,</span> <span class="n">password</span><span class="p">)</span>
      <span class="p">{</span> <span class="n">user</span><span class="p">:,</span> <span class="ss">token: </span><span class="n">user</span><span class="p">.</span><span class="nf">secret_token</span> <span class="p">}</span>
    <span class="k">else</span>
      <span class="ss">:auth_failed</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>There is an input validation. The same as <code class="language-plaintext highlighter-rouge">active_interaction</code> there is an implicit result, but the difference is that there are no other validations except data types, README suggests to do everything manually in the <code class="language-plaintext highlighter-rouge">execute</code> method.</p>

<p>Composition is done using plain old method calls:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">PlaceOrder</span> <span class="o">&lt;</span> <span class="no">Mutations</span><span class="o">::</span><span class="no">Command</span>
  <span class="n">required</span> <span class="k">do</span>
    <span class="n">object</span> <span class="ss">:user</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">execute</span>
    <span class="no">ApplicationRecord</span><span class="p">.</span><span class="nf">transaction</span> <span class="k">do</span>
      <span class="n">order</span> <span class="o">=</span> <span class="no">CreateOrder</span><span class="p">.</span><span class="nf">execute</span><span class="p">(</span><span class="n">user</span><span class="p">:)</span>
      <span class="no">ChargeCard</span><span class="p">.</span><span class="nf">execute</span><span class="p">(</span><span class="n">user</span><span class="p">:,</span> <span class="n">order</span><span class="p">:)</span>
    <span class="k">end</span>

    <span class="no">SendThankYou</span><span class="p">.</span><span class="nf">execute</span><span class="p">(</span><span class="n">user</span><span class="p">:)</span>

    <span class="n">order</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>This approach has same issues as previous solutions we inspected.</p>

<h3 id="lightservice">LightService</h3>

<p>Another popular gem I found is <a href="https://github.com/adomokos/light-service">LightService</a>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Transactional</span>
  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">call</span><span class="p">(</span><span class="n">context</span><span class="p">)</span>
    <span class="no">ApplicationRecord</span><span class="p">.</span><span class="nf">transaction</span> <span class="p">{</span> <span class="k">yield</span> <span class="p">}</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="k">class</span> <span class="nc">CalculatesTax</span>
  <span class="kp">extend</span> <span class="no">LightService</span><span class="o">::</span><span class="no">Organizer</span>

  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">call</span><span class="p">(</span><span class="n">order</span><span class="p">)</span>
    <span class="n">with</span><span class="p">(</span><span class="n">order</span><span class="p">:).</span><span class="nf">with</span><span class="p">(</span><span class="no">Transactional</span><span class="p">).</span><span class="nf">reduce</span><span class="p">(</span>
        <span class="no">LooksUpTaxPercentageAction</span><span class="p">,</span>
        <span class="no">CalculatesOrderTaxAction</span><span class="p">,</span>
        <span class="no">ProvidesFreeShippingAction</span>
      <span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="k">class</span> <span class="nc">LooksUpTaxPercentageAction</span>
  <span class="kp">extend</span> <span class="no">LightService</span><span class="o">::</span><span class="no">Action</span>
  <span class="n">expects</span> <span class="ss">:order</span>
  <span class="n">promises</span> <span class="ss">:tax_percentage</span>

  <span class="n">executed</span> <span class="k">do</span> <span class="o">|</span><span class="n">context</span><span class="o">|</span>
    <span class="n">tax_ranges</span> <span class="o">=</span> <span class="no">TaxRange</span><span class="p">.</span><span class="nf">for_region</span><span class="p">(</span><span class="n">context</span><span class="p">.</span><span class="nf">order</span><span class="p">.</span><span class="nf">region</span><span class="p">)</span>
    <span class="n">context</span><span class="p">.</span><span class="nf">tax_percentage</span> <span class="o">=</span> <span class="mi">0</span>

    <span class="k">if</span> <span class="n">object_is_nil?</span><span class="p">(</span>
      <span class="n">tax_ranges</span><span class="p">,</span>
      <span class="n">context</span><span class="p">,</span>
      <span class="s1">'The tax ranges were not found'</span>
    <span class="p">)</span>
      <span class="k">next</span> <span class="n">context</span>
    <span class="k">end</span>

    <span class="n">context</span><span class="p">.</span><span class="nf">tax_percentage</span> <span class="o">=</span> <span class="n">tax_ranges</span><span class="p">.</span><span class="nf">for_total</span><span class="p">(</span><span class="n">context</span><span class="p">.</span><span class="nf">order</span><span class="p">.</span><span class="nf">total</span><span class="p">)</span>

    <span class="k">if</span> <span class="n">object_is_nil?</span><span class="p">(</span>
      <span class="n">context</span><span class="p">.</span><span class="nf">tax_percentage</span><span class="p">,</span>
      <span class="n">context</span><span class="p">,</span>
      <span class="s1">'The tax percentage was not found'</span>
    <span class="p">)</span>
      <span class="k">next</span> <span class="n">context</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">object_is_nil?</span><span class="p">(</span><span class="n">object</span><span class="p">,</span> <span class="n">context</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">object</span><span class="p">.</span><span class="nf">nil?</span>
      <span class="n">context</span><span class="p">.</span><span class="nf">fail!</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
      <span class="k">return</span> <span class="kp">true</span>
    <span class="k">end</span>

    <span class="kp">false</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>What we can notice here:</p>

<ul>
  <li>interactor–like organizers;</li>
  <li>interactor–like around hooks for transactions (or run them manually);</li>
  <li><code class="language-plaintext highlighter-rouge">promises</code> to specify the output.</li>
</ul>

<p>Contract is defined better for separate services, but it’s still a challenge to read it for the organizer. Composition has all the issues we saw before.</p>

<h3 id="granite">Granite</h3>

<p>You might be curious how <a href="https://github.com/toptal/granite">gem</a> with &lt;200 stars appeared here. Take a look:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">PlaceOrder</span> <span class="o">&lt;</span> <span class="no">Granite</span><span class="o">::</span><span class="no">Action</span>
  <span class="n">subject</span> <span class="ss">:user</span>

  <span class="kp">private</span> <span class="k">def</span> <span class="nf">execute_perform!</span><span class="p">(</span><span class="o">*</span><span class="p">)</span>
    <span class="c1"># no need to open transaction — gem will do that for us when needed and use same</span>
    <span class="c1"># transaction for other services</span>
    <span class="n">order</span> <span class="o">=</span> <span class="n">create_order_service</span><span class="p">.</span><span class="nf">perform!</span>
    <span class="n">charge_card_service</span><span class="p">.</span><span class="nf">perform!</span> <span class="c1"># not sure how to pass order here, see below</span>
  <span class="k">end</span>

  <span class="n">after_commit</span> <span class="k">do</span>
    <span class="no">UserMailer</span><span class="p">.</span><span class="nf">thank_you</span><span class="p">(</span><span class="ss">user: </span><span class="n">subject</span><span class="p">).</span><span class="nf">perform_later</span>
  <span class="k">end</span>

  <span class="n">memoize</span> <span class="k">def</span> <span class="nf">create_order_service</span>
    <span class="no">CreateOrder</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">subject</span><span class="p">)</span>
  <span class="k">end</span>

  <span class="n">memoize</span> <span class="k">def</span> <span class="nf">charge_card_service</span>
    <span class="no">ChargeCard</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">subject</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>The thing that’s done in a different way is that Granite supports service composition out of the box. As you see, we instantiate other services and call them, the gem handles transactions.</p>

<p>Here comes a problem: gem <em>requires</em> all services to be defined as memoized instances, making it impossible to pass anything that was created by another service. I guess it’s possible to implement anything one might need using some code jiggling.</p>

<p>There are many more things in the gem: various hooks, validations, data representers, policies, context, associations, exceptions, I18n and many more. What’s missing is the documentation—it’s fairly short so you will have to dig into gem source to learn it better. I like this gem way more than others because it has a lot of cool ideas, but writing services this way feels hard: you need to keep in mind too many rules.</p>

<h3 id="dry-">dry-*</h3>

<p>Often times people mention <a href="https://dry-rb.org">dry stack</a> when they are asked about the way they implement service objects. This is a set of gems that can do different things, so the way you cook them really matters. There are a couple of thins to remember if you decide to go that way.</p>

<p><a href="https://dry-rb.org/gems/dry-validation/1.10/">dry-validation</a> can be used for the contract of input data, output is not covered by anything if I’m not mistaken.</p>

<p><a href="https://dry-rb.org/gems/dry-transaction/0.15/">dry-transaction</a> is about <em>business</em> transactions and does not help with database transactions at all, so you have to manage that manually as discussed in previous solutions.</p>

<p><a href="https://dry-rb.org/gems/dry-monads/1.6/">dry-monads</a> are often used as result objects, but there are some common pitfalls. One of the most popular features is do notation that implements a popular <a href="https://blog.logrocket.com/what-is-railway-oriented-programming/">railway pattern</a>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">CreateAccount</span>
  <span class="kp">include</span> <span class="no">Dry</span><span class="o">::</span><span class="no">Monads</span><span class="p">[</span><span class="ss">:result</span><span class="p">]</span>
  <span class="kp">include</span> <span class="no">Dry</span><span class="o">::</span><span class="no">Monads</span><span class="o">::</span><span class="no">Do</span><span class="p">.</span><span class="nf">for</span><span class="p">(</span><span class="ss">:call</span><span class="p">)</span>

  <span class="k">def</span> <span class="nf">call</span><span class="p">(</span><span class="n">params</span><span class="p">)</span>
    <span class="n">values</span> <span class="o">=</span> <span class="k">yield</span> <span class="n">validate</span><span class="p">(</span><span class="n">params</span><span class="p">)</span>
    <span class="n">account</span> <span class="o">=</span> <span class="k">yield</span> <span class="n">create_account</span><span class="p">(</span><span class="n">values</span><span class="p">[</span><span class="ss">:account</span><span class="p">])</span>
    <span class="n">owner</span> <span class="o">=</span> <span class="k">yield</span> <span class="n">create_owner</span><span class="p">(</span><span class="n">account</span><span class="p">,</span> <span class="n">values</span><span class="p">[</span><span class="ss">:owner</span><span class="p">])</span>

    <span class="no">Success</span><span class="p">([</span><span class="n">account</span><span class="p">,</span> <span class="n">owner</span><span class="p">])</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">validate</span><span class="p">(</span><span class="n">params</span><span class="p">)</span>
    <span class="c1"># returns Success(values) or Failure(:invalid_data)</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">create_account</span><span class="p">(</span><span class="n">account_values</span><span class="p">)</span>
    <span class="c1"># returns Success(account) or Failure(:account_not_created)</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">create_owner</span><span class="p">(</span><span class="n">account</span><span class="p">,</span> <span class="n">owner_values</span><span class="p">)</span>
    <span class="c1"># returns Success(owner) or Failure(:owner_not_created)</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>This looks good, but be aware that when <code class="language-plaintext highlighter-rouge">yield Failure</code> happens the database transaction will be <a href="https://dry-rb.org/gems/dry-monads/1.6/do-notation/#transaction-safety">rolled back</a>. This brings us to the problem of “all or nothing”. Imagine that you want to create an order for user from his cart, but also cleanup the cart from items that are out of stock. As a result we have 3 possible scenarios:</p>

<ul>
  <li>Success when order is created;</li>
  <li>Failure when cart is empty;</li>
  <li>something when cart <em>became</em> empty.</li>
</ul>

<p>I’d really like to use <code class="language-plaintext highlighter-rouge">Failure(:empty_cart)</code> but I cannot, because it will roll back my transaction! As a result we have to do a following thing:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">call</span>
  <span class="no">ApplicationRecord</span><span class="p">.</span><span class="nf">transaction</span> <span class="k">do</span>
    <span class="k">yield</span> <span class="n">clear_cart</span>
    <span class="k">yield</span> <span class="n">create_order</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="k">def</span> <span class="nf">clear_cart</span>
  <span class="n">cart</span><span class="p">.</span><span class="nf">cart_items</span><span class="p">.</span><span class="nf">filter</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:out_of_stock?</span><span class="p">).</span><span class="nf">delete_all</span>
<span class="k">end</span>

<span class="k">def</span> <span class="nf">create_order</span>
  <span class="k">if</span> <span class="n">cart</span><span class="p">.</span><span class="nf">cart_items</span><span class="p">.</span><span class="nf">any?</span>
    <span class="n">order</span> <span class="o">=</span> <span class="n">user</span><span class="p">.</span><span class="nf">orders</span><span class="p">.</span><span class="nf">create!</span><span class="p">(</span><span class="n">cart</span><span class="p">)</span>
    <span class="no">Success</span><span class="p">(</span><span class="ss">:order_created</span><span class="p">,</span> <span class="n">order</span><span class="p">:)</span>
  <span class="k">else</span>
    <span class="no">Success</span><span class="p">(</span><span class="ss">:empty_cart</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Alternatively, we can stop using do notation or <code class="language-plaintext highlighter-rouge">yield</code>, but that’s what we came for.</p>

<p>One last thing to mention that <code class="language-plaintext highlighter-rouge">Failure</code> rolls back transaction only when used with <code class="language-plaintext highlighter-rouge">yield</code>, this <a href="https://mikey.bike/j/2023/04/dry-rb-monad-laws.html">won’t work</a>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">call</span>
  <span class="no">ApplicationRecord</span><span class="p">.</span><span class="nf">transaction</span> <span class="k">do</span>
    <span class="k">yield</span> <span class="n">clear_cart</span>
    <span class="n">create_order</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h2 id="anyway-are-my-services-fine-or-not">Anyway, are my services fine or not?</h2>

<p>After reading this bunch of text you might be asking yourself if your services are fine or they need some changes. I prepared a checklist for you.</p>

<p>First of all, if you are not reusing the code at all or have separate classes for actions that happen before and after transactions and never mix them—probably you can stop thinking about composition issues. Otherwise—your services might be affected by some of the related anti–patterns.</p>

<p>The fastest way to check this is to install <a href="https://github.com/palkan/isolator">isolator</a>. It will report cases when someone performs non–atomic action, schedules job or makes HTTP request from inside the transaction. It might find some issues caused by composition. As soon as you get some offences, you can quickly fix some of them by <a href="https://github.com/Envek/after_commit_everywhere">after_commit_everywhere</a>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">SomeService</span>
  <span class="kp">include</span> <span class="no">AfterCommitEverywhere</span>

  <span class="k">def</span> <span class="nf">call</span>
    <span class="no">ApplicationRecord</span><span class="p">.</span><span class="nf">transaction</span> <span class="k">do</span>
      <span class="c1"># db operations</span>

      <span class="n">after_commit</span> <span class="k">do</span>
        <span class="c1"># jobs, mails and etc go here</span>
      <span class="k">end</span>

      <span class="c1"># you can continue db operations if you want</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>This is not ideal and you might still need to redesign your service object, but at least it will help to fix some nasty bugs right away by <em>separating flows</em>.</p>

<p>HTTP queries can be moved away from transaction by putting it before the transaction block:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">CheckPayment</span>
  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">order</span><span class="p">:)</span>
    <span class="vi">@order</span> <span class="o">=</span> <span class="n">order</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">call</span>
    <span class="n">response</span> <span class="o">=</span> <span class="no">PaymentProviderClient</span><span class="p">.</span><span class="nf">check_payment</span><span class="p">(</span><span class="ss">order_id: </span><span class="n">order</span><span class="p">.</span><span class="nf">external_id</span><span class="p">)</span>

    <span class="no">ApplicationRecord</span><span class="p">.</span><span class="nf">transaction</span> <span class="k">do</span>
      <span class="n">order</span><span class="p">.</span><span class="nf">update!</span><span class="p">(</span><span class="ss">payment_status: :processed</span><span class="p">)</span> <span class="k">if</span> <span class="n">response</span><span class="p">[</span><span class="ss">:status</span><span class="p">]</span> <span class="o">==</span> <span class="ss">:processed</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>However, it’s not always easy, because this service might be called from another one which opened the transaction already. As you might have guessed, the hardest part is nested transactions. I did not found an existing solution, so I prepared a quick and dirty script for you:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">ApplicationRecord</span><span class="p">.</span><span class="nf">singleton_class</span><span class="p">.</span><span class="nf">prepend</span><span class="p">(</span><span class="no">Module</span><span class="p">.</span><span class="nf">new</span> <span class="k">do</span>
  <span class="k">def</span> <span class="nf">transaction</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
    <span class="no">Thread</span><span class="p">.</span><span class="nf">current</span><span class="p">[</span><span class="ss">:transaction_stack</span><span class="p">]</span> <span class="o">||=</span> <span class="p">[]</span>

    <span class="k">unless</span> <span class="n">kwargs</span><span class="p">[</span><span class="ss">:requires_new</span><span class="p">]</span>
      <span class="n">location</span> <span class="o">=</span> <span class="n">caller_locations</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">).</span><span class="nf">first</span>

      <span class="k">if</span> <span class="no">Thread</span><span class="p">.</span><span class="nf">current</span><span class="p">[</span><span class="ss">:transaction_stack</span><span class="p">].</span><span class="nf">any?</span>
        <span class="nb">puts</span> <span class="o">&lt;&lt;~</span><span class="no">TEXT</span><span class="sh">
          Found nested transaction in </span><span class="si">#{</span><span class="n">location</span><span class="si">}</span><span class="sh">.

          Opened up in:
          </span><span class="si">#{</span><span class="no">Thread</span><span class="p">.</span><span class="nf">current</span><span class="p">[</span><span class="ss">:transaction_stack</span><span class="p">].</span><span class="nf">join</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span><span class="si">}</span><span class="sh">
</span><span class="no">        TEXT</span>
      <span class="k">end</span>
    <span class="k">end</span>

    <span class="k">if</span> <span class="no">Thread</span><span class="p">.</span><span class="nf">current</span><span class="p">[</span><span class="ss">:transaction_stack</span><span class="p">].</span><span class="nf">empty?</span> <span class="o">||</span> <span class="o">!</span><span class="n">kwargs</span><span class="p">[</span><span class="ss">:requires_new</span><span class="p">]</span>
      <span class="no">Thread</span><span class="p">.</span><span class="nf">current</span><span class="p">[</span><span class="ss">:transaction_stack</span><span class="p">]</span> <span class="o">&lt;&lt;</span> <span class="n">location</span>
    <span class="k">end</span>

    <span class="k">super</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">).</span><span class="nf">tap</span> <span class="k">do</span>
      <span class="no">Thread</span><span class="p">.</span><span class="nf">current</span><span class="p">[</span><span class="ss">:transaction_stack</span><span class="p">].</span><span class="nf">pop</span>
    <span class="k">end</span>
  <span class="k">rescue</span> <span class="n">e</span>
    <span class="no">Thread</span><span class="p">.</span><span class="nf">current</span><span class="p">[</span><span class="ss">:transaction_stack</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">raise</span> <span class="n">e</span>
  <span class="k">end</span>
<span class="k">end</span><span class="p">)</span>
</code></pre></div></div>

<p>Put it to the initializer and run your specs—maybe there will be something.</p>

<p>As mentioned, input validation can be handled by <a href="https://api.rubyonrails.org/classes/ActiveRecord/Attributes/ClassMethods.html">Rails Attributes API</a>, <a href="https://github.com/dry-rb/dry-initializer">dry-initializer</a> or something similar.</p>

<hr />

<p>You might be wondering how to to completely avoid nested transactions, write fully reusable and composable service objects without <em>any way</em> to make it wrong. I’m curious too, stay tuned and I’ll share something in the next post!</p>

<blockquote>
  <p>If you want to take a look at the thing I’m experimenting with—here <a href="https://github.com/DmitryTsepelev/clean_actions">it is</a>.</p>
</blockquote>

<p>At the meantime, take a look at your services, see if you can spot some anti–patterns using your eyes and tools. Check if that causes some bugs or performance issues in your system. Think if you are using composition, try to remember bugs caused by unhandled scenarios after the service call.</p>

<p>Maybe you will be able to solve it by redesigning the class layout. Probably making some new team agreements will help. It might turn out that your services are fine and you don’t face any issues at all.</p>

<hr />

<p>If your service layer looks like any of the patterns above, a focused audit can help you untangle it. <a href="/consulting/">Let’s talk.</a></p>]]></content><author><name></name></author><category term="ruby" /><category term="rails" /><summary type="html"><![CDATA[Examine your service objects and try to find possible issues caused by wrong composition and missing contracts]]></summary></entry><entry><title type="html">Ruby goes to the movie theater: directing the refactoring of your application</title><link href="https://dmitrytsepelev.dev/directing-refactoring" rel="alternate" type="text/html" title="Ruby goes to the movie theater: directing the refactoring of your application" /><published>2023-09-12T06:00:00+00:00</published><updated>2023-09-12T06:00:00+00:00</updated><id>https://dmitrytsepelev.dev/directing-refactoring</id><content type="html" xml:base="https://dmitrytsepelev.dev/directing-refactoring"><![CDATA[<p>This post introduces a method I use to refactor big applications. I want the process to happen in a predictable manner and make sure that important things are addressed before others. One day I realized that there is a missing tool in my workflow, so I’ll introduce my new gem called <a href="https://github.com/DmitryTsepelev/rubocop_director">rubocop_director</a>.</p>

<h2 id="refactoring-as-a-process">Refactoring as a process</h2>

<p>Refactoring is a technique of transforming the code without making changes in its functionality. Why people do that?</p>

<p>Sometimes the way that code written makes it harder for to engineers to improve the performance or add a new feature. Another example is a scenario when application is big and there are many ways to do the same thing or same <em>responsibility</em> is implemented in different <em>layers</em> of application.</p>

<p>This is how refactoring looks like in books:</p>

<ol>
  <li>the problem is spotted and a better solution is found;</li>
  <li>add some tests if coverage is not enough;</li>
  <li>code is rewritten.</li>
</ol>

<blockquote>
  <p>Please, please, do not start refactoring before covering the code with specs!</p>
</blockquote>

<p>Eventually it will be done, but a number of issues that might cause issues:</p>

<ol>
  <li>there are <em>too many</em> places to fix, so refactoring will take a lot of time to complete;</li>
  <li>most of the time there are more than one problem that’s being refactored at the given moment;</li>
  <li>people might accidentally write code in the “old style”, so amount of work will keep growing.</li>
</ol>

<p>Clearly, we need a tool to somehow track all occurrences of the given issue with the code, build a list of them to track what should be done, and another tool to prevent writing the code in the old style.</p>

<h2 id="unexpected-tool-to-solve-our-problems">Unexpected tool to solve our problems</h2>

<p>Fortunately, Ruby ecosystem already has such tool. <a href="https://github.com/rubocop/rubocop">Rubocop</a> is a linter that has a lot of plugins to enforce various best practices for your codebase. When particular rule is <em>offended</em>—Rubocop can highlight it in the code editor. Most of Ruby applications have it executed on the CI as well.</p>

<p>If you cannot fix the problem right now, but want to do it later—you can add the exception to the <code class="language-plaintext highlighter-rouge">.rubocop_todo.yml</code>:</p>

<div class="language-yml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">Style/BracesAroundHashParameters</span><span class="pi">:</span>
  <span class="na">Exclude</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s1">'</span><span class="s">app/models/user.rb'</span>
</code></pre></div></div>

<p>But how can it help us to refactor <em>our</em> application to <em>our</em> best practices? Easy, you just need to write your own cops! That might sound complex, but you can get used to it right after you write one or two of them.</p>

<h3 id="writing-custom-cops-for-rubocop">Writing custom cops for Rubocop</h3>

<p>For instance, imagine that you want to restrict opening DB transactions anywhere except <code class="language-plaintext highlighter-rouge">app/business_actions/</code>. Here’s a cop:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">module</span> <span class="nn">Isolation</span>
  <span class="k">class</span> <span class="nc">IllegalTransactionBlock</span> <span class="o">&lt;</span> <span class="no">Cop</span>
    <span class="no">MSG</span> <span class="o">=</span> <span class="s2">"Transaction shoud only be opened from business actions"</span><span class="p">.</span><span class="nf">freeze</span>

    <span class="n">def_node_matcher</span> <span class="ss">:transaction?</span><span class="p">,</span> <span class="s2">"(send ... :transaction ...)"</span>

    <span class="k">def</span> <span class="nf">on_send</span><span class="p">(</span><span class="n">node</span><span class="p">)</span>
      <span class="n">add_offense</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="ss">message: </span><span class="no">MSG</span><span class="p">)</span> <span class="k">if</span> <span class="n">transaction?</span><span class="p">(</span><span class="n">node</span><span class="p">)</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Now you can configure Rubocop using <code class="language-plaintext highlighter-rouge">.rubocop.yml</code> to run it for a specific folder:</p>

<div class="language-yml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">Isolation/IllegalTransactionBlock</span><span class="pi">:</span>
  <span class="na">Exclude</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">app/business_actions/**/*.rb</span>
</code></pre></div></div>

<p>Looks like we reached our goals:</p>

<ol>
  <li>now we can regenerate a TODO list for Rubocop to find all problems to fix;</li>
  <li>with new TODO we can see where we are in terms of overall refactoring process;</li>
  <li>no one can add new transaction in the illegal place without breaking the CI.</li>
</ol>

<blockquote>
  <p>Read more about writing cops in my <a href="https://evilmartians.com/chronicles/custom-cops-for-rubocop-an-emergency-service-for-your-codebase">post</a>, old but gold!</p>
</blockquote>

<h2 id="dealing-with-larger-codebases">Dealing with larger codebases</h2>

<p>If you ever worked on a huge application, you can imagine what your <code class="language-plaintext highlighter-rouge">.rubocop_todo.yml</code> will look like, especially after installing new plugin, upgrading old ones and writing a number of custom cops. You’re looking at this N–thousand–lines file and asking yourself where to start. You clearly understand that you cannot just fix everything right away, it will take <em>weeks</em> of human–hours. Where to start?</p>

<p>I’d suggest to take a look at two things.</p>

<p>Firstly, some offences are a bit better than others. For instance, if you have nested transactions (why is it bad? <a href="https://pragtob.wordpress.com/2017/12/12/surprises-with-nested-transactions-rollbacks-and-activerecord/">read this</a>)—you might want to fix that before, say, replacing <code class="language-plaintext highlighter-rouge">=&gt;</code> with <code class="language-plaintext highlighter-rouge">:</code> in hash literals. Also, some issues might block others, so they should go first. Looks like we need to add some <em>weights</em> to our cops.</p>

<p>Secondly, files are updated with a different frequency. I’d prefer “hot” files to satisfy all team best practices, cause people will learn them faster just by looking at this code. Also, files that were not updated for years likely gonna have no issues or can eventually be sunset, so we can postpone the refactoring. Sounds like we need to consider a frequency of updates as well. Where do we have it? In the git history!</p>

<p>Now we need to combine these two things—meet <a href="https://github.com/DmitryTsepelev/rubocop_director">rubocop_director</a>!</p>

<h3 id="setting-up-rubocop-director-">Setting up rubocop-director 🎬</h3>

<p>To get the ball rolling, install the gem and generate the default config:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>bundle add rubocop_director
<span class="nv">$ </span>bundle <span class="nb">exec </span>rubocop-director <span class="nt">--generate-config</span>
</code></pre></div></div>

<p>You will get a file with weights of all cops that have offences, default cop weight and file frequency update weight:</p>

<div class="language-yml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">update_weight</span><span class="pi">:</span> <span class="m">1</span>
<span class="na">default_cop_weight</span><span class="pi">:</span> <span class="m">1</span>
<span class="na">weights</span><span class="pi">:</span>
  <span class="na">Isolation/IllegalTransactionBlock</span><span class="pi">:</span> <span class="m">1</span>
  <span class="na">Layout/HashAlignment</span><span class="pi">:</span> <span class="m">1</span>
</code></pre></div></div>

<p>Now you can grab some pop corn and let <code class="language-plaintext highlighter-rouge">rubocop_director</code> to make a refactoring plan for you:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bundle <span class="nb">exec </span>rubocop-director
</code></pre></div></div>

<p>When it’s done (it might take a while)—you’ll see the report:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>💡 Checking git <span class="nb">history </span>since 1995-01-01 to find hot files...
💡🎥 Running rubocop to get the list of offences to fix...
💡🎥🎬 Calculating a list of files to refactor...

Path: app/controllers/user_controller.rb
Updated 99 <span class="nb">times </span>since 1995-01-01
Offenses:
  🚓 Isolation/IllegalTransactionBlock - 2
Refactoring value: 1.5431217598108933 <span class="o">(</span>54.79575%<span class="o">)</span>

Path: app/models/user.rb
Updated 136 <span class="nb">times </span>since 1995-01-01
Offenses:
  🚓 Isolation/IllegalTransactionBlock - 1
  🚓 Layout/HashAlignment - 1
Refactoring value: 1.2730122208719792 <span class="o">(</span>45.20425%<span class="o">)</span>
</code></pre></div></div>

<p>Files are sorted by the <em>refactoring value</em>, which is calculated using a formula: <code class="language-plaintext highlighter-rouge">sum of value from each cop (&lt;count of offences&gt; * &lt;cop weight&gt; * (&lt;count of file updates&gt; / &lt;total count of updates&gt;) ** &lt;update weight&gt;)</code>. As you see, file update frequency is here too!</p>

<p>What can you tune here?</p>

<ol>
  <li>change cop weights to increase the value of ones you want to go first;</li>
  <li>change the update frequency weight (you might want to increase or reduce it);</li>
  <li>change the date, since when updates are counted (e.g., <code class="language-plaintext highlighter-rouge">--since 2023-01-01</code>).</li>
</ol>

<p>When you’re done—rebuild the report and you’ll get a new prioritized list. Now you can pick up some files from the top and start working on them!</p>

<hr />

<p>In this post we discussed a method of an observable refactoring process of Ruby applications. Here are key takeaways:</p>

<ol>
  <li>start with detecting issues in the code and finding solutions for them;</li>
  <li>if you can describe an issue in terms of Rubocop—do that and get all the benefits (CI, linters etc.);</li>
  <li>otherwise—describe it in docs and track the progress manually;</li>
  <li>if you cannot fix issues all at once—build a list of files to fix, prioritize them and work on them from the top;</li>
  <li>if you were able to do everything using Rubocop—you can grab <a href="https://github.com/DmitryTsepelev/rubocop_director">rubocop_director</a> for this job.</li>
</ol>

<hr />

<p>Large-scale refactoring is easier with a plan. If you’re facing a similar situation, <a href="/consulting/">I can help you map it out.</a></p>]]></content><author><name></name></author><category term="ruby" /><category term="rails" /><category term="engineering" /><summary type="html"><![CDATA[How to plan and execute the refactoring of you Ruby application]]></summary></entry><entry><title type="html">Errors in GraphQL</title><link href="https://dmitrytsepelev.dev/errors-in-graphql" rel="alternate" type="text/html" title="Errors in GraphQL" /><published>2023-08-01T09:00:00+00:00</published><updated>2023-08-01T09:00:00+00:00</updated><id>https://dmitrytsepelev.dev/errors-in-graphql</id><content type="html" xml:base="https://dmitrytsepelev.dev/errors-in-graphql"><![CDATA[<p>One of the main GraphQL benefits is a type system. It allows both server and client applications have some <em>guarantees</em> of what they are expecting to receive. However, according to the standard, errors are not typed: it’s just an array of valid JSON objects. This is why we should design errors more thoughtfully. In this post we will discuss some good practices and patterns I saw while working on a number of GraphQL applications.</p>

<h2 id="errors-as-a-part-of-the-graphql-response">Errors as a part of the GraphQL response</h2>

<p>According to the GraphQL specification, errors are the part of the response (along with the <code class="language-plaintext highlighter-rouge">data</code> and <code class="language-plaintext highlighter-rouge">extensions</code>), for instance:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"viewer"</span><span class="p">:</span><span class="w"> </span><span class="err">nil</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"errors"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"access denied"</span><span class="p">],</span><span class="w">
  </span><span class="nl">"extensions"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"complexity"</span><span class="p">:</span><span class="w"> </span><span class="mi">12</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>The specification does not define a type for errors, like it does for the <code class="language-plaintext highlighter-rouge">data</code>. The array element inside the <code class="language-plaintext highlighter-rouge">errors</code> key should be a valid JSON structure (e.g., strings or objects).</p>

<h2 id="expected-versus-unexpected-errors">Expected versus unexpected errors</h2>

<p>Instead of putting everything under the <code class="language-plaintext highlighter-rouge">errors</code> key, let’s try to divide errors into groups:</p>

<ol>
  <li>errors that are completely unexpected and cannot be fixed by user in any way (e.g., database is down);</li>
  <li>errors that affect a part of the user workflow (e.g., user is not authorized to access a certain part of the graph, and the error will look the same for <em>any</em> field);</li>
  <li>errors in the submitted data (i.e., validation errors).</li>
</ol>

<p>We can spot a couple of interesting patterns here.</p>

<p>First and second type of errors can happen both in queries and mutations. They are not depending on the submitted data at all. Morevover, user cannot fix this error by submitting different arguments with the same query string.</p>

<p>Third group can only happen in mutations, because this kind of errors can represent either invalid state of the data in the database (e.g., processed order cannot be cancelled) or invalid mutation arguments (e.g., percent cannot be greater than 100).</p>

<h2 id="taming-toplevel-errors">Taming top–level errors</h2>

<p>GraphQL errors are not typed, so we have to add some types on our own. Let’s make errors objects (not just strings) with code and details fields:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"processOrder"</span><span class="p">:</span><span class="w"> </span><span class="err">nil</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"errors"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"code"</span><span class="p">:</span><span class="w"> </span><span class="s2">"permission_denied"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"details"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"you cannot perform this action"</span><span class="w"> </span><span class="p">}</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"code"</span><span class="p">:</span><span class="w"> </span><span class="s2">"server_error"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"details"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"DB is down"</span><span class="w"> </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Codes should go to the special list and existing codes should never change. It will help client applications to handle such errors in a common manner. Details might contain anything you want, but it might be helpful to have the message field everywhere.</p>

<h3 id="permission-errors">Permission errors</h3>

<p>If application has a permission/role system and it is possible for user to try to do some action without success guarantee — we might want to have this as a specific error. Permission errors can happen both in mutations and queries, so it makes sense to return them inside the top–level <code class="language-plaintext highlighter-rouge">errors</code> field.</p>

<p>For instance, imagine that there is a mutation for order processing:</p>

<pre><code class="language-gql">mutation ProcessOrder($id: ID!) {
  processOrder(id: $id) {
    status
    user {
      email
    }
  }
}
</code></pre>

<p>Where permission error can happen? It can be on the mutation level:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"processOrder"</span><span class="p">:</span><span class="w"> </span><span class="err">nil</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"errors"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"code"</span><span class="p">:</span><span class="w"> </span><span class="s2">"permission_error"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"details"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"field"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"processOrder"</span><span class="p">],</span><span class="w">
        </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"you cannot process this order"</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>…or processing might work fine, but user cannot be accessed:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"processOrder"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"processed"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"user"</span><span class="p">:</span><span class="w"> </span><span class="err">nil</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"errors"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"code"</span><span class="p">:</span><span class="w"> </span><span class="s2">"permission_error"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"details"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"field"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"processOrder"</span><span class="p">,</span><span class="w"> </span><span class="s2">"user"</span><span class="p">],</span><span class="w">
        </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"you cannot access this data"</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>One more thing: one more approach to permissions is to have a special place to ask server if the particular action is possible. It could be a special type in the type or root. If it says you can do it — do it, otherwise you’ll get an error in the errors key.</p>

<h3 id="application-errors">Application errors</h3>

<p>When something goes really bad on the server side (e.g., database is not responding or query format is wrong) we should put it to the <code class="language-plaintext highlighter-rouge">errors</code>. For instance:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"processOrder"</span><span class="p">:</span><span class="w"> </span><span class="err">nil</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"errors"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"code"</span><span class="p">:</span><span class="w"> </span><span class="s2">"client_error"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"details"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"request format is wrong"</span><span class="w"> </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Note that usually such errors should not be shown to the client as is. Instead, we should tell them that something wrong is happened and optionally ask to contact the support (optionally, because we should already know about this issue from our error tracker).</p>

<h2 id="handing-validation-errors">Handing validation errors</h2>

<p>Validation errors follow a similar pattern as mutation arguments, so it makes sense to keep them as a part of the response, rather than put them to the top–level <code class="language-plaintext highlighter-rouge">errors</code> array. In this case we keep the whole power of the GraphQL type system. In this case each mutation will have a union as the return type: <code class="language-plaintext highlighter-rouge">Success</code> will be returned when everything is fine, while <code class="language-plaintext highlighter-rouge">Failure</code> will represent validation errors.</p>

<p>For instance, imagine the mutation that registers a new user:</p>

<pre><code class="language-gql">mutation SignUp($email: String!, $password: String!) {
  signUp(email: $email, password: $password) {
    __typename

    ...on SignUpSuccess {
      user {
        id
      }
    }

    ...on SignUpFailure {
      email
      password
    }
  }
}
</code></pre>

<p>The successful response will look like this:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"signUp"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"__typename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"SignUpSuccess"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">123</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"errors"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>In case of failure we can access errors for each fields, which can be just lists of strings to show to user:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"signUp"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"__typename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"SignUpFailure"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"email"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
      </span><span class="nl">"password"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"should be longer than 8"</span><span class="p">]</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"errors"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Sometimes mutation fails not because of the input, but because of the database state, which is related to this particular mutation. For instance, imagine the mutation <code class="language-plaintext highlighter-rouge">ProcessOrder</code> that checks for order status and prohibits processing orders which are already processed. In this case return type could be <code class="language-plaintext highlighter-rouge">ProcessOrderSuccess | OrderCannotBeProcessed | ProcessOrderFailure</code>:</p>

<pre><code class="language-gql">mutation ProcessOrder($id: ID!) {
  processOrder(id: $id) {
    ...on ProcessOrderSuccess {
      order {
        ...OrderFields
      }
    }

    ...on OrderCannotBeProcessed {
      order {
        ...OrderFields
      }
      # no reason cause it's obvious
    }

    ...on ProcessOrderFailure {
      order {
        ...OrderFields
      }
      # we can also grab a reason of failure
      reason
    }
  }
}
</code></pre>

<p>The alternative to this approach could be a shared <code class="language-plaintext highlighter-rouge">Success</code> type which always points to the root of the graph, allowing clients to fetch whatever they want:</p>

<pre><code class="language-gql">mutation ProcessOrder($id: ID!) {
  processOrder(id: $id) {
    ...on ProcessOrderSuccess {
      # we're in the root so we can fetch whatever we need
      orderById($id: ID!) {
        ...OrderFields
      }
      viewer {
        email
      }
    }
  }
}
</code></pre>

<p>This is more flexible but also more verbose.</p>

<h2 id="a-word-about-monitoring">A word about monitoring</h2>

<p>One of the common techniques in monitoring is <a href="https://thenewstack.io/?p=22690467">USE</a>:</p>

<ul>
  <li><strong>U</strong>tlization represents the ratio of resource used (e.g., how many background workers are in use);</li>
  <li><strong>S</strong>aturation stands for the lack of the resource (e.g., how many background jobs are waiting while all workers are busy);</li>
  <li><strong>E</strong>rrors represent the amount of errors in the monitored component.</li>
</ul>

<p>As you might guess, we care about the last one in this post. In REST it was pretty simple to find errors: you can look at response HTTP codes and count 5xx. It’s note so easy with GraphQL, where most of responses are <code class="language-plaintext highlighter-rouge">200 OK</code>, so we should not forget to configure our monitoring tool to count responses with <code class="language-plaintext highlighter-rouge">errors</code> filled. Moreover, we should differ real errors from, say, permission errors. This is the moment when error codes come really handy.</p>

<hr />

<p>Let’s sum up what we discussed in the post:</p>

<ul>
  <li>generic errors that user should not see should go to <code class="language-plaintext highlighter-rouge">errors</code>;</li>
  <li>permissions errors are likely gonna go to <code class="language-plaintext highlighter-rouge">errors</code> too;</li>
  <li>when if we put something to <code class="language-plaintext highlighter-rouge">errors</code> — it’s better to have a structure (e.g., code and details)</li>
  <li>validations and mutation specific errors should go to the mutation payload.</li>
</ul>

<hr />

<p>Building a GraphQL API in Rails and want it done right from the start? <a href="/consulting/">I offer GraphQL design consulting.</a></p>]]></content><author><name></name></author><category term="graphql" /><summary type="html"><![CDATA[GraphQL errors best practices]]></summary></entry><entry><title type="html">🎈 Blog turns one year!</title><link href="https://dmitrytsepelev.dev/blog-turns-one-year" rel="alternate" type="text/html" title="🎈 Blog turns one year!" /><published>2023-03-23T00:00:00+00:00</published><updated>2023-03-23T00:00:00+00:00</updated><id>https://dmitrytsepelev.dev/blog-turns-one-year</id><content type="html" xml:base="https://dmitrytsepelev.dev/blog-turns-one-year"><![CDATA[<p>One year ago, on March 23 I rolled out a first article to this blog, and, looking back, it was a damn good decision! In this short post I’ll share some results and stats.</p>

<h2 id="overall">Overall</h2>

<p>During the year I published <em>12 posts</em>: 8 about Rails, 2 about Haskell, 1 about databaseas and 1 essay about architecture. Overall there were <em>26 300 unique visitors</em>. Thanks for the attention 🙂</p>

<h2 id="top-articles">Top articles</h2>

<h3 id="-why-ruby-has-symbols">🥇 <a href="/why-has-ruby-symbols">Why Ruby has symbols</a></h3>

<p>A post about Ruby internals, rolled out in April, which got most attention: about 11 000 unique visitors spending 5 minutes 30 seconds on the page! Moreover, it was <a href="https://techracho.bpsinc.jp/hachi8833/2022_04_28/117351">translated</a> to Japaneese, recognized as <a href="https://rubyweekly.com/issues/634">one of most popular</a> posts in RubyWeekly and reached #4 on Hackernews.</p>

<p><img src="/assets/why-ruby-has-symbols-on-hn.jpg" alt="proof" /></p>

<h3 id="-understading-why-attr_accessor-in-ruby-is-faster-than-a-regular-method">🥈 <a href="/attr-accessor-in-ruby">Understading why attr_accessor in Ruby is faster than a regular method</a></h3>

<p>Another post on how Ruby works, which was rolled out in June. This one got 3 500 unique visitors with 4 minutes 45 seconds on the page.</p>

<h3 id="-how-to-make-ruby-interpreter-run-program-written-in-a-natural-language">🥉 <a href="/natural-language-programming-with-ruby">How to make Ruby interpreter run program written in a natural language</a></h3>

<p>A weird post about metaprogramming, that was visited by 2 300 unique users, and it took them 3 minutes 22 seconds in average to dig into it.</p>

<h2 id="those-who-didnt-make-it-to-the-top">Those who didn’t make it to the top</h2>

<table>
  <thead>
    <tr>
      <th>#</th>
      <th>Title</th>
      <th>Unique visitors</th>
      <th>Avergage time on page</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>4</td>
      <td><a href="/finding-rails-memory-bloats-early">How to find a memory bloat in your Rails app before it happens</a></td>
      <td>1400</td>
      <td>5m 17 s</td>
    </tr>
    <tr>
      <td>5</td>
      <td><a href="/haskell-adventures-functors">Haskell Adventures: Functors</a></td>
      <td>762</td>
      <td>2m 45s</td>
    </tr>
    <tr>
      <td>6</td>
      <td><a href="/haskell-adventures">Haskell Adventures: digging into the declarative approach</a></td>
      <td>750</td>
      <td>3m 4s</td>
    </tr>
    <tr>
      <td>7</td>
      <td><a href="/global-methods-in-ruby">Fantastic global methods in Ruby and where to find them</a></td>
      <td>741</td>
      <td>3m 12s</td>
    </tr>
    <tr>
      <td>8</td>
      <td><a href="/applicative-ruby-railway">Applicative programming in Ruby: railway reimagined</a></td>
      <td>628</td>
      <td>2m 50s</td>
    </tr>
    <tr>
      <td>9</td>
      <td><a href="/on-ideal-architecture">On chosing the ideal architecture of the web application</a></td>
      <td>569</td>
      <td>3m 2s</td>
    </tr>
    <tr>
      <td>10</td>
      <td><a href="/complex-reports-in-postgresql">Building complex reports for your own DVD rental shop using PostgreSQL window functions</a></td>
      <td>536</td>
      <td>3m 51s</td>
    </tr>
    <tr>
      <td>11</td>
      <td><a href="/applicative-ruby-advanced-behavior">Applicative programming in Ruby: advanced behaviors</a></td>
      <td>381</td>
      <td>2m 1s</td>
    </tr>
    <tr>
      <td>12</td>
      <td><a href="/kwarg-extensions-gql-ruby">How to configure field extensions using keyword arguments in GraphQL Ruby</a></td>
      <td>284</td>
      <td>4m 13s</td>
    </tr>
  </tbody>
</table>]]></content><author><name></name></author><summary type="html"><![CDATA[How to detect a possible memory bloat in Rails before it starts being a problem]]></summary></entry><entry><title type="html">How to find a memory bloat in your Rails app before it happens</title><link href="https://dmitrytsepelev.dev/finding-rails-memory-bloats-early" rel="alternate" type="text/html" title="How to find a memory bloat in your Rails app before it happens" /><published>2023-02-21T00:00:00+00:00</published><updated>2023-02-21T00:00:00+00:00</updated><id>https://dmitrytsepelev.dev/finding-rails-memory-bloats-early</id><content type="html" xml:base="https://dmitrytsepelev.dev/finding-rails-memory-bloats-early"><![CDATA[<p>Memory bloat in Ruby happens when someone loads a lot of data to the memory. Ruby Virtual Machine does not return most of the allocated memory to the operating system even after data is collected as a garbage. It’s not a big deal for local development or console programs, but if you have a bloat in the big Rails app it might cost you a lot of money.</p>

<p>It is quite easy to find a fix memory bloat when it happens (you have a monitoring tool set up, right?), but can we <em>prevent</em> it? In this article I will present a new approach for that, using a new gem <a href="https://github.com/DmitryTsepelev/io_monitor">io_monitor</a> as a reference implementation.</p>

<h2 id="understanding-how-bloat-happens">Understanding how bloat happens</h2>

<p>Imagine a pretty regular Rails app that sometimes goes to the database for data. When we load some data from the database we put it to the memory, and, when we do not have enough memory, Ruby VM goes to the operating system and asks for more. All Ruby objects in memory are organized into <em>pages</em>, and Ruby VM can return memory back to the operating system only from <em>last free pages</em>. As a result, if your app loaded a lot of data, the pages it was stored at can be returned to the system only if there is nothing that’s still in use after them.</p>

<p>I recommend this <a href="https://www.joyfulbikeshedding.com/blog/2019-03-14-what-causes-ruby-memory-bloat.html">post</a> with lots of pictures to get a better undestanding of this process.</p>

<h2 id="why-bloat-is-a-bad-thing">Why bloat is a bad thing?</h2>

<p>Your app will need more resources to run; moreover, if you live in the Kubernetes cluster you’ll be able to run less pods on the same machine and can see some restarts when quota is reached.</p>

<p>The memory bloat is easy to find when it happens: most of APMs will show you the place where application performed a lot of allocations, so you just need to go it fix this. Can we do it <em>before</em> it starts being a problem?</p>

<h2 id="early-bloat-detection">Early bloat detection</h2>

<p>In most of cases (well, I do not have statistics except my own experience) memory bloat is caused by I/O operations. You fetch data from the database or Redis, read file from the disk, perform the network request, and unexpectedly big amount of data is about to be allocated in the application memory.</p>

<blockquote>
  <p>Of course there is a chance that your app itself creates a lot of objects from the code, but it’s a pretty rare thing to happen</p>
</blockquote>

<p>The fix is usually trivial: you need to either load data in batches (so memory consumption will be the same as the batch size) or do not load it at all and perform calculations <em>somewhere else</em>. For instance, if you need a <code class="language-plaintext highlighter-rouge">sum</code> of transactions — do <code class="language-plaintext highlighter-rouge">Transaction.sum(:amount)</code> (which is <code class="language-plaintext highlighter-rouge">SELECT SUM(amount) FROM transactions</code>) instead of <code class="language-plaintext highlighter-rouge">Transaction.all.sum(&amp;:amount)</code> (which is <code class="language-plaintext highlighter-rouge">SELECT * FROM transactions</code> and send it over the wire).</p>

<p>Here is an idea: what if we try to measure the size of data that was loaded from the I/O and compare it to the response size? Even in case if you have 100 transactions to get the sum and your APM keeps silence, this ratio will still be quite big!</p>

<p>For instance, for ActiveRecord you can do something like this:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">ActiveRecord</span><span class="o">::</span><span class="no">ConnectionAdapters</span><span class="o">::</span><span class="no">AbstractAdapter</span><span class="p">.</span><span class="nf">prepend</span><span class="p">(</span><span class="no">Module</span><span class="p">.</span><span class="nf">new</span> <span class="k">do</span>
  <span class="k">def</span> <span class="nf">build_result</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span><span class="p">)</span>
    <span class="n">io_bytesize</span> <span class="o">=</span> <span class="n">kwargs</span><span class="p">[</span><span class="ss">:rows</span><span class="p">].</span><span class="nf">sum</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">row</span><span class="o">|</span>
      <span class="n">row</span><span class="p">.</span><span class="nf">sum</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">val</span><span class="o">|</span>
        <span class="p">((</span><span class="no">String</span> <span class="o">===</span> <span class="n">val</span><span class="p">)</span> <span class="p">?</span> <span class="n">val</span> <span class="p">:</span> <span class="n">val</span><span class="p">.</span><span class="nf">to_s</span><span class="p">).</span><span class="nf">bytesize</span>
      <span class="k">end</span>
    <span class="k">end</span>

    <span class="no">Aggregator</span><span class="p">.</span><span class="nf">instance</span><span class="p">.</span><span class="nf">increment</span><span class="p">(</span><span class="n">io_bytesize</span><span class="p">)</span>

    <span class="k">super</span>
  <span class="k">end</span>
<span class="k">end</span><span class="p">)</span>
</code></pre></div></div>

<p>Now we need to subscribe to notifications from controllers for action processing:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">ActiveSupport</span><span class="o">::</span><span class="no">Notifications</span><span class="p">.</span><span class="nf">subscribe</span><span class="p">(</span><span class="s2">"process_action.action_controller"</span><span class="p">)</span> <span class="k">do</span> <span class="o">|*</span><span class="n">args</span><span class="o">|</span>
  <span class="n">io_bytesize</span> <span class="o">=</span> <span class="no">Aggregator</span><span class="p">.</span><span class="nf">instance</span><span class="p">.</span><span class="nf">io_bytesize</span>
  <span class="n">body_bytesize</span> <span class="o">=</span> <span class="n">args</span><span class="p">.</span><span class="nf">last</span><span class="p">[</span><span class="ss">:response</span><span class="p">].</span><span class="nf">body</span><span class="p">.</span><span class="nf">bytesize</span>

  <span class="n">ratio</span> <span class="o">=</span> <span class="n">body_bytesize</span><span class="p">.</span><span class="nf">to_f</span> <span class="o">/</span> <span class="n">io_bytesize</span>

  <span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span> <span class="s2">"Loaded from I/O </span><span class="si">#{</span><span class="n">io_bytesize</span><span class="si">}</span><span class="s2">, response bytesize </span><span class="si">#{</span><span class="n">body_bytesize</span><span class="si">}</span><span class="s2">, I/O to response ratio </span><span class="si">#{</span><span class="n">ratio</span><span class="si">}</span><span class="s2">"</span>
<span class="k">end</span>
</code></pre></div></div>

<p>As a result, we can see messages about actions that have a high ratio in our logs!</p>

<blockquote>
  <p>You can find the whole example <a href="https://github.com/DmitryTsepelev/io-monitor-demo">here</a>.</p>
</blockquote>

<p>Let’s add a very simple controller with two actions — fast and slow:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">App</span> <span class="o">&lt;</span> <span class="no">Rails</span><span class="o">::</span><span class="no">Application</span>
  <span class="n">routes</span><span class="p">.</span><span class="nf">append</span> <span class="k">do</span>
    <span class="n">get</span> <span class="s1">'/slow'</span><span class="p">,</span> <span class="ss">to: </span><span class="s1">'app#slow'</span>
    <span class="n">get</span> <span class="s1">'/fast'</span><span class="p">,</span> <span class="ss">to: </span><span class="s1">'app#fast'</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="k">class</span> <span class="nc">AppController</span> <span class="o">&lt;</span> <span class="no">ActionController</span><span class="o">::</span><span class="no">Base</span>
  <span class="k">def</span> <span class="nf">slow</span>
    <span class="n">render</span> <span class="ss">json: </span><span class="p">{</span><span class="ss">sum: </span><span class="no">Transaction</span><span class="p">.</span><span class="nf">all</span><span class="p">.</span><span class="nf">sum</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:amount</span><span class="p">)}</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">fast</span>
    <span class="n">render</span> <span class="ss">json: </span><span class="p">{</span><span class="ss">sum: </span><span class="no">Transaction</span><span class="p">.</span><span class="nf">sum</span><span class="p">(</span><span class="ss">:amount</span><span class="p">)}</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>If you run it — you’ll notice the difference in logs:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Started GET "/slow" for 127.0.0.1 at 2023-02-04 23:01:27 +0300
Processing by AppController#slow as */*
  Transaction Load (4.5ms)  SELECT "transactions".* FROM "transactions"
Completed 200 OK in 43ms (Views: 0.1ms | ActiveRecord: 11.7ms | Allocations: 95836)
Loaded from I/O 69899, response bytesize 15, I/O to response ratio 4659.933333333333

Started GET "/fast" for 127.0.0.1 at 2023-02-04 23:01:35 +0300
Processing by AppController#fast as */*
  Transaction Sum (3.1ms)  SELECT SUM("transactions"."amount") FROM "transactions"
Completed 200 OK in 4ms (Views: 0.1ms | ActiveRecord: 3.1ms | Allocations: 336)
Loaded from I/O 7, response bytesize 15, I/O to response ratio 0.4666666666666667
</code></pre></div></div>

<h2 id="io_monitor">io_monitor</h2>

<p>If you want to try this approach in your application, you don’t need to build this from scratch. Use <a href="https://github.com/DmitryTsepelev/io_monitor">io_monitor</a>! Right now it supports Redis and HTTP along with ActiveRecord, and can publish to logs, Rails notifications and Prometheus.</p>

<p>Setup is fairly simple. After installation you need to configure what to monitor and where to publish and you’re good to go:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">IoMonitor</span><span class="p">.</span><span class="nf">configure</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
  <span class="n">config</span><span class="p">.</span><span class="nf">publish</span> <span class="o">=</span> <span class="p">[</span><span class="ss">:logs</span><span class="p">,</span> <span class="ss">:notifications</span><span class="p">,</span> <span class="ss">:prometheus</span><span class="p">]</span> <span class="c1"># defaults to :logs</span>
  <span class="n">config</span><span class="p">.</span><span class="nf">warn_threshold</span> <span class="o">=</span> <span class="mf">0.8</span> <span class="c1"># defaults to 0</span>
  <span class="n">config</span><span class="p">.</span><span class="nf">adapters</span> <span class="o">=</span> <span class="p">[</span><span class="ss">:active_record</span><span class="p">,</span> <span class="ss">:net_http</span><span class="p">,</span> <span class="ss">:redis</span><span class="p">]</span> <span class="c1"># defaults to [:active_record]</span>
<span class="k">end</span>
</code></pre></div></div>

<p>After that you need to include it controllers you want to check (or all of them):</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">MyController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
  <span class="kp">include</span> <span class="no">IoMonitor</span><span class="o">::</span><span class="no">Controller</span>
<span class="k">end</span>
</code></pre></div></div>

<p>If something is not right you’ll see something in logs:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ActiveRecord I/O to response payload ratio is 0.1, while threshold is 0.8
</code></pre></div></div>

<p>I never tested it in the real app yet, so please let me know if you notice something weird!</p>

<hr />

<p>In this post we discussed memory bloats: now you can not only notice and fix them, but also try to <em>prevent</em>. The main outcome is a bit different though: if you work with I/O — always consider the amount of data you might get and try to minimize this amount.</p>

<hr />

<p>Memory issues in production are tricky to reproduce. If you’re dealing with a Rails app that’s eating too much memory, <a href="/consulting/">I offer performance audits</a>.</p>]]></content><author><name></name></author><category term="ruby" /><category term="performance" /><summary type="html"><![CDATA[How to detect a possible memory bloat in Rails before it starts being a problem]]></summary></entry><entry><title type="html">Fantastic global methods in Ruby and where to find them</title><link href="https://dmitrytsepelev.dev/global-methods-in-ruby" rel="alternate" type="text/html" title="Fantastic global methods in Ruby and where to find them" /><published>2023-01-31T00:00:00+00:00</published><updated>2023-01-31T00:00:00+00:00</updated><id>https://dmitrytsepelev.dev/global-methods-in-ruby</id><content type="html" xml:base="https://dmitrytsepelev.dev/global-methods-in-ruby"><![CDATA[<p>Many languages allow defining methods globally, which makes them available everywhere. Ruby is not different: you can define a method on the “top–level” and it behave like a global one. However, everything in Ruby is class, which means that these methods should belong to something. In this post we will figure out how global methods really work in Ruby!</p>

<hr />

<p>Run the <code class="language-plaintext highlighter-rouge">irb</code> and define the method like usual:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">foo</span> <span class="o">=</span> <span class="mi">42</span>
</code></pre></div></div>

<p>If you try to call it—you’ll obviously get <code class="language-plaintext highlighter-rouge">42</code>. What does it mean? It goes without saying that the method works and we can call it, but the interesting part is not about it. We know that Ruby is purely object–oriented language: everything is class or instance of the class.</p>

<blockquote>
  <p>All examples in this post can be run in IRB or copied to file and run using the <code class="language-plaintext highlighter-rouge">ruby</code> interpreter, the result would be the same</p>
</blockquote>

<p>Let’s create a new class and try to call <code class="language-plaintext highlighter-rouge">#foo</code> from it:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Example</span>
  <span class="k">def</span> <span class="nf">run_foo</span>
    <span class="n">foo</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="no">Example</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">run_foo</span> <span class="c1"># =&gt; 42</span>
</code></pre></div></div>

<p>It works! Our method can be called even from the method in a different class. It behaves like a global method.</p>

<p>Wait, doesn’t it sound suspicious? We defined the method globally and we can call it but there is no <em>global</em> scope: it should be defined in some class. So where is it defined?</p>

<h2 id="ruby-object-model">Ruby object model</h2>

<blockquote>
  <p>This section is a brief recap of the <a href="https://pragprog.com/titles/ppmetr2/metaprogramming-ruby-2/">Metaprogramming Ruby 2</a> by Paolo Perrotta and <a href="https://craftinginterpreters.com">Crafting Interpreters</a> by Robert Nystrom</p>
</blockquote>

<p>Every class in Ruby is the instance of the class called <code class="language-plaintext highlighter-rouge">Class</code>. In the Ruby VM it’s represented as a structure called <code class="language-plaintext highlighter-rouge">RClass</code>, which contains:</p>

<ul>
  <li>a list of methods;</li>
  <li>a list of instance variable names;</li>
  <li>a list of class–level instance variables;</li>
  <li>a pointer to the <em>class</em> this class is instance of (as we know, it’s <code class="language-plaintext highlighter-rouge">Class</code>);</li>
  <li>a pointer to the <em>superclass</em>, which is a parent class for the current one.</li>
</ul>

<p><img src="/assets/rclass.png" alt="RClass" /></p>

<p>According to the encapsulation principle of Object–Oriented Programming, data should be bound to the methods that operate on that data into a single unit. However, implementing it in the VM would require copying the byte code of methods to the each instance of the class. Since methods are shared, it makes sense to keep them in the single place, and Ruby keeps them in the instance of the <code class="language-plaintext highlighter-rouge">Class</code> (e.g., the byte code of <code class="language-plaintext highlighter-rouge">#run_foo</code> is hold in the <code class="language-plaintext highlighter-rouge">Example</code>).</p>

<p>Because of the metaprogramming features of Ruby we can access and modify contents of the class in the runtime. With this knowledge, let’s try to understand, where the code from the “top–level scope” is executed.</p>

<h2 id="examining-the-toplevel-scope">Examining the top–level scope</h2>

<p>Let’s try to understand where we are when we write the code on the top–level scope outside of any class:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">puts</span> <span class="nb">self</span> <span class="c1">#=&gt; main</span>
</code></pre></div></div>

<p>It did not help a lot: we learned only that we are inside the object that overrides the <code class="language-plaintext highlighter-rouge">#to_s</code> method. Let’s check our <em>class</em>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">puts</span> <span class="nb">self</span><span class="p">.</span><span class="nf">class</span> <span class="c1"># =&gt; Object</span>
</code></pre></div></div>

<p>Good, we’re inside some <a href="https://ruby-doc.org/3.2.0/Object.html">Object</a>, which is a default base class for all classes. If we are inside the <code class="language-plaintext highlighter-rouge">Object</code> instance and there is a <code class="language-plaintext highlighter-rouge">#foo</code> method, we are supposed to be able to create a new instance and call the method:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Object</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">foo</span> <span class="c1">#=&gt; private method `foo' called for #&lt;Object:0x00000001402cec50&gt;</span>
</code></pre></div></div>

<p>Exception: we cannot call the method because it’s private! Let’s make sure that it’s correct:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">private_methods</span><span class="p">.</span><span class="nf">include?</span> <span class="ss">:foo</span> <span class="c1">#=&gt; true</span>
</code></pre></div></div>

<p>Does <code class="language-plaintext highlighter-rouge">Object</code> have a base class?</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Object</span><span class="p">.</span><span class="nf">superclass</span> <span class="c1"># =&gt; BasicObject</span>
</code></pre></div></div>

<p>According to the <a href="https://ruby-doc.org/core-3.1.1/BasicObject.html">docs</a>, <code class="language-plaintext highlighter-rouge">BasicObject</code> is a base class for new object hierarchies, and it’s completely empty. Therefore, “top–level” methods won’t be available in your class if you inherit right from it:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">BasicObject</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">foo</span> <span class="c1"># =&gt; undefined method `foo' for #&lt;BasicObject:0x00000001058d5648&gt; (NoMethodError)</span>
</code></pre></div></div>

<p>Let’s summarize our explorations:</p>

<ul>
  <li>when some method is defined on the top–level scope, it’s defined in the <code class="language-plaintext highlighter-rouge">Object</code> class;</li>
  <li>this method can be called from any other class, except ones inheriting from <code class="language-plaintext highlighter-rouge">BasicObject</code>;</li>
  <li>the defined method is private, so it cannot be called directly on the instance of the <code class="language-plaintext highlighter-rouge">Object</code>.</li>
</ul>

<p>Looks like that’s the answer! There is <em>no global scope</em>, we can access the method on the top level (because it’s defined here) or in other classes (because they inherit from <code class="language-plaintext highlighter-rouge">Object</code>), but this method is not visible from the outside because it’s private.</p>

<h2 id="what-about-variables">What about variables?</h2>

<p>Let’s repeat the experiment, but instead of defining <code class="language-plaintext highlighter-rouge">foo</code> as a method we are going to make it variable:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">foo</span> <span class="o">=</span> <span class="mi">42</span>

<span class="k">class</span> <span class="nc">Example</span>
  <span class="k">def</span> <span class="nf">get_foo</span>
    <span class="n">foo</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="no">Example</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">get_foo</span> <span class="c1"># =&gt; undefined local variable or method `foo' for #&lt;Example:0x0000000126ac0d38&gt;</span>
</code></pre></div></div>

<p>Exception again! The problem is that <code class="language-plaintext highlighter-rouge">foo</code> was not available inside the method, because it’s not local for the instance of the <code class="language-plaintext highlighter-rouge">Example</code> class.</p>

<p>Like before, let’s check how <code class="language-plaintext highlighter-rouge">foo</code> was defined.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">instance_variables</span> <span class="c1"># =&gt; []</span>
</code></pre></div></div>

<p>No instance variables! Maybe it’s local?</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">local_variables</span> <span class="c1">#=&gt; [:foo, :_, :version, :str]</span>
</code></pre></div></div>

<p>Correct! Our variable <code class="language-plaintext highlighter-rouge">foo</code> is defined locally for the top–level scope (or, more preciselu, for the instance of the <code class="language-plaintext highlighter-rouge">Object</code> we are executing our code) and that’s why it’s not available inside the instance of <code class="language-plaintext highlighter-rouge">Example</code>, which inherits from the <code class="language-plaintext highlighter-rouge">Object</code>.</p>

<hr />

<p>How does it work?</p>

<p>When we run Ruby script using <code class="language-plaintext highlighter-rouge">ruby</code> or REPL using <code class="language-plaintext highlighter-rouge">irb</code>, Ruby creates a new <em>workspace</em> for us. By default it will use <code class="language-plaintext highlighter-rouge">TOPLEVEL_BINDING</code> for everything, but it’s possible to pass a different binding to the workspace constructor. Here is a <a href="https://github.com/ruby/ruby/blob/3d6500ee6e3676979d5199dc7b5feab0d15235de/lib/irb/workspace.rb">source code</a> that runs a new IRB session.</p>]]></content><author><name></name></author><category term="ruby" /><summary type="html"><![CDATA[Where Ruby methods keeps methods and variables defined on the top–level scope]]></summary></entry><entry><title type="html">Applicative programming in Ruby: advanced behaviors</title><link href="https://dmitrytsepelev.dev/applicative-ruby-advanced-behavior" rel="alternate" type="text/html" title="Applicative programming in Ruby: advanced behaviors" /><published>2023-01-10T00:00:00+00:00</published><updated>2023-01-10T00:00:00+00:00</updated><id>https://dmitrytsepelev.dev/applicative-ruby-advanced-behaviors</id><content type="html" xml:base="https://dmitrytsepelev.dev/applicative-ruby-advanced-behavior"><![CDATA[<p><strong><a href="/applicative-ruby-railway">Part 1</a> | Part 2</strong></p>

<p>In the previous <a href="/applicative-ruby-advanced-behavior">post</a> we implemented safe currying on applicative functors. We used a container called <code class="language-plaintext highlighter-rouge">Either</code> to create these interfaces. In this post we will go even further and create applicative lists and parsers.</p>

<h2 id="applicative-array">Applicative array</h2>

<p>Let’s take a look how functors can be implemented for lists:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">ApplicativeArray</span> <span class="o">&lt;</span> <span class="no">Array</span>
  <span class="kp">include</span> <span class="no">Functor</span>
  <span class="k">def</span> <span class="nf">fmap</span><span class="p">(</span><span class="o">&amp;</span><span class="n">fn</span><span class="p">)</span> <span class="o">=</span> <span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="n">fn</span><span class="p">.</span><span class="nf">curry</span><span class="p">)</span>

  <span class="kp">include</span> <span class="no">Applicative</span>
  <span class="k">class</span> <span class="o">&lt;&lt;</span> <span class="nb">self</span>
    <span class="k">def</span> <span class="nf">pure</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o">=</span> <span class="no">ApplicativeArray</span><span class="p">.</span><span class="nf">new</span><span class="p">([</span><span class="n">x</span><span class="p">])</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">^</span><span class="p">(</span><span class="n">other</span><span class="p">)</span>
    <span class="no">ApplicativeArray</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">flat_map</span> <span class="p">{</span> <span class="o">|</span><span class="n">fn</span><span class="o">|</span> <span class="n">other</span><span class="p">.</span><span class="nf">fmap</span><span class="p">(</span><span class="o">&amp;</span><span class="n">fn</span><span class="p">)</span> <span class="p">})</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">Functor#fmap</code> is almost a regular map, but it uses <code class="language-plaintext highlighter-rouge">curry</code> call functions inside the list partially. <code class="language-plaintext highlighter-rouge">pure</code> takes the value and wraps it into the array. Application (<code class="language-plaintext highlighter-rouge">^</code>) takes function from another array and applies it to each element of the current array. Let’s try it:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">plus</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span>
<span class="k">def</span> <span class="nf">mult</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="o">=</span> <span class="n">x</span> <span class="o">*</span> <span class="n">y</span>

<span class="n">array_with_functions</span> <span class="o">=</span> <span class="no">ApplicativeArray</span><span class="p">.</span><span class="nf">new</span><span class="p">([</span><span class="nb">method</span><span class="p">(</span><span class="ss">:plus</span><span class="p">),</span> <span class="nb">method</span><span class="p">(</span><span class="ss">:mult</span><span class="p">)])</span>
<span class="n">array_with_args</span> <span class="o">=</span> <span class="no">ApplicativeArray</span><span class="p">.</span><span class="nf">new</span><span class="p">([</span><span class="mi">2</span><span class="p">,</span> <span class="mi">7</span><span class="p">])</span>
<span class="n">array_with_args_2</span> <span class="o">=</span> <span class="no">ApplicativeArray</span><span class="p">.</span><span class="nf">new</span><span class="p">([</span><span class="mi">3</span><span class="p">,</span> <span class="mi">5</span><span class="p">])</span>

<span class="n">array_with_functions</span> <span class="o">^</span> <span class="n">array_with_args</span> <span class="o">^</span> <span class="n">array_with_args_2</span>
<span class="c1"># =&gt; [5, 7, 10, 12, 6, 10, 21, 35]</span>
</code></pre></div></div>

<p>Why 8 elements?</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">plus</code> is applied to two elements;</li>
  <li><code class="language-plaintext highlighter-rouge">mult</code> is applied to same elements, getting 4 partially applied functions;</li>
  <li>these functions are applied to each of two elements getting 8 elements in total.</li>
</ol>

<p>Can we apply functions to the corresponding element only? Sure, in Haskell this type of the list is called <code class="language-plaintext highlighter-rouge">ZipList</code>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Applicative::ZipList</span>
  <span class="nb">attr_reader</span> <span class="ss">:list</span>

  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">list</span><span class="p">)</span> <span class="o">=</span> <span class="vi">@list</span> <span class="o">=</span> <span class="n">list</span>

  <span class="kp">include</span> <span class="no">Applicative</span><span class="o">::</span><span class="no">Functor</span>
  <span class="k">def</span> <span class="nf">fmap</span><span class="p">(</span><span class="o">&amp;</span><span class="n">fn</span><span class="p">)</span> <span class="o">=</span> <span class="vi">@list</span><span class="p">.</span><span class="nf">map</span><span class="p">(</span><span class="o">&amp;</span><span class="n">fn</span><span class="p">.</span><span class="nf">curry</span><span class="p">)</span>

  <span class="kp">include</span> <span class="no">Applicative</span><span class="o">::</span><span class="no">ApplicativeFunctor</span>

  <span class="k">class</span> <span class="o">&lt;&lt;</span> <span class="nb">self</span>
    <span class="k">def</span> <span class="nf">pure</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o">=</span> <span class="no">Applicative</span><span class="o">::</span><span class="no">ZipList</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">repeatedly</span> <span class="p">{</span> <span class="n">x</span> <span class="p">})</span>

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

    <span class="k">def</span> <span class="nf">repeatedly</span><span class="p">(</span><span class="o">&amp;</span><span class="n">block</span><span class="p">)</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">y</span><span class="o">|</span>
        <span class="kp">loop</span> <span class="p">{</span> <span class="n">y</span> <span class="o">&lt;&lt;</span> <span class="n">block</span><span class="p">.</span><span class="nf">call</span> <span class="p">}</span>
      <span class="k">end</span><span class="p">.</span><span class="nf">lazy</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">^</span><span class="p">(</span><span class="n">other</span><span class="p">)</span>
    <span class="n">eager_list</span> <span class="o">=</span>
      <span class="k">if</span> <span class="vi">@list</span><span class="p">.</span><span class="nf">is_a?</span><span class="p">(</span><span class="no">Enumerator</span><span class="o">::</span><span class="no">Lazy</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="n">other</span><span class="p">.</span><span class="nf">list</span><span class="p">.</span><span class="nf">is_a?</span><span class="p">(</span><span class="no">Enumerator</span><span class="o">::</span><span class="no">Lazy</span><span class="p">)</span>
        <span class="vi">@list</span>
      <span class="k">else</span>
        <span class="vi">@list</span><span class="p">.</span><span class="nf">take</span><span class="p">(</span><span class="n">other</span><span class="p">.</span><span class="nf">list</span><span class="p">.</span><span class="nf">length</span><span class="p">)</span>
      <span class="k">end</span>

    <span class="no">Applicative</span><span class="o">::</span><span class="no">ZipList</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">eager_list</span><span class="p">.</span><span class="nf">zip</span><span class="p">(</span><span class="n">other</span><span class="p">.</span><span class="nf">list</span><span class="p">).</span><span class="nf">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">fn</span><span class="p">,</span> <span class="n">x</span><span class="o">|</span> <span class="n">fn</span><span class="p">.</span><span class="nf">curry</span><span class="o">.</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="p">})</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Implementation is slightly more complex, because we have to handle <em>endless</em> lists: we need to do that because <code class="language-plaintext highlighter-rouge">pure</code> returns it. Here is an example:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">plus</span> <span class="o">=</span> <span class="nb">lambda</span> <span class="p">{</span> <span class="o">|</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="o">|</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span> <span class="p">}</span>
<span class="n">mult</span> <span class="o">=</span> <span class="nb">lambda</span> <span class="p">{</span> <span class="o">|</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="o">|</span> <span class="n">x</span> <span class="o">*</span> <span class="n">y</span> <span class="p">}</span>

<span class="n">functions</span> <span class="o">=</span> <span class="no">ZipList</span><span class="p">.</span><span class="nf">new</span><span class="p">([</span><span class="n">plus</span><span class="p">,</span> <span class="n">mult</span><span class="p">])</span>
<span class="n">args</span> <span class="o">=</span> <span class="no">ZipList</span><span class="p">.</span><span class="nf">new</span><span class="p">([</span><span class="mi">2</span><span class="p">,</span> <span class="mi">7</span><span class="p">])</span>
<span class="n">args_2</span> <span class="o">=</span> <span class="no">ZipList</span><span class="p">.</span><span class="nf">new</span><span class="p">([</span><span class="mi">3</span><span class="p">,</span> <span class="mi">5</span><span class="p">])</span>

<span class="p">(</span><span class="n">functions</span> <span class="o">^</span> <span class="n">args</span> <span class="o">^</span> <span class="n">args_2</span><span class="p">).</span><span class="nf">list</span> <span class="c1"># =&gt; [5, 35]</span>
</code></pre></div></div>

<p>Let’s try the same thing but using <code class="language-plaintext highlighter-rouge">pure</code>, that constructs an endless list for us:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">functions</span> <span class="o">=</span> <span class="no">ZipList</span><span class="o">::</span><span class="n">pure</span><span class="p">(</span><span class="n">plus</span><span class="p">)</span> <span class="c1"># endless</span>
<span class="n">args</span> <span class="o">=</span> <span class="no">ZipList</span><span class="o">::</span><span class="n">pure</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="c1"># endless</span>
<span class="n">args_2</span> <span class="o">=</span> <span class="no">ZipList</span><span class="p">.</span><span class="nf">new</span><span class="p">([</span><span class="mi">4</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">8</span><span class="p">])</span> <span class="c1"># finite</span>

<span class="p">(</span><span class="n">functions</span> <span class="o">^</span> <span class="n">args</span> <span class="o">^</span> <span class="n">args_2</span><span class="p">).</span><span class="nf">list</span><span class="p">.</span><span class="nf">eager</span><span class="p">.</span><span class="nf">to_a</span> <span class="c1"># =&gt; [6, 8, 10]</span>
</code></pre></div></div>

<p>In this case we get only 3 elements (2 + 4, 2 + 6 and 2 + 8).</p>

<h3 id="applicative-parser">Applicative parser</h3>

<p>Let’s take a look at more complex application of applicatives: an applicative <em>parser</em>. We start with the new data structure called <code class="language-plaintext highlighter-rouge">Pair</code>, which can hold two values:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Pair</span>
  <span class="nb">attr_reader</span> <span class="ss">:fst</span><span class="p">,</span> <span class="ss">:snd</span>

  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">fst</span><span class="p">,</span> <span class="n">snd</span><span class="p">)</span>
    <span class="vi">@fst</span> <span class="o">=</span> <span class="n">fst</span>
    <span class="vi">@snd</span> <span class="o">=</span> <span class="n">snd</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">deconstruct</span> <span class="o">=</span> <span class="p">[</span><span class="vi">@fst</span><span class="p">,</span> <span class="vi">@snd</span><span class="p">]</span>

  <span class="kp">include</span> <span class="no">Applicative</span><span class="o">::</span><span class="no">Functor</span>
  <span class="k">def</span> <span class="nf">fmap</span><span class="p">(</span><span class="o">&amp;</span><span class="n">fn</span><span class="p">)</span> <span class="o">=</span> <span class="no">Pair</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">fst</span><span class="p">,</span> <span class="n">fn</span><span class="o">.</span><span class="p">(</span><span class="n">snd</span><span class="p">))</span>
<span class="k">end</span>
</code></pre></div></div>

<p>It will be a functor, where <code class="language-plaintext highlighter-rouge">fmap</code> applies function to the <em>second</em> element of the pair, without changing the first one. Now let’s define a class that presents a generic parser:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Parser</span>
  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="o">&amp;</span><span class="n">parse_fn</span><span class="p">)</span> <span class="o">=</span> <span class="vi">@parse_fn</span> <span class="o">=</span> <span class="n">parse_fn</span>

  <span class="k">def</span> <span class="nf">parse</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">=</span> <span class="vi">@parse_fn</span><span class="o">.</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
<span class="k">end</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">parse_fn</code> function should accept and parses some string. When parsing fails it should return <code class="language-plaintext highlighter-rouge">Left</code> with error message, otherwise — <code class="language-plaintext highlighter-rouge">Right</code> with the <code class="language-plaintext highlighter-rouge">Pair</code>. The first element of the <code class="language-plaintext highlighter-rouge">Pair</code> is the remaining string, the second element is the thing we just parsed. For instance, here is a parser that looks for a character <code class="language-plaintext highlighter-rouge">'x'</code> in the beginning of the string:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">x_parser</span> <span class="o">=</span> <span class="no">Parser</span><span class="p">.</span><span class="nf">new</span> <span class="k">do</span> <span class="o">|</span><span class="n">s</span><span class="o">|</span>
  <span class="k">if</span> <span class="n">s</span><span class="p">.</span><span class="nf">empty?</span>
    <span class="no">Left</span><span class="p">(</span><span class="s2">"unexpected end of input"</span><span class="p">)</span> <span class="c1"># because there is no 'x' at the beginning of the empty string</span>
  <span class="k">else</span>
    <span class="n">char</span> <span class="o">=</span> <span class="n">s</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
    <span class="n">char</span> <span class="o">==</span> <span class="s1">'x'</span> <span class="p">?</span> <span class="no">Right</span><span class="p">(</span><span class="no">Pair</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">s</span><span class="p">[</span><span class="mi">1</span><span class="o">..</span><span class="p">],</span> <span class="n">char</span><span class="p">))</span> <span class="p">:</span> <span class="no">Left</span><span class="p">(</span><span class="s2">"unexpected </span><span class="si">#{</span><span class="n">char</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="n">x_parser</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s2">""</span><span class="p">)</span> <span class="c1"># =&gt; Left("unexpected end of input")</span>
<span class="n">x_parser</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s2">"ab"</span><span class="p">)</span> <span class="c1"># =&gt; Left("unexpected a")</span>
<span class="n">x_parser</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s2">"xab"</span><span class="p">)</span> <span class="c1"># =&gt; Right("ab", "x")</span>
</code></pre></div></div>

<p>Let’s implement <code class="language-plaintext highlighter-rouge">Functor</code> for the parser. <code class="language-plaintext highlighter-rouge">fmap</code> accepts some function that can be applied to the result of the parser and constructs a new parser. Note that we do not parse anything, just build a new parser:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Parser</span>
  <span class="kp">include</span> <span class="no">Applicative</span><span class="o">::</span><span class="no">Functor</span>

  <span class="k">def</span> <span class="nf">fmap</span><span class="p">(</span><span class="o">&amp;</span><span class="n">fn</span><span class="p">)</span>
    <span class="no">Parser</span><span class="p">.</span><span class="nf">new</span> <span class="k">do</span> <span class="o">|</span><span class="n">s</span><span class="o">|</span>
      <span class="n">parse</span><span class="p">(</span><span class="n">s</span><span class="p">).</span><span class="nf">fmap</span> <span class="p">{</span> <span class="o">|</span><span class="n">pair</span><span class="o">|</span> <span class="n">pair</span><span class="p">.</span><span class="nf">fmap</span><span class="p">(</span><span class="o">&amp;</span><span class="n">fn</span><span class="p">)</span> <span class="p">}</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="n">x_parser</span><span class="p">.</span><span class="nf">fmap</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:upcase</span><span class="p">).</span><span class="nf">parse</span><span class="p">(</span><span class="s2">"xab"</span><span class="p">)</span> <span class="c1"># =&gt; Right("ab", "X")</span>
</code></pre></div></div>

<p>Let’s add a couple of helpers to build parsers easily:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Parser</span>
  <span class="k">class</span> <span class="o">&lt;&lt;</span> <span class="nb">self</span>
    <span class="k">def</span> <span class="nf">satisfy</span><span class="p">(</span><span class="o">&amp;</span><span class="n">predicate</span><span class="p">)</span>
      <span class="no">Parser</span><span class="p">.</span><span class="nf">new</span> <span class="k">do</span> <span class="o">|</span><span class="n">s</span><span class="o">|</span>
        <span class="k">if</span> <span class="n">s</span><span class="p">.</span><span class="nf">empty?</span>
          <span class="no">Left</span><span class="p">(</span><span class="s2">"unexpected end of input"</span><span class="p">)</span>
        <span class="k">else</span>
          <span class="n">char</span> <span class="o">=</span> <span class="n">s</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
          <span class="n">predicate</span><span class="o">.</span><span class="p">(</span><span class="n">char</span><span class="p">)</span> <span class="p">?</span> <span class="no">Right</span><span class="p">(</span><span class="no">Pair</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">s</span><span class="p">[</span><span class="mi">1</span><span class="o">..</span><span class="p">],</span> <span class="n">char</span><span class="p">))</span> <span class="p">:</span> <span class="no">Left</span><span class="p">(</span><span class="s2">"unexpected </span><span class="si">#{</span><span class="n">char</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
        <span class="k">end</span>
      <span class="k">end</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="nf">char</span><span class="p">(</span><span class="n">c</span><span class="p">)</span>
      <span class="n">satisfy</span> <span class="p">{</span> <span class="o">|</span><span class="n">current</span><span class="o">|</span> <span class="n">c</span> <span class="o">==</span> <span class="n">current</span> <span class="p">}</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="nf">string</span><span class="p">(</span><span class="n">str</span><span class="p">)</span>
      <span class="no">Parser</span><span class="p">.</span><span class="nf">new</span> <span class="k">do</span> <span class="o">|</span><span class="n">s</span><span class="o">|</span>
        <span class="k">if</span> <span class="n">s</span><span class="p">.</span><span class="nf">start_with?</span><span class="p">(</span><span class="n">str</span><span class="p">)</span>
          <span class="no">Right</span><span class="p">(</span><span class="no">Pair</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">s</span><span class="p">[</span><span class="n">str</span><span class="p">.</span><span class="nf">length</span><span class="o">..</span><span class="p">],</span> <span class="n">str</span><span class="p">))</span>
        <span class="k">else</span>
          <span class="no">Left</span><span class="p">(</span><span class="s2">"unexpected </span><span class="si">#{</span><span class="n">s</span><span class="si">}</span><span class="s2">, expected </span><span class="si">#{</span><span class="n">str</span><span class="si">}</span><span class="s2">"</span><span class="p">)</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></div>

<p>Now let’s make our parser <code class="language-plaintext highlighter-rouge">Applicative</code> to make it possible to combine parsers:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Parser</span>
  <span class="kp">include</span> <span class="no">Applicative</span><span class="o">::</span><span class="no">ApplicativeFunctor</span>

  <span class="k">class</span> <span class="o">&lt;&lt;</span> <span class="nb">self</span>
    <span class="k">def</span> <span class="nf">pure</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
      <span class="no">Parser</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="o">|</span><span class="n">s</span><span class="o">|</span> <span class="no">Right</span><span class="p">(</span><span class="no">Pair</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">value</span><span class="p">))</span> <span class="p">}</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">^</span><span class="p">(</span><span class="n">other</span><span class="p">)</span>
    <span class="no">Parser</span><span class="p">.</span><span class="nf">new</span> <span class="k">do</span> <span class="o">|</span><span class="n">s</span><span class="o">|</span>
      <span class="k">case</span> <span class="n">parse</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
      <span class="k">in</span> <span class="no">Applicative</span><span class="o">::</span><span class="no">Either</span><span class="o">::</span><span class="no">Right</span><span class="p">(</span><span class="no">Pair</span><span class="p">(</span><span class="n">s1</span><span class="p">,</span> <span class="n">g</span><span class="p">))</span>
        <span class="k">case</span> <span class="n">other</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">s1</span><span class="p">)</span>
        <span class="k">in</span> <span class="no">Applicative</span><span class="o">::</span><span class="no">Either</span><span class="o">::</span><span class="no">Right</span><span class="p">(</span><span class="no">Pair</span><span class="p">(</span><span class="n">s2</span><span class="p">,</span> <span class="n">a</span><span class="p">))</span> <span class="k">then</span> <span class="no">Right</span><span class="p">(</span><span class="no">Pair</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">s2</span><span class="p">,</span> <span class="n">g</span><span class="o">.</span><span class="p">(</span><span class="n">a</span><span class="p">)))</span>
        <span class="k">in</span> <span class="n">left</span> <span class="k">then</span> <span class="n">left</span>
        <span class="k">end</span>
      <span class="k">in</span> <span class="n">left</span> <span class="k">then</span> <span class="n">left</span>
      <span class="k">end</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">pure</code> creates a new parser that always “parses” any value we pass, returning the remaining string as is. Application creates a new parser using following steps:</p>

<ul>
  <li>if current parser cannot parse a string — we return <code class="language-plaintext highlighter-rouge">Left</code>;</li>
  <li>otherwise it returns us the remaining string <code class="language-plaintext highlighter-rouge">s1</code> and the result <code class="language-plaintext highlighter-rouge">a</code>, we tries to parse <code class="language-plaintext highlighter-rouge">s1</code> with the second parser:
    <ul>
      <li>if second parser fails — we return <code class="language-plaintext highlighter-rouge">Left</code>;</li>
      <li>otherwise second pair returns us a function <code class="language-plaintext highlighter-rouge">g</code> (because we’re in the <code class="language-plaintext highlighter-rouge">Applicative</code>), so we return <code class="language-plaintext highlighter-rouge">Right</code>:
        <ul>
          <li>the first element is a string that remained from the second parser;</li>
          <li>the second element is a result of calling <code class="language-plaintext highlighter-rouge">g</code> with <code class="language-plaintext highlighter-rouge">a</code>.</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<p>This is how it works:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">parser</span> <span class="o">=</span> <span class="p">(</span>
  <span class="no">Parser</span><span class="p">.</span><span class="nf">pure</span><span class="p">(</span><span class="nb">lambda</span> <span class="p">{</span> <span class="o">|</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="o">|</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span> <span class="p">}.</span><span class="nf">curry</span><span class="p">)</span> <span class="o">^</span>
    <span class="no">Parser</span><span class="p">.</span><span class="nf">char</span><span class="p">(</span><span class="s1">'A'</span><span class="p">)</span> <span class="o">^</span>
    <span class="no">Parser</span><span class="p">.</span><span class="nf">string</span><span class="p">(</span><span class="s2">"BC"</span><span class="p">)</span>
<span class="p">)</span>

<span class="n">parser</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s2">"ABC"</span><span class="p">)</span> <span class="c1"># =&gt; Right ("", "ABC")</span>
</code></pre></div></div>

<p>As you see, <code class="language-plaintext highlighter-rouge">^</code> operates as the <code class="language-plaintext highlighter-rouge">AND</code> operator: in this example we ask parser to find us character <code class="language-plaintext highlighter-rouge">A</code> followed by string <code class="language-plaintext highlighter-rouge">BC</code>. What about <code class="language-plaintext highlighter-rouge">OR</code>?</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">module</span> <span class="nn">Alternative</span>
  <span class="k">def</span> <span class="nf">|</span><span class="p">(</span><span class="n">other</span><span class="p">)</span> <span class="o">=</span> <span class="k">raise</span> <span class="no">NotImplementedError</span>
<span class="k">end</span>

<span class="k">class</span> <span class="nc">Parser</span>
  <span class="kp">include</span> <span class="no">Alternative</span>

  <span class="k">def</span> <span class="nf">|</span><span class="p">(</span><span class="n">other</span><span class="p">)</span>
    <span class="no">Parser</span><span class="p">.</span><span class="nf">new</span> <span class="k">do</span> <span class="o">|</span><span class="n">s</span><span class="o">|</span>
      <span class="k">case</span> <span class="n">parse</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
      <span class="k">in</span> <span class="no">Applicative</span><span class="o">::</span><span class="no">Either</span><span class="o">::</span><span class="no">Right</span><span class="p">(</span><span class="n">r</span><span class="p">)</span> <span class="k">then</span> <span class="no">Right</span><span class="p">(</span><span class="n">r</span><span class="p">)</span>
      <span class="k">in</span> <span class="n">_</span>
        <span class="k">case</span> <span class="n">other</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
        <span class="k">in</span> <span class="no">Applicative</span><span class="o">::</span><span class="no">Either</span><span class="o">::</span><span class="no">Right</span><span class="p">(</span><span class="n">r</span><span class="p">)</span> <span class="k">then</span> <span class="no">Right</span><span class="p">(</span><span class="n">r</span><span class="p">)</span>
        <span class="k">in</span> <span class="n">left</span> <span class="k">then</span> <span class="n">left</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></div>

<p>The new method <code class="language-plaintext highlighter-rouge">|</code> tries to parse with the first parser, if it fails — with the second, and returns the result.</p>

<p>Let’s combine everything together:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">parser</span> <span class="o">=</span> <span class="p">(</span>
  <span class="no">Parser</span><span class="p">.</span><span class="nf">pure</span><span class="p">(</span><span class="nb">lambda</span> <span class="p">{</span> <span class="o">|</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">c</span><span class="o">|</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span> <span class="o">+</span> <span class="n">c</span> <span class="p">}.</span><span class="nf">curry</span><span class="p">)</span> <span class="o">^</span>
    <span class="p">(</span><span class="no">Parser</span><span class="p">.</span><span class="nf">char</span><span class="p">(</span><span class="s1">'A'</span><span class="p">)</span> <span class="o">|</span> <span class="no">Parser</span><span class="p">.</span><span class="nf">char</span><span class="p">(</span><span class="s1">'B'</span><span class="p">))</span> <span class="o">^</span> <span class="c1"># we want A or B ...</span>
    <span class="no">Parser</span><span class="p">.</span><span class="nf">char</span><span class="p">(</span><span class="s1">'C'</span><span class="p">)</span> <span class="o">^</span> <span class="c1"># ... then C ...</span>
    <span class="no">Parser</span><span class="p">.</span><span class="nf">string</span><span class="p">(</span><span class="s2">"42"</span><span class="p">)</span> <span class="c1"># ... then 42</span>
<span class="p">).</span><span class="nf">fmap</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:downcase</span><span class="p">)</span> <span class="c1"># parser is functor, so `fmap` can be used to transform the result</span>

<span class="n">parser</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s2">"AC42D"</span><span class="p">)</span> <span class="c1"># =&gt; #&lt;Either::Right:… @value=#&lt;Pair:… @fst="D", @snd="ac"&gt;&gt;</span>
<span class="n">parser</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s2">"BC42D"</span><span class="p">)</span> <span class="c1"># =&gt; #&lt;Either::Right:… @value=#&lt;Pair:… @fst="D", @snd="bc"&gt;&gt;</span>
<span class="n">parser</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s2">"DCB"</span><span class="p">)</span> <span class="c1"># =&gt; #&lt;Either::Left:… @error="unexpected D"&gt;</span>
</code></pre></div></div>

<p>Impressive, right?</p>

<h3 id="traversable">Traversable</h3>

<p>Let’s try to build something <em>on top</em> of applicatives.</p>

<p><img src="/assets/traversable.png" alt="Applicative functor" /></p>

<p>Imagine that you have some applicative container <code class="language-plaintext highlighter-rouge">f</code> which has a function from <code class="language-plaintext highlighter-rouge">a</code> to <code class="language-plaintext highlighter-rouge">b</code> inside. For instance, this function accepts <code class="language-plaintext highlighter-rouge">Either Int</code> and tries to increment it returning <code class="language-plaintext highlighter-rouge">Either Int</code>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">increment</span> <span class="o">=</span> <span class="nb">lambda</span> <span class="p">{</span> <span class="o">|</span><span class="n">either_value</span><span class="o">|</span> <span class="n">either_value</span><span class="p">.</span><span class="nf">fmap</span> <span class="p">{</span> <span class="o">|</span><span class="n">value</span><span class="o">|</span> <span class="n">value</span> <span class="o">+</span> <span class="mi">1</span> <span class="p">}</span> <span class="p">}</span>

<span class="n">increment</span><span class="o">.</span><span class="p">(</span><span class="no">Right</span><span class="p">(</span><span class="mi">42</span><span class="p">))</span> <span class="c1"># =&gt; Right(43)</span>
<span class="n">increment</span><span class="o">.</span><span class="p">(</span><span class="no">Left</span><span class="p">(</span><span class="s2">"error"</span><span class="p">))</span> <span class="c1"># =&gt; Left("error")</span>
</code></pre></div></div>

<p>Then, we have a new module called <code class="language-plaintext highlighter-rouge">Traversable</code>. It has a single method called <code class="language-plaintext highlighter-rouge">traverse</code>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">module</span> <span class="nn">Traversable</span>
  <span class="k">def</span> <span class="nf">traverse</span><span class="p">(</span><span class="n">applicative_class</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">_fn</span><span class="p">)</span> <span class="o">=</span>
    <span class="k">raise</span> <span class="no">NotImplementedError</span>
<span class="k">end</span>
</code></pre></div></div>

<p>This function accepts some <code class="language-plaintext highlighter-rouge">applicative_class</code> (which includes <code class="language-plaintext highlighter-rouge">Applicative</code>) and a function we described above. The result of the call should be the instance of <em>the same</em> <code class="language-plaintext highlighter-rouge">Applicative</code> that contains the instance of the <em>the same</em> <code class="language-plaintext highlighter-rouge">Traversable</code>. <code class="language-plaintext highlighter-rouge">Traversable</code> should contain the value, that was returned by <code class="language-plaintext highlighter-rouge">fn</code> applied to the data inside the initial traversable.</p>

<p>That sounds smart, so let’s just write it and run. Here is the implementation for array:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">ApplicativeArray</span> <span class="o">&lt;</span> <span class="no">Array</span>
  <span class="kp">include</span> <span class="no">Traversable</span>

  <span class="k">def</span> <span class="nf">traverse</span><span class="p">(</span><span class="n">applicative_class</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">fn</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">applicative_class</span><span class="o">::</span><span class="n">pure</span><span class="p">([])</span> <span class="k">if</span> <span class="n">empty?</span>

    <span class="n">x</span><span class="p">,</span> <span class="o">*</span><span class="n">xs</span> <span class="o">=</span> <span class="nb">self</span>

    <span class="n">applicative_class</span><span class="o">::</span><span class="n">pure</span><span class="p">(</span><span class="nb">lambda</span> <span class="p">{</span> <span class="o">|</span><span class="n">ta</span><span class="p">,</span> <span class="n">rest</span><span class="o">|</span> <span class="p">[</span><span class="n">ta</span><span class="p">]</span> <span class="o">+</span> <span class="n">rest</span> <span class="p">})</span> <span class="o">^</span>
      <span class="n">fn</span><span class="o">.</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o">^</span>
      <span class="no">ApplicativeArray</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">xs</span><span class="p">).</span><span class="nf">traverse</span><span class="p">(</span><span class="n">applicative_class</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">fn</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>And the example of usage:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">increment</span> <span class="o">=</span> <span class="nb">lambda</span> <span class="p">{</span> <span class="o">|</span><span class="n">maybe_value</span><span class="o">|</span> <span class="n">maybe_value</span><span class="p">.</span><span class="nf">fmap</span> <span class="p">{</span> <span class="o">|</span><span class="n">value</span><span class="o">|</span> <span class="n">value</span> <span class="o">+</span> <span class="mi">1</span> <span class="p">}</span> <span class="p">}</span>

<span class="no">ApplicativeArray</span><span class="p">.</span><span class="nf">new</span><span class="p">([</span><span class="no">Right</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span> <span class="no">Right</span><span class="p">(</span><span class="mi">3</span><span class="p">),</span> <span class="no">Right</span><span class="p">(</span><span class="mi">5</span><span class="p">)])</span>
  <span class="p">.</span><span class="nf">traverse</span><span class="p">(</span><span class="no">Either</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">increment</span><span class="p">)</span> <span class="c1"># =&gt; #&lt;Either::Right:... @value=[2, 4, 6]&gt;</span>

<span class="no">ApplicativeArray</span><span class="p">.</span><span class="nf">new</span><span class="p">([</span><span class="no">Right</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span> <span class="no">Left</span><span class="p">(</span><span class="s2">"error"</span><span class="p">)])</span>
  <span class="p">.</span><span class="nf">traverse</span><span class="p">(</span><span class="no">Either</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">increment</span><span class="p">)</span> <span class="c1"># =&gt; #&lt;Either::Left:... @error=“error"&gt;</span>
</code></pre></div></div>

<p>Now we can describe the behavior in the easier way: <code class="language-plaintext highlighter-rouge">traverse</code> goes through some data structure containing values in containers, applies the function to the value in container and <em>extracts</em> container to the top. In our example, list of <code class="language-plaintext highlighter-rouge">Either Int</code> becomes <code class="language-plaintext highlighter-rouge">Either</code> list of <code class="language-plaintext highlighter-rouge">Int</code>:</p>

<ul>
  <li>if initial list has only <code class="language-plaintext highlighter-rouge">Right</code> then <code class="language-plaintext highlighter-rouge">Right</code> will be outside;</li>
  <li>if there are any lefts — the first one will be extracted.</li>
</ul>

<h2 id="outro">Outro</h2>

<p>In this post we implemented two different applicative functors for lists, created a simple (but yet powerful!) applicative parser and made our lists <em>traversable</em>. As you see, applicative functor is a powerful abstraction that helps to describe complex behavior and hide it inside the container.</p>

<p>Hopefully one day we will make something out of it!</p>]]></content><author><name></name></author><category term="ruby" /><category term="functional-programming" /><summary type="html"><![CDATA[Various applicative functor implementations in Ruby]]></summary></entry><entry><title type="html">Applicative programming in Ruby: railway reimagined</title><link href="https://dmitrytsepelev.dev/applicative-ruby-railway" rel="alternate" type="text/html" title="Applicative programming in Ruby: railway reimagined" /><published>2022-12-20T00:00:00+00:00</published><updated>2022-12-20T00:00:00+00:00</updated><id>https://dmitrytsepelev.dev/applicative-ruby-railway</id><content type="html" xml:base="https://dmitrytsepelev.dev/applicative-ruby-railway"><![CDATA[<p><strong>Part 1 | <a href="/applicative-ruby-advanced-behavior">Part 2</a></strong></p>

<p>In this post we will see how applicative programming can be used for implementing code in the Railway style using a gem <a href="https://github.com/DmitryTsepelev/applicative-rb">applicative-rb</a>.</p>

<hr />

<p>Railway programming is a common patten for scenarios when you have a sequence of steps, which are executed in a given order. When everything is good—you get a successful result, if one step fails—you don’t perform the remaining ones and return the failure. In other words—it’s a fancy way of error–handling.</p>

<p>Imagine a service that process an order:</p>

<ul>
  <li>we deduct money from user account;</li>
  <li>we check that the required item is in the stock;</li>
  <li>we update order status.</li>
</ul>

<p>This is how we can do it with a DSL similar to well–known <a href="https://github.com/dry-rb/dry-monads">dry-monads</a>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">ProcessOrder</span>
  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">order</span><span class="p">)</span> <span class="o">=</span> <span class="vi">@order</span> <span class="o">=</span> <span class="n">order</span>

  <span class="k">def</span> <span class="nf">perform</span>
    <span class="n">result</span> <span class="o">=</span> <span class="no">ApplicationRecord</span><span class="p">.</span><span class="nf">transaction</span> <span class="k">do</span> <span class="c1"># start the transaction</span>
      <span class="n">deduct_from_user_account</span><span class="p">.</span><span class="nf">bind</span> <span class="p">{</span>
        <span class="n">prepare_shipment</span><span class="p">.</span><span class="nf">bind</span> <span class="p">{</span> <span class="c1"># we get here only when previous step returned success</span>
          <span class="n">update_order_status</span>
        <span class="p">}</span>
      <span class="p">}.</span><span class="nf">tap</span> <span class="k">do</span> <span class="o">|</span><span class="n">result</span><span class="o">|</span>
        <span class="c1"># rollback in case of failure</span>
        <span class="k">raise</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Rollback</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">result</span><span class="p">.</span><span class="nf">failure</span><span class="p">)</span> <span class="k">if</span> <span class="n">result</span><span class="p">.</span><span class="nf">failure?</span>
      <span class="k">end</span>
    <span class="k">end</span>
  <span class="k">end</span>

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

  <span class="k">def</span> <span class="nf">deduct_from_user_account</span>
    <span class="k">if</span> <span class="vi">@order</span><span class="p">.</span><span class="nf">user</span><span class="p">.</span><span class="nf">balance</span> <span class="o">&gt;</span> <span class="vi">@order</span><span class="p">.</span><span class="nf">amount</span>
      <span class="vi">@order</span><span class="p">.</span><span class="nf">user</span><span class="p">.</span><span class="nf">deduct_amount</span><span class="p">(</span><span class="vi">@order</span><span class="p">.</span><span class="nf">amount</span><span class="p">)</span>
      <span class="c1"># we return success without explanation</span>
      <span class="no">Right</span><span class="p">()</span>
    <span class="k">else</span>
      <span class="c1"># we return failure with the error message inside</span>
      <span class="no">Left</span><span class="p">(</span><span class="s2">"cannot deduct </span><span class="si">#{</span><span class="vi">@order</span><span class="p">.</span><span class="nf">amount</span><span class="si">}</span><span class="s2">, user has </span><span class="si">#{</span><span class="vi">@order</span><span class="p">.</span><span class="nf">user</span><span class="p">.</span><span class="nf">balance</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">prepare_shipment</span>
    <span class="vi">@order</span><span class="p">.</span><span class="nf">item_id</span> <span class="o">==</span> <span class="mi">42</span> <span class="p">?</span> <span class="no">Right</span><span class="p">()</span> <span class="p">:</span> <span class="no">Left</span><span class="p">(</span><span class="s2">"not enough items in warehouse"</span><span class="p">)</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">update_order_status</span>
    <span class="vi">@order</span><span class="p">.</span><span class="nf">processed!</span>
    <span class="no">Right</span><span class="p">()</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>To make things work each <em>step</em> should return either success (<code class="language-plaintext highlighter-rouge">Right</code>) or failure (<code class="language-plaintext highlighter-rouge">Left</code>). The behavior itself depends on the container: the decision what to do is made inside the <code class="language-plaintext highlighter-rouge">#bind</code> method.</p>

<h2 id="container-either">Container Either</h2>

<p>Let’s step away from the service object for now and focus on the container. The container is called <a href="https://github.com/DmitryTsepelev/applicative-rb/blob/master/lib/applicative/either.rb">Either</a>, and it can be implemented in the following way:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Either</span>
  <span class="k">class</span> <span class="nc">Left</span> <span class="o">&lt;</span> <span class="no">Either</span>
    <span class="nb">attr_reader</span> <span class="ss">:error</span>

    <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">error</span><span class="p">)</span> <span class="o">=</span> <span class="vi">@error</span> <span class="o">=</span> <span class="n">error</span>
    <span class="k">def</span> <span class="nf">deconstruct</span> <span class="o">=</span> <span class="p">[</span><span class="vi">@error</span><span class="p">]</span>
  <span class="k">end</span>

  <span class="k">class</span> <span class="nc">Right</span> <span class="o">&lt;</span> <span class="no">Either</span>
    <span class="nb">attr_reader</span> <span class="ss">:value</span>

    <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="o">=</span> <span class="vi">@value</span> <span class="o">=</span> <span class="n">value</span>
    <span class="k">def</span> <span class="nf">deconstruct</span> <span class="o">=</span> <span class="p">[</span><span class="vi">@value</span><span class="p">]</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">Right</code> means a successful result and might hold some value, <code class="language-plaintext highlighter-rouge">Left</code> means some failure with the error message inside. As you see, it’s very similar to well–known <code class="language-plaintext highlighter-rouge">Maybe</code>, but can hold a value inside.</p>

<p>Let’s see it in action, <code class="language-plaintext highlighter-rouge">fetch_email</code> is a method to get the email by user ID:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">fetch_email</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>
  <span class="k">if</span> <span class="n">user_id</span> <span class="o">==</span> <span class="mi">42</span>
    <span class="no">Either</span><span class="o">::</span><span class="no">Right</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s2">"john.doe@example.com"</span><span class="p">)</span>
  <span class="k">else</span>
    <span class="no">Either</span><span class="o">::</span><span class="no">Left</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s2">"User </span><span class="si">#{</span><span class="n">user_id</span><span class="si">}</span><span class="s2"> not found"</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="k">def</span> <span class="nf">format_email</span><span class="p">(</span><span class="n">either_email</span><span class="p">)</span>
  <span class="k">case</span> <span class="n">either_email</span>
  <span class="k">in</span> <span class="no">Either</span><span class="o">::</span><span class="no">Right</span><span class="p">(</span><span class="n">email</span><span class="p">)</span> <span class="c1"># here we unpacked email string from the container</span>
    <span class="no">Either</span><span class="o">::</span><span class="no">Right</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s2">"Email: </span><span class="si">#{</span><span class="n">email</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="c1"># here we pack new string back to the container</span>
  <span class="k">in</span> <span class="n">left</span>
    <span class="n">left</span> <span class="c1"># return container as is</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="n">format_email</span><span class="p">(</span><span class="n">fetch_email</span><span class="p">(</span><span class="mi">42</span><span class="p">))</span> <span class="c1"># =&gt; #&lt;Either::Right:… @value="Email: john.doe@example.com"&gt;</span>
<span class="n">format_email</span><span class="p">(</span><span class="n">fetch_email</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span> <span class="c1"># =&gt; #&lt;Either::Left:… @error="User 1 not found"&gt;</span>
</code></pre></div></div>

<p>How we work with the value inside the container? You have to unpack it if possible (there’s no need to change the error value), work with value and pack back.</p>

<p>Looks like there will be a lot of code where we pack and unpack things! Can we avoid it?</p>

<h2 id="functors">Functors</h2>

<p>There is a nice abstraction for it called <em>functor</em>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">module</span> <span class="nn">Functor</span>
  <span class="c1"># (a -&gt; b) -&gt; f a -&gt; f b</span>
  <span class="k">def</span> <span class="nf">fmap</span><span class="p">(</span><span class="o">&amp;</span><span class="n">_fn</span><span class="p">)</span> <span class="o">=</span> <span class="k">raise</span> <span class="no">NotImplementedError</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Functor interface has one function, we will call it <code class="language-plaintext highlighter-rouge">fmap</code>, like it’s called in Haskell. Ruby does not have types, so we cannot add a signature to the code. Let’s use a Haskell notation, since it’s pretty readable <code class="language-plaintext highlighter-rouge">(a -&gt; b) -&gt; f a -&gt; f b</code>.</p>

<p><img src="/assets/functor.png" alt="Functor" /></p>

<p>In other words, it should pass the function that transforms a value of type <code class="language-plaintext highlighter-rouge">a</code> to a value of type <code class="language-plaintext highlighter-rouge">b</code>. <code class="language-plaintext highlighter-rouge">#fmap</code> will call this function on the data in the container, which has a type <code class="language-plaintext highlighter-rouge">f a</code> (<code class="language-plaintext highlighter-rouge">f</code> is “functor”). As a result, a value of type <code class="language-plaintext highlighter-rouge">f b</code> will be returned.</p>

<blockquote>
  <p>Read more about Functors in my <a href="https://dmitrytsepelev.dev/haskell-adventures-functors">Haskell post</a></p>
</blockquote>

<p>Here is the example implementation for Either:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Either</span>
  <span class="c1"># ...</span>

  <span class="kp">include</span> <span class="no">Functor</span>
  <span class="k">def</span> <span class="nf">fmap</span><span class="p">(</span><span class="o">&amp;</span><span class="n">fn</span><span class="p">)</span>
    <span class="k">case</span> <span class="nb">self</span>
    <span class="k">in</span> <span class="no">Functor</span><span class="o">::</span><span class="no">Right</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="k">then</span> <span class="no">Functor</span><span class="o">::</span><span class="no">Right</span><span class="p">(</span><span class="n">fn</span><span class="p">.</span><span class="nf">curry</span><span class="o">.</span><span class="p">(</span><span class="n">value</span><span class="p">))</span>
    <span class="k">in</span> <span class="n">left</span> <span class="k">then</span> <span class="n">left</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>With this function <code class="language-plaintext highlighter-rouge">format_email</code> can be simplified:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">fetch_email</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>
  <span class="k">if</span> <span class="n">user_id</span> <span class="o">==</span> <span class="mi">42</span>
    <span class="no">Either</span><span class="o">::</span><span class="no">Right</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s2">"john.doe@example.com"</span><span class="p">)</span>
  <span class="k">else</span>
    <span class="no">Either</span><span class="o">::</span><span class="no">Left</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s2">"User </span><span class="si">#{</span><span class="n">user_id</span><span class="si">}</span><span class="s2"> not found"</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="k">def</span> <span class="nf">format_email</span><span class="p">(</span><span class="n">either_email</span><span class="p">)</span> <span class="o">=</span> <span class="n">either_email</span><span class="p">.</span><span class="nf">fmap</span> <span class="p">{</span> <span class="o">|</span><span class="n">email</span><span class="o">|</span> <span class="s2">"Email: </span><span class="si">#{</span><span class="n">email</span><span class="si">}</span><span class="s2">"</span> <span class="p">}</span>
</code></pre></div></div>

<p>Proper functor should follow some rules:</p>

<ol>
  <li>If <code class="language-plaintext highlighter-rouge">identity</code> (<code class="language-plaintext highlighter-rouge">def identity(value) = value</code>) is passed, than <code class="language-plaintext highlighter-rouge">fmap</code> should return value without changes;</li>
  <li><code class="language-plaintext highlighter-rouge">fmap (f . g) == fmap f . fmap g</code>, where <code class="language-plaintext highlighter-rouge">.</code> is a <a href="https://en.wikipedia.org/wiki/Function_composition_(computer_science)">composition</a> of functions.</li>
</ol>

<h2 id="applicative-functors">Applicative functors</h2>

<p>This approach works great for functions with only one argument. But what if we two arguments or more? Thanks to <a href="https://www.rubydoc.info/stdlib/core/Method:curry">curry</a>, functions can take less arguments and return another function:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">sum</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span>

<span class="no">Either</span><span class="o">::</span><span class="no">Right</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">42</span><span class="p">).</span><span class="nf">fmap</span><span class="p">(</span><span class="o">&amp;</span><span class="nb">method</span><span class="p">(</span><span class="ss">:sum</span><span class="p">))</span>
<span class="c1"># =&gt; #&lt;Either::Right:... @value=#&lt;Proc:... (lambda)&gt;&gt;</span>
</code></pre></div></div>

<p>We can call this function with another argument and get the result:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Either</span><span class="o">::</span><span class="no">Right</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">42</span><span class="p">).</span><span class="nf">fmap</span><span class="p">(</span><span class="o">&amp;</span><span class="nb">method</span><span class="p">(</span><span class="ss">:sum</span><span class="p">)).</span><span class="nf">value</span><span class="o">.</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="c1"># =&gt; 43</span>
</code></pre></div></div>

<p>How to make it more readable?</p>

<p>Applicative functor interface is the extension of <code class="language-plaintext highlighter-rouge">Functor</code> and contains two more methods:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">pure</code> that returns <em>most simple</em> container with value;</li>
  <li><code class="language-plaintext highlighter-rouge">^</code> takes the function from the first container and applies it to the value stored in the second container.</li>
</ul>

<p>Applicative functors also have some laws, but they are a bit more complex so we will not discuss them here. Let’s assume that all the implementations in the post are valid.</p>

<p>This is how interface looks like:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">module</span> <span class="nn">Applicative</span>
  <span class="kp">include</span> <span class="no">Functor</span> <span class="c1"># applicative functor should have same methods as functor</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">klass</span><span class="p">)</span>
    <span class="c1"># a -&gt; f a</span>
    <span class="n">klass</span><span class="p">.</span><span class="nf">extend</span><span class="p">(</span><span class="no">Module</span><span class="p">.</span><span class="nf">new</span> <span class="k">do</span>
      <span class="k">def</span> <span class="nf">pure</span><span class="p">(</span><span class="n">_value</span><span class="p">)</span> <span class="o">=</span> <span class="k">raise</span> <span class="no">NotImplementedError</span>
    <span class="k">end</span><span class="p">)</span>
  <span class="k">end</span>

  <span class="c1"># a -&gt; f a</span>
  <span class="k">def</span> <span class="nf">pure</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="o">=</span> <span class="nb">self</span><span class="p">.</span><span class="nf">class</span><span class="p">.</span><span class="nf">pure</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>

  <span class="c1"># f (a -&gt; b) -&gt; f a -&gt; f b</span>
  <span class="k">def</span> <span class="nf">^</span><span class="p">(</span><span class="n">_other</span><span class="p">)</span> <span class="o">=</span> <span class="k">raise</span> <span class="no">NotImplementedError</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Let’s try to use it for <code class="language-plaintext highlighter-rouge">Either</code>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Either</span>
  <span class="c1"># ...</span>
  <span class="kp">include</span> <span class="no">Applicative</span>

  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">pure</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="o">=</span> <span class="no">Right</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>

  <span class="k">def</span> <span class="nf">^</span><span class="p">(</span><span class="n">other</span><span class="p">)</span>
    <span class="k">case</span> <span class="nb">self</span>
    <span class="k">in</span> <span class="no">Right</span><span class="p">(</span><span class="n">fn</span><span class="p">)</span> <span class="k">then</span> <span class="n">other</span><span class="p">.</span><span class="nf">fmap</span><span class="p">(</span><span class="o">&amp;</span><span class="n">fn</span><span class="p">)</span>
    <span class="k">in</span> <span class="n">left</span> <span class="k">then</span> <span class="n">left</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Note that things will happen only if both containers are <code class="language-plaintext highlighter-rouge">Right</code>, otherwise it will just keep the error.</p>

<p>Let’s rewrite <code class="language-plaintext highlighter-rouge">format_email</code> one more time:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">format_email</span><span class="p">(</span><span class="n">either_email</span><span class="p">)</span>
  <span class="n">add_label</span> <span class="o">=</span> <span class="nb">lambda</span> <span class="p">{</span> <span class="o">|</span><span class="n">label</span><span class="p">,</span> <span class="n">email</span><span class="o">|</span> <span class="s2">"</span><span class="si">#{</span><span class="n">label</span><span class="si">}</span><span class="s2">: </span><span class="si">#{</span><span class="n">email</span><span class="si">}</span><span class="s2">"</span> <span class="p">}</span>
  <span class="no">Either</span><span class="p">.</span><span class="nf">pure</span><span class="p">(</span><span class="n">add_label</span><span class="p">)</span> <span class="o">^</span> <span class="no">Either</span><span class="o">::</span><span class="no">Right</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s2">"Email"</span><span class="p">)</span> <span class="o">^</span> <span class="n">either_email</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Why is it useful? It makes <code class="language-plaintext highlighter-rouge">curry</code> “safe”, because we can describe a “golden path”. Error will be propagated because of the container <em>semantics</em>: we agreed that <code class="language-plaintext highlighter-rouge">Left</code> should be kept as is without changes. If you have some steps connected with <code class="language-plaintext highlighter-rouge">^</code> and one of them returns <code class="language-plaintext highlighter-rouge">Left</code>, all steps to the right won’t even happen and the <code class="language-plaintext highlighter-rouge">Left</code> will be returned.</p>

<blockquote>
  <p>If it’s stilly blurry—check out this <a href="https://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html">post</a> with pictures</p>
</blockquote>

<p>However, there is a small downside: each argument for the function we want to curry safely (<code class="language-plaintext highlighter-rouge">Either.pure(add_label)</code>) should be calculated independently, because these calculations cannot see each other. This is what monads were invented for.</p>

<h2 id="monads">Monads</h2>

<p>We discussed monads briefly in the beginning, and I could not stop myself from implementing monads from scratch. We are not going to dig dip into the theory, and jump right to the practice.</p>

<p>Unlike applicative functors, monads can access the data form the previous steps. In order to make something monad you need to implement only two methods <code class="language-plaintext highlighter-rouge">return</code> and <code class="language-plaintext highlighter-rouge">bind</code>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">module</span> <span class="nn">Monad</span>
  <span class="kp">include</span> <span class="no">Applicative</span> <span class="c1"># monad should have same methods as applicative functor</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">klass</span><span class="p">)</span>
    <span class="n">klass</span><span class="p">.</span><span class="nf">extend</span><span class="p">(</span><span class="no">Module</span><span class="p">.</span><span class="nf">new</span> <span class="k">do</span>
      <span class="c1"># a -&gt; m a</span>
      <span class="k">def</span> <span class="nf">returnM</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="o">=</span> <span class="n">pure</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
    <span class="k">end</span><span class="p">)</span>
  <span class="k">end</span>

  <span class="c1"># m a -&gt; (a -&gt; m b) -&gt; m b</span>
  <span class="k">def</span> <span class="nf">bind</span><span class="p">(</span><span class="o">&amp;</span><span class="n">fn</span><span class="p">)</span> <span class="o">=</span> <span class="k">raise</span> <span class="no">NotImplementedError</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Check out the source <a href="https://github.com/DmitryTsepelev/applicative-rb/blob/master/lib/applicative/monad.rb">here</a>. As you see, <code class="language-plaintext highlighter-rouge">returnM</code> does the same thing as we did in <code class="language-plaintext highlighter-rouge">Applicative#pure</code>, while <code class="language-plaintext highlighter-rouge">bind</code> is more interesting: it accepts the block, calls it with the current value in the container (if it makes sense, as usual), and returns the result.</p>

<blockquote>
  <p>We will go with <code class="language-plaintext highlighter-rouge">returnM</code> cause <code class="language-plaintext highlighter-rouge">return</code> is a reserved word in Ruby</p>
</blockquote>

<p>Compare signatures of <code class="language-plaintext highlighter-rouge">Applicative#^</code> and <code class="language-plaintext highlighter-rouge">Monad#bind</code>:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Applicative#^</code>: <code class="language-plaintext highlighter-rouge">f (a -&gt; b) -&gt; f a -&gt; f b</code></li>
  <li><code class="language-plaintext highlighter-rouge">Monad#bind</code>: <code class="language-plaintext highlighter-rouge">m a -&gt; (a -&gt; m b) -&gt; m b</code>.</li>
</ul>

<p>Note the difference: in the <code class="language-plaintext highlighter-rouge">Applicative#^</code> we applied a function inside the container to the value inside the container, while in <code class="language-plaintext highlighter-rouge">Monad</code> we pass the function to transform the unpacked value of type <code class="language-plaintext highlighter-rouge">a</code> to the value of type <code class="language-plaintext highlighter-rouge">b</code> packed to the container <code class="language-plaintext highlighter-rouge">m</code>.</p>

<p>Let’s see how to do it for <code class="language-plaintext highlighter-rouge">Either</code>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Either</span>
  <span class="kp">include</span> <span class="no">Monad</span>

  <span class="k">def</span> <span class="nf">bind</span><span class="p">(</span><span class="o">&amp;</span><span class="n">fn</span><span class="p">)</span>
    <span class="k">case</span> <span class="nb">self</span>
    <span class="k">in</span> <span class="no">Right</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="k">then</span> <span class="n">fn</span><span class="o">.</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
    <span class="k">in</span> <span class="n">left</span> <span class="k">then</span> <span class="n">left</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Let’s change <code class="language-plaintext highlighter-rouge">fetch_email</code>: now it can also return the invalid email, so we need to validate it before formatting:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">fetch_email</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>
  <span class="k">case</span> <span class="n">user_id</span>
  <span class="k">when</span> <span class="mi">42</span> <span class="k">then</span> <span class="no">Right</span><span class="p">(</span><span class="s2">"john.doe@example.com"</span><span class="p">)</span>
  <span class="k">when</span> <span class="mi">666</span> <span class="k">then</span> <span class="no">Right</span><span class="p">(</span><span class="s2">"invalid"</span><span class="p">)</span>
  <span class="k">else</span> <span class="no">Left</span><span class="p">(</span><span class="s2">"User </span><span class="si">#{</span><span class="n">user_id</span><span class="si">}</span><span class="s2"> not found"</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="k">def</span> <span class="nf">validate</span><span class="p">(</span><span class="n">email</span><span class="p">)</span>
  <span class="n">email</span><span class="p">.</span><span class="nf">include?</span><span class="p">(</span><span class="s2">"@"</span><span class="p">)</span> <span class="p">?</span> <span class="no">Either</span><span class="o">::</span><span class="n">returnM</span><span class="p">(</span><span class="n">email</span><span class="p">)</span> <span class="p">:</span> <span class="no">Left</span><span class="p">(</span><span class="s2">"invalid email"</span><span class="p">)</span>
<span class="k">end</span>

<span class="k">def</span> <span class="nf">format_email</span><span class="p">(</span><span class="n">email</span><span class="p">)</span> <span class="o">=</span> <span class="no">Right</span><span class="p">(</span><span class="s2">"Email: </span><span class="si">#{</span><span class="n">email</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
</code></pre></div></div>

<p>We can write a function that fetches and validates the email using the monad interface of <code class="language-plaintext highlighter-rouge">Either</code>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">fetch_validate_and_format</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>
  <span class="n">fetch_email</span><span class="p">(</span><span class="n">user_id</span><span class="p">).</span><span class="nf">bind</span> <span class="p">{</span> <span class="o">|</span><span class="n">email</span><span class="o">|</span>
    <span class="c1"># we get here only if `fetch_email` returned Right,</span>
    <span class="c1"># but email is a String, not Either!</span>
    <span class="n">validate</span><span class="p">(</span><span class="n">email</span><span class="p">).</span><span class="nf">bind</span> <span class="p">{</span> <span class="o">|</span><span class="n">validated_email</span><span class="o">|</span>
      <span class="n">format_email</span><span class="p">(</span><span class="n">validated_email</span><span class="p">)</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="k">end</span>

<span class="n">fetch_validate_and_format</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span> <span class="c1"># =&gt; #&lt;Either::Right:… @value="Email: john.doe@example.com"&gt;</span>
<span class="n">fetch_validate_and_format</span><span class="p">(</span><span class="mi">666</span><span class="p">)</span> <span class="c1"># =&gt; #&lt;Either::Left:… @error="invalid email"&gt;</span>
<span class="n">fetch_validate_and_format</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="c1"># =&gt; #&lt;Either::Left:… @error="User 1 not found"&gt;</span>
</code></pre></div></div>

<p>Monads are kind of extension for Applicatives, and give us more features. Can we use them always? Not really, because it’s possible to create more applicatives than monads (check out this <a href="https://www.staff.city.ac.uk/~ross/papers/Applicative.pdf">article</a> to learn more on that).</p>

<h2 id="service-object-in-applicative-style">Service object in applicative style</h2>

<p>Let’s get back to the service objects. This is what we had in the beginning of the post:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">ProcessOrder</span>
  <span class="kp">include</span> <span class="no">Dry</span><span class="o">::</span><span class="no">Monads</span><span class="p">[</span><span class="ss">:result</span><span class="p">]</span>

  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">order</span><span class="p">)</span> <span class="o">=</span> <span class="vi">@order</span> <span class="o">=</span> <span class="n">order</span>

  <span class="k">def</span> <span class="nf">perform</span>
    <span class="no">ApplicationRecord</span><span class="p">.</span><span class="nf">transaction</span> <span class="k">do</span>
      <span class="n">deduct_from_user_account</span><span class="p">.</span><span class="nf">bind</span> <span class="p">{</span>
        <span class="n">prepare_shipment</span><span class="p">.</span><span class="nf">bind</span> <span class="p">{</span>
          <span class="n">update_order_status</span>
        <span class="p">}</span>
      <span class="p">}.</span><span class="nf">tap</span> <span class="p">{</span> <span class="o">|</span><span class="n">result</span><span class="o">|</span>
        <span class="k">raise</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Rollback</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">result</span><span class="p">.</span><span class="nf">failure</span><span class="p">)</span> <span class="k">if</span> <span class="n">result</span><span class="p">.</span><span class="nf">failure?</span>
      <span class="p">}</span>
    <span class="k">end</span>
  <span class="k">end</span>

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

  <span class="k">def</span> <span class="nf">deduct_from_user_account</span>
    <span class="c1"># ...</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">prepare_shipment</span>
    <span class="c1"># ...</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">update_order_status</span>
    <span class="c1"># ...</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Note that all three actions are completely independent, which makes it the ideal candidate for the applicative approach! Let’s update the service object itself:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">ProcessOrder</span> <span class="o">&lt;</span> <span class="no">MultiStepService</span>
  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">order</span><span class="p">)</span> <span class="o">=</span> <span class="vi">@order</span> <span class="o">=</span> <span class="n">order</span>

  <span class="n">add_step</span> <span class="ss">:deduct_from_user_account</span>
  <span class="n">add_step</span> <span class="ss">:prepare_shipment</span>
  <span class="n">add_step</span> <span class="ss">:update_order_status</span>

  <span class="k">def</span> <span class="nf">deduct_from_user_account</span>
    <span class="c1"># ...</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">prepare_shipment</span>
    <span class="c1"># ...</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">update_order_status</span>
    <span class="c1"># ...</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Now we need to add the base class:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">identity</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="o">=</span> <span class="n">value</span> <span class="c1"># the most helpful function ever</span>
<span class="k">def</span> <span class="nf">Right</span><span class="p">(</span><span class="n">value</span> <span class="o">=</span> <span class="nb">method</span><span class="p">(</span><span class="ss">:identity</span><span class="p">))</span> <span class="o">=</span> <span class="no">Either</span><span class="o">::</span><span class="no">Right</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="c1"># but we need it to make application work</span>
<span class="k">def</span> <span class="nf">Left</span><span class="p">(</span><span class="n">error</span> <span class="o">=</span> <span class="nb">method</span><span class="p">(</span><span class="ss">:identity</span><span class="p">))</span> <span class="o">=</span> <span class="no">Either</span><span class="o">::</span><span class="no">Left</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">error</span><span class="p">)</span>

<span class="k">class</span> <span class="nc">MultiStepService</span>
  <span class="k">class</span> <span class="o">&lt;&lt;</span> <span class="nb">self</span>
    <span class="k">def</span> <span class="nf">add_step</span><span class="p">(</span><span class="n">step</span><span class="p">)</span> <span class="o">=</span> <span class="n">steps</span> <span class="o">&lt;&lt;</span> <span class="n">step</span>

    <span class="k">def</span> <span class="nf">steps</span> <span class="o">=</span> <span class="vi">@steps</span> <span class="o">||=</span> <span class="p">[]</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">perform</span>
    <span class="no">ApplicationRecord</span><span class="p">.</span><span class="nf">transaction</span> <span class="k">do</span>
      <span class="nb">self</span><span class="p">.</span><span class="nf">class</span><span class="p">.</span><span class="nf">steps</span>
        <span class="p">.</span><span class="nf">reduce</span><span class="p">(</span><span class="no">Right</span><span class="p">())</span> <span class="p">{</span> <span class="o">|</span><span class="n">result</span><span class="p">,</span> <span class="n">step</span><span class="o">|</span> <span class="n">result</span> <span class="o">^</span> <span class="nb">send</span><span class="p">(</span><span class="n">step</span><span class="p">)</span> <span class="p">}</span>
        <span class="p">.</span><span class="nf">on_error</span> <span class="p">{</span> <span class="o">|</span><span class="n">error</span><span class="o">|</span> <span class="k">raise</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Rollback</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">error</span><span class="p">)</span> <span class="p">}</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">#add_step</code> collects a list of methods to be called. <code class="language-plaintext highlighter-rouge">#perform</code> reduces steps using the <code class="language-plaintext highlighter-rouge">Right()</code> as the initial value. <code class="language-plaintext highlighter-rouge">Right()</code> holds the function that just returns a value passed to it (<code class="language-plaintext highlighter-rouge">identity</code>), which makes the application work: <code class="language-plaintext highlighter-rouge">^</code> will run steps until we execute them all or get the first error.</p>

<p>You can find the full example <a href="https://github.com/DmitryTsepelev/applicative-rb/blob/master/examples/service.rb">here</a>.</p>

<h2 id="so-the-only-use-of-applicatives-is-error-handling">So the only use of applicatives is error handling?</h2>

<p>Nope! In this post we explored a single container called <code class="language-plaintext highlighter-rouge">Either</code> (and <code class="language-plaintext highlighter-rouge">Maybe</code>, because it’s almost the same thing), but there are many more data structures that can implement <code class="language-plaintext highlighter-rouge">Functor</code> and <code class="language-plaintext highlighter-rouge">Applicative</code> interfaces! Also, the implementation of the applicative functor that follows the laws can be either used as the argument of other functions or have some extensions (like <code class="language-plaintext highlighter-rouge">Monad</code>). These way of combining functions can lead us to more complex and interesting calculations.</p>

<h2 id="outro">Outro</h2>

<p>Railway programming gives us a clearer way to handle errors: we describe a golden path and errors are handled by the external code in the predictable way. It is usually implemented using <code class="language-plaintext highlighter-rouge">Either</code>/<code class="language-plaintext highlighter-rouge">Maybe</code> monads, but we saw how to replace them with applicative functors in cases when steps are completely independent.</p>

<p>There is a common mistake to think that monads and applicative functors can be used only for that. In the next post we will see how different data structures can implement the <code class="language-plaintext highlighter-rouge">Applicative</code> interface and what interesting behavior they can provide to us.</p>]]></content><author><name></name></author><category term="ruby" /><category term="functional-programming" /><summary type="html"><![CDATA[How to write Railway–style code in Ruby with the Applicative functors]]></summary></entry></feed>