<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Augusts Bautra</title>
    <description>The latest articles on DEV Community by Augusts Bautra (@epigene).</description>
    <link>https://dev.to/epigene</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4291%2F4e7fca4b-6bed-45e7-9c2b-758af8f279cb.jpg</url>
      <title>DEV Community: Augusts Bautra</title>
      <link>https://dev.to/epigene</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/epigene"/>
    <language>en</language>
    <item>
      <title>Thoughts on Codingame 2026 Spring challenge</title>
      <dc:creator>Augusts Bautra</dc:creator>
      <pubDate>Mon, 25 May 2026 09:24:06 +0000</pubDate>
      <link>https://dev.to/epigene/thoughts-on-codingame-2026-spring-challenge-5891</link>
      <guid>https://dev.to/epigene/thoughts-on-codingame-2026-spring-challenge-5891</guid>
      <description>&lt;p&gt;Thoughts on Codingame 2026 Spring challenge (Trolls in woods)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg43yu5fbl2cx4n4spkcb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg43yu5fbl2cx4n4spkcb.png" alt=" " width="799" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I liked the challenge a lot, the most enjoyable experience so far! I even reached legend league, a first or me, placing 60th/2k (dangit, lost best Rubyist spot at the last minute).&lt;br&gt;
Kudos to developers for interesting mechanics and nice graphics. Of particular note was the good mix of static VS dynamic elements in the game world - the grid never changes, which is a relief! And various QoL features such as turn time exceeding allowance, input data copying etc.&lt;/p&gt;

&lt;p&gt;Some citicisms:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;As always, the game docs could have been more detailed, for example, what are the conditions for early termination of the game (no trees, and no score changes for a number of turns?), and how exactly is wood distributed if both players chop and workers have the same chop power, who gets a wood first?&lt;/li&gt;
&lt;li&gt;It took too long to get new arena score, iterating in legend league was impossible for me, almost every change I tried in the final weekend resulted in a reduced score and it took 15mins to resolve. I blame this on excessive game length, maybe 250, 200 turns would have been sufficient.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I had good results with a mix of aggressive and passively scaling strategies - if I get ahead, I seek to hobble opponent's lemon gathering, preventing them from reaching my potential, and winning in the long run due to better points/turn; all the while being OK to invest several turns into growing trees and gathering significantly more resources if it's likely to pay off in the long run. I think the best scoring players leaned even harder into this, training not just one or two, but three and four workers, each even better.&lt;/p&gt;

&lt;p&gt;My biggest struggle, as always, was the multi-agent aspect of this challenge. Preventing the workers from getting in each other's way was a problem I struggled throughout.&lt;/p&gt;

&lt;p&gt;My bot is mere 2k lines, you have check it out &lt;a href="https://github.com/Epigene/codingame_26_spring/blob/main/lib/controller.rb" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Plans as 1st class concepts
&lt;/h2&gt;

&lt;p&gt;Using plain &lt;code&gt;commands&lt;/code&gt; array and then &lt;code&gt;puts commands.join("; ")&lt;/code&gt; may work for a while, but it becomes unworkable once multiple agents are in play and you need introspection like "is this agent doing anything this turn?" or "is any other agent already going here?".&lt;/p&gt;

&lt;p&gt;Do yourself a favour and introduce a Plan concept like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Plan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Struct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:worker_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:weight&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Furthermore, distinguish "ultimate goal" from "command to result for this turn". This can help further improve plan collision (there's an interesting 2x2 possibility matrix here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;ultimate goals and turn command match, very bad&lt;/li&gt;
&lt;li&gt;ultimate goals match, but turn commands differ (workers on different squares, no surprise), still bad.&lt;/li&gt;
&lt;li&gt;ultimate goals differ, but turn commands match, uhg, gotta look for pathing alternatives or somesuch&lt;/li&gt;
&lt;li&gt;ultimate goal and commands differ, yay
)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Timing tracking
&lt;/h2&gt;

