<?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>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>
    <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://dev.to/epigene/make-your-feature-specs-69-more-stable-5dhp</link>
      <guid>https://dev.to/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>
    <item>
      <title>HTTP status VS response body</title>
      <dc:creator>Augusts Bautra</dc:creator>
      <pubDate>Thu, 09 Oct 2025 14:19:04 +0000</pubDate>
      <link>https://dev.to/epigene/http-status-vs-response-body-1f8c</link>
      <guid>https://dev.to/epigene/http-status-vs-response-body-1f8c</guid>
      <description>&lt;h3&gt;
  
  
  TL;DR
&lt;/h3&gt;

&lt;p&gt;When designing a (JSON) API, pay extra attention to HTTP statuses used. I usually refer to &lt;a href="https://gist.github.com/mlanett/a31c340b132ddefa9cca" rel="noopener noreferrer"&gt;Rails status cheat-sheet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Quick tips:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For &lt;code&gt;201 :created&lt;/code&gt; prefer no body, just a HEAD response.
&lt;/li&gt;
&lt;li&gt;Be on top of your 4** code use

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;401 :unauthorized&lt;/code&gt; (actually "unauthenticated") if no session/token to identify 'user'. Use response body to specify details (no token, expired token, invalid token etc.)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;403 :forbidden&lt;/code&gt; if user is known, but lacks permissions to interact with the resource. Use response body for details about resource and lacking permission.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;400 :bad_request&lt;/code&gt; if something off with params or format etc. I use this if, for example, filtering params are wrong, page size is exceeded and other meta-probems. Use response body to explain the problem.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;422 :unprocessable_entity&lt;/code&gt; for generic validation errors where user should review submitted data and try again. Think extra hard about how to standardize the response so that you can inform the user about the exact (possibly nested, array) field that is problematic. "Dig" string a-la &lt;code&gt;"data", "users", 0, "email"&lt;/code&gt; is useful to specify the "path" inside the submitted structure.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  The story
&lt;/h3&gt;

&lt;p&gt;Today I was struggling with legacy API comms. &lt;/p&gt;

&lt;p&gt;The logic was 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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;between?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;299&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;yay_success&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But responses like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"success":false, "message":"ERR: No vendor found for ABC"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and even&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"success":true, "message":"ERR: No item found for 123"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;were naturally tripping us up, because apparently parsing the response JSON is now also necessary. This is further complexed by lack of docs and consistency, some endpoints respond with "OK", which is not valid JSON, sigh.&lt;br&gt;
It would be really nice, if responses of problems of this kind were not of the 2** group, but a 422 or somesuch. In that case knowledge and parsing issues for the body become irrelevant, things become slef-documenting™, yada, yada.&lt;/p&gt;

</description>
      <category>api</category>
      <category>architecture</category>
      <category>backend</category>
    </item>
  </channel>
</rss>
