<?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>Forem: Augusts Bautra</title>
    <description>The latest articles on Forem by Augusts Bautra (@epigene).</description>
    <link>https://forem.com/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>Forem: Augusts Bautra</title>
      <link>https://forem.com/epigene</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/epigene"/>
    <language>en</language>
    <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://forem.com/epigene/til-lexical-fractional-indexing-58j9</link>
      <guid>https://forem.com/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://forem.com/epigene/share-your-cancer-code-2afg</link>
      <guid>https://forem.com/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://forem.com/epigene/versions-vs-approvals-2o1i</link>
      <guid>https://forem.com/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://forem.com/epigene/til-template-strict-locals-tsl-31eb</link>
      <guid>https://forem.com/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://forem.com/epigene/til-blazing-fast-correlated-exists-queries-2gmd</link>
      <guid>https://forem.com/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://forem.com/epigene/the-secret-of-minimum-coverage-29dm</link>
      <guid>https://forem.com/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://forem.com/epigene/til-inspect-rails-app-routes-from-info-panel-3k1b</link>
      <guid>https://forem.com/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://forem.com/epigene/more-readable-failure-messages-for-rspec-change-24da</link>
      <guid>https://forem.com/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://forem.com/epigene/til-explode-hash-keys-into-variables-in-ruby-32-5bil</link>
      <guid>https://forem.com/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://forem.com/epigene/til-define-custom-flash-types-in-rails-2849</link>
      <guid>https://forem.com/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://forem.com/epigene/til-db-constraints-for-column-values-in-rails-aoh</link>
      <guid>https://forem.com/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>
    <item>
      <title>Make your feature specs 69%™ more stable</title>
      <dc:creator>Augusts Bautra</dc:creator>
      <pubDate>Wed, 22 Oct 2025 13:26:42 +0000</pubDate>
      <link>https://forem.com/epigene/make-your-feature-specs-69-more-stable-5dhp</link>
      <guid>https://forem.com/epigene/make-your-feature-specs-69-more-stable-5dhp</guid>
      <description>&lt;p&gt;These past couple of weeks we've been improving our existing feature spec suite to prepare it for switching over to &lt;code&gt;--headless=new&lt;/code&gt; chrome option.&lt;br&gt;
Several of the existing specs had to be updated with proper awaiting, for example&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;click_button&lt;/span&gt; &lt;span class="s2"&gt;"Update"&lt;/span&gt;

&lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"new value"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;oftentimes failed because the button click hadn't been fully processed yet, 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="n"&gt;click_button&lt;/span&gt; &lt;span class="s2"&gt;"Update"&lt;/span&gt;

&lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;have_flash_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Update successful"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"new value"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another source of deeper fragility was the behavior of &lt;code&gt;.set&lt;/code&gt; and &lt;code&gt;fill_in&lt;/code&gt; - sometimes, especially if an input already had a value, the new value would either be appended or not inputted at all.&lt;/p&gt;

&lt;p&gt;We significanly reduced the incidence of these fails by specifying a clear option like this:&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;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:each&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:js&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default_set_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;clear: :backspace&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>
    </item>
  </channel>
</rss>