&lt;p&gt;Save turn start time at the very beginning of turn.&lt;br&gt;
Be able to tell how long you've taken so far. This allows early-termination in case of deep prediction or, inversely, using leftover time in simpler turns to do some pre-crunching for next turns.&lt;/p&gt;

&lt;p&gt;In this challenge I used this technique to lazily pre-fill pathing to trees, since their positions only become available on 1st turn.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# @return Numeric # in ms&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;turn_time_taken&lt;/span&gt;
  &lt;span class="n"&gt;t1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clock_gettime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CLOCK_MONOTONIC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;elapsed_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;t0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1000.0&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# using a value somewhat lower than 50ms stated in rules for safety&lt;/span&gt;
&lt;span class="c1"&gt;# @return Numeric # in ms&lt;/span&gt;
&lt;span class="no"&gt;TURN_TIME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;turn_time_remaining&lt;/span&gt;
  &lt;span class="mi"&gt;45&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;turn_time_taken&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  State VS de-novo
&lt;/h2&gt;

&lt;p&gt;Say I see an opponent does something the bot should take note of and remember, to then use in later turns. This complexes spec setup and testing, necessitates an easy way to feed the internal state in initializer, which can get cumbersome.&lt;/p&gt;

&lt;p&gt;Strongly prefer deciding on best move solely based on info given that turn, but sometimes memory is really helpful, YMMV.&lt;/p&gt;

&lt;h2&gt;
  
  
  Override #inspect
&lt;/h2&gt;

&lt;p&gt;Does your main object vomit out hundreds of lines of irrelevant ivar data when inspected in console? Override its &lt;code&gt;def inspect&lt;/code&gt; for what you need!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;inspect&lt;/span&gt;
  &lt;span class="n"&gt;ivars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;instance_variables&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:@row&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# or whatever you want to hide&lt;/span&gt;

  &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ivars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ivar&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;ivar&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;=&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;instance_variable_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ivar&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;", "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="s2"&gt;"#&amp;lt;&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Rails systems you can try the more targeted override for &lt;code&gt;#pretty_print_instance_variables&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pretty_print_instance_variables&lt;/span&gt;
  &lt;span class="nb"&gt;instance_variables&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:@file&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Subpath memoization
&lt;/h2&gt;

&lt;p&gt;This is big. Say you run your shortest path algo and it gives you a four-node long path [1, 2, 3, 4]. What you've actually gotten is all shortest subpaths - [1, 2], [2, 3], [3, 4], [1, 2, 3], and [2, 3, 4]. Memoize these alongside the main path!&lt;/p&gt;

&lt;h2&gt;
  
  
  Short-circuiting sorting
&lt;/h2&gt;

&lt;p&gt;Consider sorting optimization that uses short-circuiting - if there is just one element in a collection, sorting it can be skipped altogether. This is useful if the sorting block does expensive things like pathing calculations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;quick_max_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;one?&lt;/span&gt;
    &lt;span class="n"&gt;first&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="n"&gt;max_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>ai</category>
      <category>algorithms</category>
      <category>gamedev</category>
      <category>ruby</category>
    </item>
    <item>
      <title>TIL: Lexical Fractional Indexing</title>
      <dc:creator>Augusts Bautra</dc:creator>
      <pubDate>Tue, 28 Apr 2026 13:38:00 +0000</pubDate>
      <link>https://dev.to/epigene/til-lexical-fractional-indexing-58j9</link>
      <guid>https://dev.to/epigene/til-lexical-fractional-indexing-58j9</guid>
      <description>&lt;p&gt;Today I got a task with one of those things™ again - allow users to order items in a list as they please with drag-and-drop UI.&lt;/p&gt;

&lt;p&gt;I'd usually reach for integer &lt;code&gt;:seq&lt;/code&gt; column and do a range update, it's performant enough for our needs and I don't need to worry about seq value drift, but today GPT convinced me that there might be a better approach - Fractional Indexing, specifically, it's Lexical variant where order values are strings, not integers (larger value-set, yay).&lt;/p&gt;

&lt;p&gt;See &lt;a href="https://github.com/sqliteai/fractional-indexing" rel="noopener noreferrer"&gt;https://github.com/sqliteai/fractional-indexing&lt;/a&gt; for reference.&lt;/p&gt;

&lt;p&gt;The beauty of this approach is that it's &lt;code&gt;O(1)&lt;/code&gt; - you only update the one item that changed place!&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;You define an alphabet, usually the ASCII-friendly base62 &lt;code&gt;0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz&lt;/code&gt; and when drag-and-dropping an item in a new position you provide :seq of both neighbours. Out of this we get three cases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Placed first, there is only right neighbor. Assume left to be 0 or 1 as needed.&lt;/li&gt;
&lt;li&gt;Placed in the middle, both neighbors present&lt;/li&gt;
&lt;li&gt;Placed last, only left neighbor present. Simply increment left neighbor's :seq by half of alphavet size (31)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In all cases the algo is similar. Convert the neighbor seq lexical values to integers and find the middle value between them. Convert this middle value back to lexical expression and set that as the new :seq of the record. Done.&lt;/p&gt;

&lt;p&gt;Over time the :seq values will grow in length to cope with no values being available in gaps, but in practice values should not exceed 10 letters and getting this would require hundreds-to-thousands of tactical worst-case gap repositions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation tips
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;I recommend setting a value regex constraint on the column if you're using Postgres, so expected alphabet is enforced.&lt;/li&gt;
&lt;li&gt;Start sequences with at least "1" because if first record is of sequence "0", nothing can ever be put before it without updating it also.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>algorithms</category>
      <category>backend</category>
      <category>database</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Share your cancer code!</title>
      <dc:creator>Augusts Bautra</dc:creator>
      <pubDate>Tue, 24 Mar 2026 13:31:54 +0000</pubDate>
      <link>https://dev.to/epigene/share-your-cancer-code-2afg</link>
      <guid>https://dev.to/epigene/share-your-cancer-code-2afg</guid>
      <description>&lt;p&gt;Today I was doing a read-through of existing code to prepare for an upcoming feature, and I came across this abomination.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# controller code&lt;/span&gt;
&lt;span class="c1"&gt;# GET /project_team_tasks&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
  &lt;span class="vi"&gt;@teams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;teams&lt;/span&gt; 
  &lt;span class="no"&gt;TeamTask&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define_teams_quantity_distributions_attr_readers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@teams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# model code&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define_teams_quantity_distributions_attr_readers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;teams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;teams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;team&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="no"&gt;Budget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;component_groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;component_group&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;define_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"team_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;team&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_assembled_elements_quantity_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;component_group&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;teams_quantity_distributions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"team_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;team&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_assembled_elements_quantity_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;component_group&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# then in view&lt;/span&gt;
&lt;span class="n"&gt;team&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"team_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;team&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_assembled_elements_quantity_1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As far as I understand, calling the class method defines an instance method (a getter) for every team*component_group pair. Apparently the underlying data are stored in TeamTask instance &lt;code&gt;teams_quantity_distributions&lt;/code&gt; jsonb field.&lt;br&gt;
This code is pernicious because it's a source of memory leak - given a sufficiently large number of teams and component groups we can end up having dynamically defined thousands of instance methods on TeamTask, and this will occur only as action is called for different projects. Plus, it's wholly unnecessary, why not just access the &lt;code&gt;teams_quantity_distributions&lt;/code&gt; field in a regular manner (one still needs to compose the method name same as a hash key), or use method_missing metaprogramming to have just one method definition?!&lt;/p&gt;

&lt;p&gt;What hairy code have you come across lately?&lt;/p&gt;

</description>
      <category>bad</category>
      <category>code</category>
    </item>
    <item>
      <title>Versions VS Approvals</title>
      <dc:creator>Augusts Bautra</dc:creator>
      <pubDate>Mon, 23 Mar 2026 10:16:48 +0000</pubDate>
      <link>https://dev.to/epigene/versions-vs-approvals-2o1i</link>
      <guid>https://dev.to/epigene/versions-vs-approvals-2o1i</guid>
      <description>&lt;p&gt;Today I encountered a tricky problem in a seemingly simple "one record is not showing up in #index" bugfix task.&lt;/p&gt;

&lt;p&gt;The problem stems from this data situation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:project_version_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:is_actual&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; [&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both versioned and non-versioned records can be :actual, can this be right?&lt;/p&gt;

&lt;p&gt;Thinking about it, I came to the realization that working with versioned data requires clear answers to these questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Are 'draft' data ever persisted in a sort of 'lobby'?&lt;/li&gt;
&lt;li&gt;What is more important - accessing possible draft data or possible latest approved/actual version?&lt;/li&gt;
&lt;li&gt;What operation(s) result in new version(s)?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Depending on the answers, you can get one of these two systems:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpll7k2jtp8vikzkqlhrv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpll7k2jtp8vikzkqlhrv.png" alt=" " width="800" height="525"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The key difference for developers is the meaning and importance of unversioned data. In version case, regular data are the actual and special access is needed for historic version data. Whereas in approval pattern it's the opposite - unversioned data is less important, since it's not yet 'approved'.&lt;/p&gt;

&lt;p&gt;Naturally, approval workflow will be harder to reason about and will require careful development to communicate that plain &lt;code&gt;project.tasks.actual&lt;/code&gt; is not quite &lt;code&gt;project.last_approved_version.tasks&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>database</category>
      <category>rails</category>
      <category>ruby</category>
    </item>
    <item>
      <title>TIL: Template Strict Locals (TSL)</title>
      <dc:creator>Augusts Bautra</dc:creator>
      <pubDate>Fri, 13 Feb 2026 09:22:32 +0000</pubDate>
      <link>https://dev.to/epigene/til-template-strict-locals-tsl-31eb</link>
      <guid>https://dev.to/epigene/til-template-strict-locals-tsl-31eb</guid>
      <description>&lt;p&gt;The other day I was giving Chris' "Powerful Rails Features You Might Not Know" presentation a look, and among many useful tips (date and time helpers, nice) was a particular gem - Template Strict Locals.&lt;/p&gt;

&lt;p&gt;Up till now I had been leaving comments like this in view partials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;%# Partial requires `user: @user, group: user_group` %&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But now we have something better, magic comment that will actually enforce the locals!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;%# locals: (user:, user_group:, last_signed_in: nil) -%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also have optional locals with a default value, and even specify a partial that must not be passed any locals:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;# group is now optional
&lt;span class="c"&gt;&amp;lt;%# locals: (user:, user_group: nil) -%&amp;gt;&lt;/span&gt;

# no locals, please
&lt;span class="c"&gt;&amp;lt;%# locals: () -%&amp;gt;&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Title credit to &lt;a href="https://www.youtube.com/watch?v=iPCqwZ9Ouc4" rel="noopener noreferrer"&gt;Chris Oliver&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>views</category>
    </item>
    <item>
      <title>TIL: Blazing-fast correlated exists queries</title>
      <dc:creator>Augusts Bautra</dc:creator>
      <pubDate>Fri, 09 Jan 2026 14:45:29 +0000</pubDate>
      <link>https://dev.to/epigene/til-blazing-fast-correlated-exists-queries-2gmd</link>
      <guid>https://dev.to/epigene/til-blazing-fast-correlated-exists-queries-2gmd</guid>
      <description>&lt;p&gt;Today I took some time at the end of the first work week of the year to review some metrics and touch up some performance issues.&lt;/p&gt;

&lt;p&gt;In one spot we're querying for, using Twitter-clone parlance "Posts with at least one :suggestion Comment", or more generally "gater records from table X that have at least one of association Y matching some conditions".&lt;/p&gt;

&lt;p&gt;Usually I solve this with a join or a subquery like so&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;joins&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:comments&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;type: :suggestion&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But this can get inefficient if there are few Posts and a huge amount of Comments, we're basically redundantly checking all the comments belonging to the same post.&lt;/p&gt;

&lt;p&gt;The key idea is to realize that detecting just one matching Comment for a post is sufficient, we don't have to check/join all of them. Think clearly about whether this situation applies in your systems.&lt;/p&gt;

&lt;p&gt;We can achieve the same lookup result with a clever use of EXISTS like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="no"&gt;Comment&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"comments.post_id = posts.id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# IMPORTANT correlation clause&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;type: :suggestion&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For simple datasets Postgres may be smart enough to optimize both  approaches to a semi-join, so don't be surprised if there's no improvement in some cases, this is still a useful technique to know.&lt;/p&gt;

&lt;p&gt;See also a &lt;a href="https://dev.to/epigene/til-efficient-subqueries-with-exists-38l8"&gt;previous post&lt;/a&gt; of mine regarding the power of EXISTS subqueries.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>performance</category>
      <category>sql</category>
    </item>
    <item>
      <title>The Secret of Minimum Coverage</title>
      <dc:creator>Augusts Bautra</dc:creator>
      <pubDate>Fri, 19 Dec 2025 14:11:42 +0000</pubDate>
      <link>https://dev.to/epigene/the-secret-of-minimum-coverage-29dm</link>
      <guid>https://dev.to/epigene/the-secret-of-minimum-coverage-29dm</guid>
      <description>&lt;p&gt;Today is the last day of work before holidays and I'm finishing up some chores.&lt;br&gt;
One thing I wanted to do was to improve and increase the required minimum of spec coverage in our project. Get it from 30% to 35%. This is somewhat complexed by there being unspecced legacy code and us being unable to just set a blanket 90% minimum for all new files.&lt;/p&gt;

&lt;p&gt;Going through the low offenders I came across a file like this:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fexy0bd7rejvo52da6663.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fexy0bd7rejvo52da6663.png" alt=" " width="436" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The file has no specs, but it gets a surprisingly high coverage because of mere loading of the file. Every inclusion line will artifically pad coverage percent, ditto for various macro calls. No inclusions would still result in a 66% coverage.&lt;/p&gt;

&lt;p&gt;This is bad news for our existing attempts to ensure good coverage for new code. Not sure what improvements we could make.&lt;br&gt;
Maybe adding a linter that checks that every source code file has a corresponding spec file could be a simple improvement - reviewers are much more likely to spot an empty rather than altogehter missing spec file.&lt;br&gt;
Another approach could be to subtract some rows (1-2) from total and covered counts, the example's 3/4 could become (3-2)/(4-2) = 1/2 = 50%.&lt;br&gt;
Finally, dramatically increasing the minimum coverage to 76%+ would to the trick, but would require an inordinate amount of work to mark existing offenders with &lt;code&gt;# :nocov:&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;P.S. I ended up combining spec file presence check and high minimum coverage for all files &lt;strong&gt;except a special list of legacy offenders&lt;/strong&gt; in a &lt;code&gt;SimpleCov.at_exit&lt;/code&gt; block. &lt;/p&gt;

</description>
      <category>rails</category>
      <category>rspec</category>
      <category>tests</category>
      <category>coverage</category>
    </item>
    <item>
      <title>TIL: Inspect Rails app routes from info panel</title>
      <dc:creator>Augusts Bautra</dc:creator>
      <pubDate>Mon, 01 Dec 2025 14:08:39 +0000</pubDate>
      <link>https://dev.to/epigene/til-inspect-rails-app-routes-from-info-panel-3k1b</link>
      <guid>https://dev.to/epigene/til-inspect-rails-app-routes-from-info-panel-3k1b</guid>
      <description>&lt;p&gt;I usually run &lt;code&gt;$ rails routes | grep ...&lt;/code&gt; from console, but today I had to find a pesky excess route definition for a very popular resource that appears in many namespaces. If only there was a way to inspect the exact line in &lt;code&gt;routes.rb&lt;/code&gt; that defines the route...&lt;/p&gt;

&lt;p&gt;There is! At &lt;code&gt;/rails/info/routes&lt;/code&gt; path of the app. The search is not entirely helpful, but should allow narrowing the results down a bit.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>routes</category>
    </item>
    <item>
      <title>More readable failure messages for RSpec #change</title>
      <dc:creator>Augusts Bautra</dc:creator>
      <pubDate>Thu, 30 Oct 2025 11:18:04 +0000</pubDate>
      <link>https://dev.to/epigene/more-readable-failure-messages-for-rspec-change-24da</link>
      <guid>https://dev.to/epigene/more-readable-failure-messages-for-rspec-change-24da</guid>
      <description>&lt;p&gt;The fact that change failure messages for bulky objects like arrays of hashes or just large hashes has griped me for a while and today I decided to use my pre-vacation day when tying up loose ends to see if I can override RSpec's built-in failure message for &lt;code&gt;change&lt;/code&gt; matcher to be more readable.&lt;/p&gt;

&lt;p&gt;Built-in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="sb"&gt;`object.data`&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;have&lt;/span&gt; &lt;span class="n"&gt;changed&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;a: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;but&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;b: &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Desired (multi-line, values aligned):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="sb"&gt;`object.data`&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;have&lt;/span&gt; &lt;span class="n"&gt;changed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;but&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;did&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="ss"&gt;expectation:
  expected: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;a: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="ss"&gt;actual: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;b: &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This turned out to be a bit more involved than monkeypatching a single message method. When you use a compound &lt;code&gt;change {}.and(change {})&lt;/code&gt; structure, RSpec uses &lt;code&gt;RSpec::Matchers::BuiltIn::Compound::And&lt;/code&gt; matcher, instead of &lt;code&gt;RSpec::Matchers::BuiltIn::Change&lt;/code&gt;, so overriding in several places is necessary.&lt;/p&gt;

&lt;p&gt;It can probably be achieved more cleanly by someone more familiar with RSpec's architecture and codebase, but I achieved my goal with this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# place in some /spec/support/respec_change_message_patch.rb&lt;/span&gt;

&lt;span class="no"&gt;CHANGE_FAILURE_REGEX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;%r'&lt;/span&gt;&lt;span class="se"&gt;\A&lt;/span&gt;&lt;span class="sr"&gt;expected `(?&amp;lt;subject&amp;gt;[^`]+)` to have changed to (?&amp;lt;expected&amp;gt;.+), but is now (?&amp;lt;actual&amp;gt;.+)&lt;/span&gt;&lt;span class="se"&gt;\z&lt;/span&gt;&lt;span class="sr"&gt;'m&lt;/span&gt;

&lt;span class="no"&gt;CHANGE_FAILURE_MESSAGE_IMPROVER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;original_message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;original_message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CHANGE_FAILURE_REGEX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class="no"&gt;TEXT&lt;/span&gt;&lt;span class="sh"&gt;
      expected `&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:subject&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;` to have changed, but the value did not match expectation:
        expected: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:expected&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
          actual: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:actual&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;    TEXT&lt;/span&gt;

    &lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Formatters&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ConsoleCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:failure&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="n"&gt;original_message&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Matchers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BuiltIn&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Change&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="no"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;failure_message&lt;/span&gt;
      &lt;span class="no"&gt;CHANGE_FAILURE_MESSAGE_IMPROVER&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;failure_message_when_negated&lt;/span&gt;
      &lt;span class="no"&gt;CHANGE_FAILURE_MESSAGE_IMPROVER&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Matchers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BuiltIn&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Compound&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Matchers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BuiltIn&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Compound&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;And&lt;/span&gt;
&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="no"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;failure_message&lt;/span&gt;
        &lt;span class="no"&gt;CHANGE_FAILURE_MESSAGE_IMPROVER&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;compound_failure_message&lt;/span&gt;
        &lt;span class="no"&gt;CHANGE_FAILURE_MESSAGE_IMPROVER&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's to hoping I get this merged into RSpec at some point. 🍻 &lt;/p&gt;

</description>
      <category>rspec</category>
      <category>monkeypatch</category>
      <category>qol</category>
    </item>
    <item>
      <title>TIL: Explode Hash keys into variables in Ruby 3.2+</title>
      <dc:creator>Augusts Bautra</dc:creator>
      <pubDate>Tue, 28 Oct 2025 11:49:26 +0000</pubDate>
      <link>https://dev.to/epigene/til-explode-hash-keys-into-variables-in-ruby-32-5bil</link>
      <guid>https://dev.to/epigene/til-explode-hash-keys-into-variables-in-ruby-32-5bil</guid>
      <description>&lt;p&gt;Oftentimes we're looping over an array of hashes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="ss"&gt;a: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;a: &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;some_logic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:a&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="c1"&gt;# OR&lt;/span&gt;
  &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:a&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;some_logic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what if we have several keys in the hash? Assigning everything to a variable can get verbose.&lt;/p&gt;

&lt;p&gt;Luckily, modern Ruby provides a neat way to explode the data in a oneliner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="ss"&gt;a: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;a: &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;a: &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;some_logic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Furthermore, you can specify a custom variable name to use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="ss"&gt;a: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;a: &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;a: &lt;/span&gt;&lt;span class="n"&gt;the_value&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;some_logic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;the_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, nested hashes can also be exploded easily:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="ss"&gt;namespace: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;foo: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;bar: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;baz: &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}}}]&lt;/span&gt;
&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;namespace: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="ss"&gt;bar: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;baz: &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;# Only terminal nodes without further nesting get variables&lt;/span&gt;
  &lt;span class="n"&gt;some_logic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>learning</category>
      <category>programming</category>
      <category>ruby</category>
    </item>
    <item>
      <title>TIL: define custom flash types in Rails</title>
      <dc:creator>Augusts Bautra</dc:creator>
      <pubDate>Fri, 24 Oct 2025 11:20:26 +0000</pubDate>
      <link>https://dev.to/epigene/til-define-custom-flash-types-in-rails-2849</link>
      <guid>https://dev.to/epigene/til-define-custom-flash-types-in-rails-2849</guid>
      <description>&lt;p&gt;We've been using a custom &lt;code&gt;:warning&lt;/code&gt; flash type beyond Rails' default &lt;code&gt;:notice&lt;/code&gt; and &lt;code&gt;:alert&lt;/code&gt;, but passing it was cumbersome, had to put it in flash manually, the common way of just passing a key to &lt;code&gt;redirect_to&lt;/code&gt; like &lt;code&gt;redirect_to(root_path, warning: "Try again!")&lt;/code&gt; did not work.&lt;/p&gt;

&lt;p&gt;Turns out you can define custom flash types and have Rails expose the convenience keys automagically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="n"&gt;add_flash_types&lt;/span&gt; &lt;span class="ss"&gt;:warning&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>learning</category>
      <category>rails</category>
      <category>ruby</category>
    </item>
    <item>
      <title>TIL: DB constraints for column values in Rails</title>
      <dc:creator>Augusts Bautra</dc:creator>
      <pubDate>Thu, 23 Oct 2025 08:06:02 +0000</pubDate>
      <link>https://dev.to/epigene/til-db-constraints-for-column-values-in-rails-aoh</link>
      <guid>https://dev.to/epigene/til-db-constraints-for-column-values-in-rails-aoh</guid>
      <description>&lt;p&gt;Today I used the opportunity to try out Rails' &lt;code&gt;#add_check_constraint&lt;/code&gt; migration helper to constrain a new coefficient column's permitted values to only positive ones.&lt;/p&gt;

&lt;p&gt;It's really simple!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;add_check_constraint&lt;/span&gt; &lt;span class="ss"&gt;:my_things&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"some_coefficient &amp;gt; 0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"my_things_some_coefficient_positive"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;if_not_exists: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This technique will help improve the data consistency and quality in the Rails app I maintain, alongside such long-standing techniques as disallowing NULLs, limiting length, and adding foreign-key constraints.&lt;/p&gt;

</description>
      <category>database</category>
      <category>rails</category>
      <category>ruby</category>
    </item>
  </channel>
</rss>
