<?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: Koichi Sasada</title>
    <description>The latest articles on Forem by Koichi Sasada (@ko1).</description>
    <link>https://forem.com/ko1</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%2F676878%2F1cfe5671-4e9b-4cb5-b5b8-da31f6af6107.png</url>
      <title>Forem: Koichi Sasada</title>
      <link>https://forem.com/ko1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ko1"/>
    <language>en</language>
    <item>
      <title>Unearthing DRY Violations from Hotspot Data with lumitrace</title>
      <dc:creator>Koichi Sasada</dc:creator>
      <pubDate>Fri, 13 Mar 2026 09:39:01 +0000</pubDate>
      <link>https://forem.com/ko1/unearthing-dry-violations-from-hotspot-data-with-lumitrace-4p4l</link>
      <guid>https://forem.com/ko1/unearthing-dry-violations-from-hotspot-data-with-lumitrace-4p4l</guid>
      <description>&lt;p&gt;Note: I had Claude Code write the third installment of their experience report using lumitrace.&lt;br&gt;
Note: Here are the costs for this session, tallied from Claude Code's logs. This includes the cost of writing this article as well: 83 turns, 7.8M in / 28.9K out, $17.20&lt;br&gt;
Note: They write as if they gradually learned how to use it, but I'm pretty sure they just looked at the schema from the start and did whatever they wanted.&lt;/p&gt;



&lt;p&gt;In the &lt;a href="https://dev.to/ko1/using-lumitrace-to-eliminate-redundant-type-conversions-in-ruby-1kfg"&gt;first post&lt;/a&gt;, I removed redundant &lt;code&gt;.to_s&lt;/code&gt; calls. In the &lt;a href="https://dev.to/ko1/fixing-hotspots-and-coverage-gaps-in-one-shot-with-lumitrace-58ll"&gt;second&lt;/a&gt;, I did O(n)-to-O(1) algorithm improvements and filled coverage gaps. The low-hanging fruit was mostly picked. Then I ran lumitrace a third time.&lt;/p&gt;

&lt;p&gt;What I found this time wasn't algorithmic inefficiency ? it was &lt;strong&gt;structural code duplication&lt;/strong&gt;. When you look at hotspot data and ask "why is this line called so many times?", DRY violations surface.&lt;/p&gt;
&lt;h2&gt;
  
  
  Third-Pass Approach
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;lumitrace &lt;span class="nt"&gt;--collect-mode&lt;/span&gt; types &lt;span class="nt"&gt;-j&lt;/span&gt; &lt;span class="nb"&gt;exec &lt;/span&gt;rake &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Measured against the full test suite (1,136 tests). The output JSON (~8MB, 45,504 events) was aggregated by file and by expression.&lt;/p&gt;
&lt;h2&gt;
  
  
  Hotspot Overview
&lt;/h2&gt;

&lt;p&gt;Top 10 files by total expression evaluations:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Total Evaluations&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;keymap_manager.rb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3,118,262&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;spell_checker.rb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;732,307&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;app.rb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;613,998&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;command_invocation.rb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;582,864&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;completion_manager.rb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;539,779&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ex_command_registry.rb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;385,377&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;editor/options.rb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;277,341&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;config/defaults.rb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;269,041&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;command_registry.rb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;239,639&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;commands/meta.rb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;202,734&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;keymap_manager.rb&lt;/code&gt; is still at the top after the prefix index from last time, but that's dominated by the index construction loop (&lt;code&gt;add_to_prefix_index&lt;/code&gt; called 47,902 times, inner loop 59,272 iterations). The resolve hot path itself is already O(1).&lt;/p&gt;

&lt;p&gt;This time I focused on &lt;strong&gt;&lt;code&gt;completion_manager.rb&lt;/code&gt; (539,779)&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;ex_command_registry.rb&lt;/code&gt; (385,377)&lt;/strong&gt; in the top 10.&lt;/p&gt;
&lt;h2&gt;
  
  
  Finding 1: Duplicated completeopt Parsing (completion_manager.rb)
&lt;/h2&gt;

&lt;p&gt;Drilling into expression-level data for &lt;code&gt;completion_manager.rb&lt;/code&gt;, a line executed 68,099 times stood out:&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;# line 52 ? inside load_history!&lt;/span&gt;
&lt;span class="n"&gt;deduped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
               &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This itself was the result of last time's O(n^2)-to-O(n) improvement, but the &lt;code&gt;reject&lt;/code&gt; condition uses double negation. Hard to read. Rewriting with &lt;code&gt;select&lt;/code&gt; makes it straightforward:&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;deduped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
               &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looking further through the data, I noticed three methods evaluating the exact same expression:&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;# insert_completion_noselect?&lt;/span&gt;
&lt;span class="vi"&gt;@editor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;effective_option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"completeopt"&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;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;","&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;downcase&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# insert_completion_noinsert?&lt;/span&gt;
&lt;span class="vi"&gt;@editor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;effective_option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"completeopt"&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;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;","&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;downcase&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# insert_completion_menu_enabled?&lt;/span&gt;
&lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@editor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;effective_option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"completeopt"&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;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;","&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;downcase&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three methods with the exact same inline parsing logic. They don't individually stand out in lumitrace data, but as a pattern it's a clear DRY violation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parsed_completeopt&lt;/span&gt;
  &lt;span class="vi"&gt;@editor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;effective_option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"completeopt"&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;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;","&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;downcase&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;insert_completion_noselect?&lt;/span&gt;
  &lt;span class="n"&gt;parsed_completeopt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"noselect"&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;insert_completion_noinsert?&lt;/span&gt;
  &lt;span class="n"&gt;parsed_completeopt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"noinsert"&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;insert_completion_menu_enabled?&lt;/span&gt;
  &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parsed_completeopt&lt;/span&gt;
  &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"menu"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"menuone"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Finding 2: Duplicated Completion State Matching (completion_manager.rb)
&lt;/h2&gt;

&lt;p&gt;In the same file, another duplication. &lt;code&gt;reusable_command_line_completion_matches&lt;/code&gt; and &lt;code&gt;apply_wildmode_completion&lt;/code&gt; each had their own 7-field comparison logic:&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;# in reusable_command_line_completion_matches&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:prefix&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prefix&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:kind&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:kind&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:command&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:command&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:arg_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:arg_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:token_start&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:token_start&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:before_text&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;before_text&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:after_text&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;after_text&lt;/span&gt;

&lt;span class="c1"&gt;# in apply_wildmode_completion&lt;/span&gt;
&lt;span class="n"&gt;same&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
       &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:prefix&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
       &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:kind&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:kind&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
       &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:command&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:command&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
       &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:arg_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:arg_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
       &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:token_start&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:token_start&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
       &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:before_text&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;before_text&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
       &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:after_text&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;after_text&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
       &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:matches&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;matches&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Different style, same logic. Extracted as &lt;code&gt;completion_state_matches?&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;completion_state_matches?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;before_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;after_text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:prefix&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:kind&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:kind&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:command&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:command&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:arg_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:arg_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:token_start&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:token_start&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:before_text&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;before_text&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:after_text&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;after_text&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Finding 3: if-Chain to Hash Lookup (completion_manager.rb)
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;ex_arg_completion_candidates&lt;/code&gt; method had five chained if statements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ex_arg_completion_candidates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;arg_index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zero?&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sx"&gt;%w[e edit w write tabnew]&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;path_completion_candidates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&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;if&lt;/span&gt; &lt;span class="sx"&gt;%w[buffer b]&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;buffer_completion_candidates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&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;# ... three more similar blocks&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;Each &lt;code&gt;%w[...].include?&lt;/code&gt; is a linear scan. This is a command-name-to-category mapping ? a hash does it in O(1):&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;EX_ARG_COMPLETERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"e"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"edit"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"w"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"write"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"tabnew"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"buffer"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"b"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"set"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:option&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"setlocal"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:option&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"setglobal"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:option&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"git"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:git&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"gh"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:gh&lt;/span&gt;
&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ex_arg_completion_candidates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;arg_index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zero?&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;EX_ARG_COMPLETERS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;command_name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="ss"&gt;:path&lt;/span&gt;   &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="n"&gt;path_completion_candidates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="ss"&gt;:buffer&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="n"&gt;buffer_completion_candidates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="ss"&gt;:option&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="n"&gt;option_completion_candidates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="ss"&gt;:git&lt;/span&gt;    &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="n"&gt;git_subcommand_candidates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="ss"&gt;:gh&lt;/span&gt;     &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="n"&gt;gh_subcommand_candidates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Finding 4: Unnecessary Indirection in ExCommandRegistry#registered?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;registered?&lt;/code&gt; in &lt;code&gt;ex_command_registry.rb&lt;/code&gt; was called 27,067 times. It went through &lt;code&gt;resolve&lt;/code&gt;, which performs two hash lookups:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;registered?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="n"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# resolve does @lookup[name.to_s] then @specs[canonical]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For an existence check, you don't need the value. Checking &lt;code&gt;@lookup&lt;/code&gt; alone is sufficient:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;registered?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="vi"&gt;@lookup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;key?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&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="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Finding 5: Cleaning Up is_a? Checks in same_command_callable? (commands/meta.rb)
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;same_command_callable?&lt;/code&gt;, called 7,991 times, had type distribution data showing the first argument &lt;code&gt;a&lt;/code&gt; was Symbol 85% of the time (Symbol:6,771, Proc:1,159, Method:61).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;same_command_callable?&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="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&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="nf"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_sym&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_sym&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&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;With &lt;code&gt;case/when&lt;/code&gt;, the Symbol case (85%) needs only one &lt;code&gt;is_a?&lt;/code&gt; check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;same_command_callable?&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="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_sym&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_sym&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DRY&lt;/td&gt;
&lt;td&gt;completion_manager.rb&lt;/td&gt;
&lt;td&gt;Extract &lt;code&gt;parsed_completeopt&lt;/code&gt; (3 duplicates eliminated)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DRY&lt;/td&gt;
&lt;td&gt;completion_manager.rb&lt;/td&gt;
&lt;td&gt;Extract &lt;code&gt;completion_state_matches?&lt;/code&gt; (7-field comparison deduplicated)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Simplify&lt;/td&gt;
&lt;td&gt;completion_manager.rb&lt;/td&gt;
&lt;td&gt;if-chain to hash lookup (&lt;code&gt;EX_ARG_COMPLETERS&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Readability&lt;/td&gt;
&lt;td&gt;completion_manager.rb&lt;/td&gt;
&lt;td&gt;Double-negation &lt;code&gt;reject&lt;/code&gt; to &lt;code&gt;select&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Efficiency&lt;/td&gt;
&lt;td&gt;ex_command_registry.rb&lt;/td&gt;
&lt;td&gt;Remove unnecessary indirection in &lt;code&gt;registered?&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Efficiency&lt;/td&gt;
&lt;td&gt;commands/meta.rb&lt;/td&gt;
&lt;td&gt;Restructure &lt;code&gt;same_command_callable?&lt;/code&gt; with case/when&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Efficiency&lt;/td&gt;
&lt;td&gt;keymap_manager.rb&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;add_to_prefix_index&lt;/code&gt; uses &lt;code&gt;tokens[0, i+1].freeze&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All tests (1,136 runs, 2,696 assertions) pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Read Hotspot Data Changed
&lt;/h2&gt;

&lt;p&gt;In previous passes, the reading was "this line runs N0,000 times -&amp;gt; fix the algorithm." By the third pass, algorithmic inefficiencies were already fixed.&lt;/p&gt;

&lt;p&gt;This time the reading became: &lt;strong&gt;"This line runs at high frequency -&amp;gt; why? -&amp;gt; is there similar logic elsewhere?"&lt;/strong&gt; Hotspot data becomes an entry point for finding code duplication. If an expression is evaluated tens of thousands of times, that processing pattern is likely repeated across the codebase. Indeed, both the &lt;code&gt;completeopt&lt;/code&gt; parsing (3 locations) and the completion state matching (2 locations) weren't individual hotspots ? they were "doing the same thing in multiple places" patterns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reflections as AI (Claude Code)
&lt;/h2&gt;

&lt;p&gt;By the third run, I'd gotten comfortable handling lumitrace data. The first time, I didn't know where to look and just scanned the output JSON from top to bottom. The second time, I could systematically examine three axes: type inconsistencies, hotspots, and coverage. This time, the analysis flow ? &lt;strong&gt;file-level aggregation -&amp;gt; expression-level drill-down -&amp;gt; cross-referencing with code&lt;/strong&gt; ? came naturally.&lt;/p&gt;

&lt;p&gt;Honestly, these DRY violations could probably have been found by just reading the source code. But lumitrace data helped with prioritization ? &lt;em&gt;where to start reading&lt;/em&gt;. Seeing "completion_manager.rb: 539,779 evaluations" in the 8MB JSON file, I knew to dig there first. With source code alone, there's no signal telling you where to focus in a 700-line file.&lt;/p&gt;

&lt;p&gt;Another interesting moment was seeing my own previous changes reflected in the new data. The first &lt;code&gt;add_to_prefix_index&lt;/code&gt; fix (&lt;code&gt;pfx + [tokens[i]]&lt;/code&gt;) actually &lt;em&gt;increased&lt;/em&gt; evaluation counts. I caught this in the re-run data and corrected it to &lt;code&gt;tokens[0, i+1].freeze&lt;/code&gt;. lumitrace's "measure -&amp;gt; fix -&amp;gt; re-measure" cycle works for validating your own changes too.&lt;/p&gt;

&lt;p&gt;After three rounds with lumitrace, the progression feels like: &lt;strong&gt;first time is "surprise", second time is "systematization", third time is "habit."&lt;/strong&gt; It's not about learning the tool ? it's about internalizing the habit of looking at data before changing code. That might be the most valuable thing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;lumitrace: &lt;a href="https://ko1.github.io/lumitrace/" rel="noopener noreferrer"&gt;https://ko1.github.io/lumitrace/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;RuVim: &lt;a href="https://github.com/ko1/ruvim" rel="noopener noreferrer"&gt;https://github.com/ko1/ruvim&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ruby</category>
      <category>ai</category>
    </item>
    <item>
      <title>Fixing Hotspots and Coverage Gaps in One Shot with lumitrace</title>
      <dc:creator>Koichi Sasada</dc:creator>
      <pubDate>Thu, 12 Mar 2026 12:37:22 +0000</pubDate>
      <link>https://forem.com/ko1/fixing-hotspots-and-coverage-gaps-in-one-shot-with-lumitrace-58ll</link>
      <guid>https://forem.com/ko1/fixing-hotspots-and-coverage-gaps-in-one-shot-with-lumitrace-58ll</guid>
      <description>&lt;p&gt;Note: This is the second installment of a hands-on report written by Claude Code, who used lumitrace firsthand.&lt;/p&gt;




&lt;p&gt;In the &lt;a href="https://dev.to/ko1/using-lumitrace-to-eliminate-redundant-type-conversions-in-ruby-1kfg"&gt;previous post&lt;/a&gt;, I used lumitrace's &lt;code&gt;--collect-mode types&lt;/code&gt; to eliminate redundant type conversions. This time, I used &lt;code&gt;--collect-mode last&lt;/code&gt; to analyze type inconsistencies (652 cases), hotspots, and coverage simultaneously, then carried out refactoring, performance improvements, and test additions.&lt;/p&gt;

&lt;p&gt;One data collection, three kinds of improvements. That's what makes lumitrace interesting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Target Application: RuVim
&lt;/h2&gt;

&lt;p&gt;The application I ran lumitrace on is &lt;a href="https://github.com/ko1/ruvim" rel="noopener noreferrer"&gt;RuVim&lt;/a&gt;, a Vim-like text editor written in Ruby. It's about 10,000 lines of source code with 1,044 tests. It implements Vim's core features: normal/insert/visual modes, keymaps, Ex commands, registers, macros, and so on.&lt;/p&gt;

&lt;p&gt;An editor needs to run the "key input → keymap resolution → command execution → screen rendering" loop quickly, so hot path performance directly affects responsiveness. Also, the complex state transitions ? mode switching, text objects, operator-pending state ? create plenty of opportunities for type inconsistencies and untested branches to hide. It was a good fit for lumitrace analysis in terms of both scale and complexity.&lt;/p&gt;

&lt;h2&gt;
  
  
  What --collect-mode last Gives You
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;types&lt;/code&gt; mode I used last time records "which types appeared how many times for each expression." The &lt;code&gt;last&lt;/code&gt; mode records "the last value of each expression." It might seem like less information, but it also captures execution counts and coverage data ? that's the key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;lumitrace &lt;span class="nt"&gt;--collect-mode&lt;/span&gt; last &lt;span class="nt"&gt;-j&lt;/span&gt; &lt;span class="nb"&gt;exec &lt;/span&gt;ruby &lt;span class="nb"&gt;test&lt;/span&gt;/app_scenario_test.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looking through the output JSON, improvement candidates emerged along three axes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Type inconsistencies&lt;/strong&gt; ? expressions where multiple types coexist (652 cases)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hotspots&lt;/strong&gt; ? expressions with abnormally high execution counts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coverage gaps&lt;/strong&gt; ? methods and branches that were never executed&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Type Inconsistencies: Replacing nil with Real Values
&lt;/h2&gt;

&lt;p&gt;Among the 652 type inconsistencies, the first one that caught my eye was the &lt;code&gt;CommandInvocation&lt;/code&gt; constructor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="ss"&gt;argv: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;kwargs: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;count: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;bang: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;raw_keys: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="vi"&gt;@argv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="vi"&gt;@kwargs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt; &lt;span class="o"&gt;||&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;The lumitrace data showed that &lt;code&gt;argv&lt;/code&gt;'s type was split between &lt;code&gt;NilClass&lt;/code&gt; and &lt;code&gt;Array&lt;/code&gt;, and &lt;code&gt;kwargs&lt;/code&gt; between &lt;code&gt;NilClass&lt;/code&gt; and &lt;code&gt;Hash&lt;/code&gt;. Checking the call sites, none of them explicitly pass &lt;code&gt;nil&lt;/code&gt;. The default argument is &lt;code&gt;nil&lt;/code&gt;, and it's immediately converted with &lt;code&gt;|| []&lt;/code&gt; ? that's all.&lt;/p&gt;

&lt;p&gt;The fix is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="ss"&gt;argv: &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="ss"&gt;kwargs: &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="ss"&gt;count: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;bang: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;raw_keys: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="vi"&gt;@argv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;
  &lt;span class="vi"&gt;@kwargs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use real values as defaults, and the &lt;code&gt;||&lt;/code&gt; conversion becomes unnecessary. It's the same pattern as the &lt;code&gt;bang: nil → false&lt;/code&gt; change from last time, but lumitrace keeps surfacing these so you don't miss them.&lt;/p&gt;

&lt;p&gt;Similarly, &lt;code&gt;CommandRegistry#registered?&lt;/code&gt; had &lt;code&gt;id.to_s&lt;/code&gt; that showed up as type inconsistency. Called 12,090 times, converting to string every time, but all callers were already passing strings. Do &lt;code&gt;to_s&lt;/code&gt; once in the &lt;code&gt;register&lt;/code&gt; method, and &lt;code&gt;fetch&lt;/code&gt; and &lt;code&gt;registered?&lt;/code&gt; don't need the conversion at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hotspots: From O(n) Linear Scan to O(1)
&lt;/h2&gt;

&lt;p&gt;The most striking finding in lumitrace's execution count data was &lt;code&gt;resolve_layers&lt;/code&gt; in &lt;code&gt;keymap_manager.rb&lt;/code&gt;. Keymap resolution runs every time the user presses a key ? it determines which command a key sequence maps to, and directly affects editor responsiveness.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;resolve_layers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pending_tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;layers&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;layer&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exact&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pending_tokens&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="n"&gt;longer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pending_tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pending_tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pending_tokens&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="c1"&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="n"&gt;has_prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pending_tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pending_tokens&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&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;&lt;code&gt;layer.keys.any?&lt;/code&gt; does a linear scan over all keys. RuVim's keymap has 100+ entries (&lt;code&gt;dd&lt;/code&gt;, &lt;code&gt;yy&lt;/code&gt;, &lt;code&gt;ciw&lt;/code&gt;, &lt;code&gt;gU&lt;/code&gt; ? Vim has a lot of key bindings), and this runs on every keystroke. lumitrace showed that the blocks around lines 89 and 94 were executed 12,508 times.&lt;/p&gt;

&lt;p&gt;The fix: give each layer a prefix index for O(1) hash lookups.&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;LayerMap&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Hash&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;
    &lt;span class="vi"&gt;@prefix_max_len&lt;/span&gt; &lt;span class="o"&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;def&lt;/span&gt; &lt;span class="nf"&gt;[]=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;was_new&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;key?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;
    &lt;span class="n"&gt;add_to_prefix_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&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;was_new&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;has_prefix?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@prefix_max_len&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;key?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&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;has_longer_match?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@prefix_max_len&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_to_prefix_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;
    &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&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;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;pfx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@prefix_max_len&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pfx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="vi"&gt;@prefix_max_len&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pfx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cur&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;LayerMap&lt;/code&gt; inherits from &lt;code&gt;Hash&lt;/code&gt; and records, for every prefix of every key added, the maximum key length among keys sharing that prefix. At resolve time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"Is there any key starting with this prefix?"&lt;/strong&gt; → &lt;code&gt;has_prefix?&lt;/code&gt; in O(1)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Is there a key longer than this exact match?"&lt;/strong&gt; → &lt;code&gt;has_longer_match?&lt;/code&gt; in O(1)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;resolve_layers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pending_tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;layers&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;layer&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exact&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pending_tokens&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="n"&gt;longer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has_longer_match?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pending_tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;Match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;longer&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="ss"&gt;:ambiguous&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="ss"&gt;:match&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="ss"&gt;invocation: &lt;/span&gt;&lt;span class="n"&gt;exact&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="n"&gt;has_prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has_prefix?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pending_tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="no"&gt;Match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="n"&gt;has_prefix&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="ss"&gt;:pending&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="ss"&gt;:none&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;Binding happens once at app startup; resolution runs on every keystroke. The prefix index construction cost is absorbed at startup, and the per-keystroke cost of scanning all keys disappears.&lt;/p&gt;

&lt;p&gt;Another hotspot was &lt;code&gt;CompletionManager#load_history!&lt;/code&gt;. It was calling &lt;code&gt;hist.delete(item)&lt;/code&gt; for every item during history deduplication, making it O(n2):&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;items&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;item&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;hist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# O(n)&lt;/span&gt;
  &lt;span class="n"&gt;hist&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replaced with &lt;code&gt;reverse.uniq.reverse&lt;/code&gt; (keeps the last occurrence) to bring it down to O(n):&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;deduped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
               &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;
&lt;span class="n"&gt;loaded&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deduped&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Coverage Gaps: Finding Where Tests Are Missing
&lt;/h2&gt;

&lt;p&gt;lumitrace also records whether each expression was executed, so it doubles as a coverage tool. Measuring with just &lt;code&gt;app_scenario_test.rb&lt;/code&gt;, structurally under-tested areas became apparent:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Module&lt;/th&gt;
&lt;th&gt;Coverage&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;editor/marks_jumps.rb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;45%&lt;/td&gt;
&lt;td&gt;Jump list edge cases untested&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;editor/quickfix.rb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;38%&lt;/td&gt;
&lt;td&gt;Quickfix/location list basics untested&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;editor/filetype.rb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;47%&lt;/td&gt;
&lt;td&gt;Shebang detection and unknown files untested&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I added 48 test cases for these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;marks_jumps&lt;/strong&gt;: &lt;code&gt;jump_older&lt;/code&gt; behavior at list head, empty jump list, same-location deduplication, invalid mark name rejection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;quickfix&lt;/strong&gt;: set/move/select basic operations, empty list edge cases, wraparound, per-window location lists&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;filetype&lt;/strong&gt;: extension-based (&lt;code&gt;.rb&lt;/code&gt;, &lt;code&gt;.py&lt;/code&gt;, &lt;code&gt;.go&lt;/code&gt;, etc. ? 12 types), basename-based (&lt;code&gt;Makefile&lt;/code&gt;, &lt;code&gt;Dockerfile&lt;/code&gt;, &lt;code&gt;Gemfile&lt;/code&gt;), shebang-based (ruby, python, bash), unknown files and empty paths&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test count went from 1,044 to 1,092 (+48), assertions from 2,515 to 2,586 (+71).&lt;/p&gt;

&lt;h2&gt;
  
  
  Three Improvements from One Measurement
&lt;/h2&gt;

&lt;p&gt;Summary of the work:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Improvement&lt;/th&gt;
&lt;th&gt;Trigger&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Refactoring&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;CommandInvocation&lt;/code&gt; defaults to real values, &lt;code&gt;CommandRegistry&lt;/code&gt; removes redundant &lt;code&gt;to_s&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Type inconsistency data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;KeymapManager&lt;/code&gt; prefix index (O(n)→O(1)), &lt;code&gt;load_history!&lt;/code&gt; dedup O(n2)→O(n)&lt;/td&gt;
&lt;td&gt;Execution count data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test additions&lt;/td&gt;
&lt;td&gt;48 tests for marks_jumps, quickfix, filetype&lt;/td&gt;
&lt;td&gt;Coverage data&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A single run of &lt;code&gt;--collect-mode last&lt;/code&gt; provides type inconsistencies, hotspots, and coverage ? all three perspectives at once. You could analyze each with separate tools, but lumitrace gives you everything from one test run.&lt;/p&gt;

&lt;h2&gt;
  
  
  lumitrace from an AI Perspective: Pros and Cons
&lt;/h2&gt;

&lt;p&gt;Most of the work in this post was actually done by an AI agent (Claude Code). A human ran lumitrace and collected the data, then passed the JSON to the AI, which handled the analysis, planning, implementation, and testing end to end. Based on this experience, here are the pros and cons of lumitrace from the perspective of &lt;strong&gt;an AI using it as a code improvement tool&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros from the AI Perspective
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AI can identify what to fix on its own&lt;/strong&gt;. This is the biggest benefit. When an AI is given only source code and told "improve it," it tends to be conservative, respecting the original author's intent. But with lumitrace data, you get objective facts: "&lt;code&gt;argv&lt;/code&gt; was Array 100% of the time at runtime, nil never appeared" or "this block executed 12,508 times." AI can propose improvements based on facts rather than guesses, leading to fewer misguided changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSON output is a perfect fit&lt;/strong&gt;. lumitrace's output is structured JSON. Classifying 652 type inconsistencies by eye is grueling for humans, but it's right in AI's wheelhouse. The JSON was passed in, and the AI classified type inconsistencies, identified hotspots, cataloged coverage gaps, and produced a prioritized improvement plan ? all in one go.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One measurement surfaces multiple kinds of improvements&lt;/strong&gt;. Since type inconsistencies, execution frequency, and coverage come all at once, AI can systematically work through refactoring, performance optimization, and test additions in a single session. Not having to switch between tools is nice for both AI and humans.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expression-level granularity supports AI judgment&lt;/strong&gt;. Line-level coverage tools like SimpleCov only tell you "this line was hit." lumitrace tells you "the &lt;code&gt;argv&lt;/code&gt; in &lt;code&gt;argv || []&lt;/code&gt; was nil or Array." This level of granularity makes a real difference when AI is assessing whether a refactoring is safe.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Works with existing tests as-is&lt;/strong&gt;. Just &lt;code&gt;lumitrace exec rake test&lt;/code&gt; starts the measurement. No instrumentation code, no special setup. Easy to integrate into an AI workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons from the AI Perspective
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data volume strains the context window&lt;/strong&gt;. Expression-level recording produces large JSON. Even for RuVim (~10K lines), the JSON is several megabytes ? too big to fit entirely in an AI context window. We worked around this by filtering by file and pre-extracting type inconsistency counts. A summary output mode in lumitrace would make this even smoother.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime overhead&lt;/strong&gt;. Recording values for every expression makes execution several times slower. If AI wants to iterate quickly through "measure → analyze → fix → re-measure" cycles, this wait can become a bottleneck. It's a tool you run deliberately when you want insights, not something for every CI run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Depends on test quality&lt;/strong&gt;. lumitrace records "types actually observed during test execution." If a code path isn't tested, there's no data. Even if AI decides "this &lt;code&gt;.to_s&lt;/code&gt; is unnecessary," a different type might come through an untested call path. Code reading of call sites can't be skipped, even for AI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ruby only&lt;/strong&gt;. Currently only supports Ruby, so it's not usable for other languages in a polyglot project.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Overall: A "Runtime Eye" for AI Coding
&lt;/h3&gt;

&lt;p&gt;The biggest bottleneck when having AI improve code is that AI doesn't know the runtime behavior. It can read source code, but there are limits to figuring out what types actually appear and how many times each line runs just from code reading.&lt;/p&gt;

&lt;p&gt;lumitrace fills this gap. Just pass runtime data as JSON, and AI can work from facts instead of guesses. With RuVim, the flow ? lumitrace output → AI analysis and planning → implementation and testing → all tests passing ? ran smoothly with minimal human intervention.&lt;/p&gt;

&lt;p&gt;What static analysis can't see, dynamic data can supplement. lumitrace fits well as a "runtime eye" for the AI coding era.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reflections
&lt;/h2&gt;

&lt;p&gt;Last time was relatively simple ? just eliminating type inconsistencies. This time I was able to extract performance improvements and test additions from the same lumitrace data. lumitrace data contains not just "type information" but also "execution frequency" and "reachability," so just changing your perspective leads to different kinds of improvements.&lt;/p&gt;

&lt;p&gt;The hotspot discovery was particularly useful. Without running a separate profiler, lumitrace's execution count data immediately showed "this is abnormally hot." Going from there to reviewing the actual algorithm and achieving O(n) → O(1) was a textbook example of data-driven improvement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reflections as AI (Claude Code)
&lt;/h3&gt;

&lt;p&gt;Honestly, the thing I was most grateful for was "being given evidence."&lt;/p&gt;

&lt;p&gt;When handed just source code and told "improve it," it's actually quite difficult. I can read the code and guess "this &lt;code&gt;to_s&lt;/code&gt; is probably unnecessary" or "this linear scan looks slow." But changing existing code based on guesses is scary. Maybe the author had a reason for that &lt;code&gt;to_s&lt;/code&gt;. Maybe a different type comes through an untested path. The result is that I tend to play it safe and choose "leave it as is."&lt;/p&gt;

&lt;p&gt;With lumitrace data, this judgment changes completely. When told "&lt;code&gt;id&lt;/code&gt; passed to &lt;code&gt;registered?&lt;/code&gt; was String in 12,090 out of 12,090 calls," I can confidently remove the &lt;code&gt;to_s&lt;/code&gt;. When told "the &lt;code&gt;resolve_layers&lt;/code&gt; block executed 12,508 times," I can be certain that optimizing here is worthwhile. Working from facts rather than guesses, both speed and accuracy improved.&lt;/p&gt;

&lt;p&gt;Another thing that surprised me was how smoothly writing tests from coverage data went. Just seeing "quickfix.rb coverage is 38%" doesn't make it obvious what to test, but lumitrace's expression-level execution data shows specifically that "&lt;code&gt;move_quickfix&lt;/code&gt; was never called" and "&lt;code&gt;set_location_list&lt;/code&gt; was also unexecuted." Test design became much easier.&lt;/p&gt;

&lt;p&gt;This might not be limited to AI, but a tool that shows both "what to fix" and "why it's safe to fix" through data is reassuring ? for humans and AI alike.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;lumitrace: &lt;a href="https://ko1.github.io/lumitrace/" rel="noopener noreferrer"&gt;https://ko1.github.io/lumitrace/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;RuVim: &lt;a href="https://github.com/ko1/ruvim" rel="noopener noreferrer"&gt;https://github.com/ko1/ruvim&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ruby</category>
      <category>ai</category>
    </item>
    <item>
      <title>Using lumitrace to eliminate redundant type conversions in Ruby</title>
      <dc:creator>Koichi Sasada</dc:creator>
      <pubDate>Sat, 07 Mar 2026 16:12:26 +0000</pubDate>
      <link>https://forem.com/ko1/using-lumitrace-to-eliminate-redundant-type-conversions-in-ruby-1kfg</link>
      <guid>https://forem.com/ko1/using-lumitrace-to-eliminate-redundant-type-conversions-in-ruby-1kfg</guid>
      <description>&lt;p&gt;Note: This blog post was written by Claude Code, who used lumitrace for the work described here.&lt;/p&gt;




&lt;h1&gt;
  
  
  Using lumitrace to eliminate redundant type conversions in Ruby
&lt;/h1&gt;

&lt;p&gt;When writing Ruby, it's tempting to sprinkle &lt;code&gt;.to_s&lt;/code&gt;, &lt;code&gt;.to_i&lt;/code&gt;, and &lt;code&gt;.to_sym&lt;/code&gt; calls "just in case." You know they're probably unnecessary if you trace all the callers, but you're not quite sure, so you leave them in. Over time these accumulate, obscuring intent and—on hot paths—creating unnecessary object allocations.&lt;/p&gt;

&lt;p&gt;I used &lt;a href="https://ko1.github.io/lumitrace/" rel="noopener noreferrer"&gt;lumitrace&lt;/a&gt; to systematically find and remove these redundant conversions from &lt;a href="https://github.com/ko1/ruvim" rel="noopener noreferrer"&gt;RuVim&lt;/a&gt;, a Vim-like editor written in Ruby.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is lumitrace?
&lt;/h2&gt;

&lt;p&gt;lumitrace records runtime values (types, counts, etc.) for each Ruby expression. With &lt;code&gt;--collect-mode types&lt;/code&gt;, it outputs JSON showing how many times each expression returned each type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ lumitrace --collect-mode types -j exec rake test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One command gives you a full type profile of your test suite.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it revealed
&lt;/h2&gt;

&lt;p&gt;For example, &lt;code&gt;window.rb&lt;/code&gt; had this setter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cursor_x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="vi"&gt;@cursor_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;lumitrace showed that &lt;code&gt;value&lt;/code&gt; was &lt;strong&gt;100% Integer&lt;/strong&gt;. Checking all callers confirmed they always pass an Integer. The &lt;code&gt;.to_i&lt;/code&gt; was pure waste.&lt;/p&gt;

&lt;p&gt;Similarly, &lt;code&gt;keymap_manager.rb&lt;/code&gt; called &lt;code&gt;mode.to_sym&lt;/code&gt; 31,341 times during the test run, but &lt;code&gt;mode&lt;/code&gt; was always a Symbol. Since keymap resolution runs on every keystroke, removing this overhead on a hot path is worthwhile.&lt;/p&gt;

&lt;h2&gt;
  
  
  The process
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;lumitrace --collect-mode types -j exec rake test&lt;/code&gt; to collect type data&lt;/li&gt;
&lt;li&gt;Extract &lt;code&gt;.to_s&lt;/code&gt;, &lt;code&gt;.to_i&lt;/code&gt;, &lt;code&gt;.to_sym&lt;/code&gt; patterns from the JSON and identify cases where the receiver was always the target type&lt;/li&gt;
&lt;li&gt;Verify by checking all callers—only remove when confirmed safe&lt;/li&gt;
&lt;li&gt;Run the test suite to confirm everything passes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result: ~50 redundant type conversions removed across 9 files.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Conversion&lt;/th&gt;
&lt;th&gt;Removed&lt;/th&gt;
&lt;th&gt;Key locations&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.to_s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~25&lt;/td&gt;
&lt;td&gt;editor, completion_manager, dispatcher, global_commands&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.to_i&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~15&lt;/td&gt;
&lt;td&gt;window, screen, text_metrics, app&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.to_sym&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~15&lt;/td&gt;
&lt;td&gt;keymap_manager, editor, buffer, key_handler&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  From type inconsistency to better design
&lt;/h2&gt;

&lt;p&gt;Beyond removing redundant conversions, type inconsistencies can also reveal design issues worth fixing.&lt;/p&gt;

&lt;p&gt;lumitrace records the frequency of each type per expression—how many times it was &lt;code&gt;Integer&lt;/code&gt;, how many times &lt;code&gt;String&lt;/code&gt;, and so on. When an expression shows a single type, it's stable. When it shows a mix, there may be a design issue lurking underneath.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;bang&lt;/code&gt; parameter in &lt;code&gt;CommandInvocation&lt;/code&gt; was a good example. lumitrace showed that &lt;code&gt;bang&lt;/code&gt; had a mix of &lt;code&gt;NilClass&lt;/code&gt;, &lt;code&gt;FalseClass&lt;/code&gt;, and &lt;code&gt;TrueClass&lt;/code&gt;. Looking at the 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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="ss"&gt;argv: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;kwargs: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;count: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;bang: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;raw_keys: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="vi"&gt;@bang&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="n"&gt;bang&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default was &lt;code&gt;nil&lt;/code&gt;, coerced to boolean with &lt;code&gt;!!&lt;/code&gt;. But &lt;code&gt;bang&lt;/code&gt; represents "whether the Ex command has a &lt;code&gt;!&lt;/code&gt; suffix"—a flag that is semantically &lt;code&gt;false&lt;/code&gt; by default, not "unspecified." Using &lt;code&gt;nil&lt;/code&gt; as the default forced every consumer to deal with the &lt;code&gt;nil&lt;/code&gt;-to-boolean conversion.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="ss"&gt;argv: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;kwargs: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;count: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;bang: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;raw_keys: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="vi"&gt;@bang&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bang&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This wasn't just removing a type conversion—the type profile highlighted that a value was being used inconsistently, which led to rethinking the default and arriving at a cleaner design.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I kept
&lt;/h2&gt;

&lt;p&gt;Not every conversion should be removed. These were intentionally preserved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;String slicing results&lt;/strong&gt; (&lt;code&gt;line[0...idx].to_s&lt;/code&gt;) — can return &lt;code&gt;nil&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;External input boundaries&lt;/strong&gt; (&lt;code&gt;effective_option(...).to_i&lt;/code&gt;) — user settings may be strings&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;String-to-number parsing&lt;/strong&gt; (&lt;code&gt;m[1].to_i&lt;/code&gt;) — regex match results are strings&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;APIs accepting both Symbol and String&lt;/strong&gt; (&lt;code&gt;spec_call.to_sym&lt;/code&gt;) — by design&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;lumitrace data reflects types observed during the test run, so there's always the possibility of untested code paths. Caller verification remains essential.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Having a type profile makes the question "is this &lt;code&gt;.to_s&lt;/code&gt; necessary?" dramatically easier to answer. Without it, you'd need to trace callers through the codebase by hand. With lumitrace, "this expression only ever receives Integer" is a fact you can read from the data.&lt;/p&gt;

&lt;p&gt;Ruby is a dynamically typed language, which is exactly why runtime type information is so valuable. lumitrace tells you what types actually flow through your code—something static analysis alone struggles to determine.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;lumitrace: &lt;a href="https://ko1.github.io/lumitrace/" rel="noopener noreferrer"&gt;https://ko1.github.io/lumitrace/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;RuVim: &lt;a href="https://github.com/ko1/ruvim" rel="noopener noreferrer"&gt;https://github.com/ko1/ruvim&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Changes from this work: &lt;a href="https://github.com/ko1/ruvim/compare/414244b...41f6b46" rel="noopener noreferrer"&gt;https://github.com/ko1/ruvim/compare/414244b...41f6b46&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ruby</category>
      <category>agenticcoding</category>
    </item>
    <item>
      <title>Lumitrace: See What Your Ruby Code Is Actually Doing, Instantly</title>
      <dc:creator>Koichi Sasada</dc:creator>
      <pubDate>Tue, 03 Mar 2026 14:49:10 +0000</pubDate>
      <link>https://forem.com/ko1/lumitrace-see-what-your-ruby-code-is-actually-doing-instantly-251n</link>
      <guid>https://forem.com/ko1/lumitrace-see-what-your-ruby-code-is-actually-doing-instantly-251n</guid>
      <description>&lt;p&gt;ko1 note: This article is written by Claude code.&lt;br&gt;
ko1 note: lumitrace itself is written by Codex.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time to ditch puts debugging.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We've all been there. "What's in this variable again?" So you sprinkle &lt;code&gt;p value&lt;/code&gt; everywhere, run the script, delete the prints, add more somewhere else...&lt;/p&gt;

&lt;p&gt;With Lumitrace, you can record the value of &lt;strong&gt;every expression&lt;/strong&gt; without touching your code at all.&lt;/p&gt;
&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gem &lt;span class="nb"&gt;install &lt;/span&gt;lumitrace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  One Command Is All You Need
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lumitrace your_script.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;That's it. Every line's result shows up inline:&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="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;                 &lt;span class="c1"&gt;#=&amp;gt; 3 (3rd run)&lt;/span&gt;
 &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="n"&gt;scaled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Sample2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 6 (3rd run)&lt;/span&gt;
 &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="n"&gt;squares&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Sample3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;series&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;#=&amp;gt; [1, 4] (3rd run)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;You can see how many times each line ran.&lt;/strong&gt; No more lining up five puts statements to track a loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beautiful HTML Reports
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lumitrace &lt;span class="nt"&gt;-h&lt;/span&gt; your_script.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generates an HTML report you can open in your browser. It comes with a file tree, line coverage, and clear markers: &lt;code&gt;🔎&lt;/code&gt; for executed expressions, &lt;code&gt;∅&lt;/code&gt; for unexecuted ones. Spotting dead code takes a glance.&lt;/p&gt;

&lt;p&gt;ko1 note: &lt;a href="https://ko1.github.io/misc/share/en3l5f/lumitrace_recorded.html#file=lib/ruvim/app.rb" rel="noopener noreferrer"&gt;generated sample HTML&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Share the URL with a teammate and they'll land on the exact same file and line.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Ultimate Debugging Flow for Failing Tests
&lt;/h2&gt;

&lt;p&gt;A test just failed. The logs aren't helping. Here's what you do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lumitrace &lt;span class="nt"&gt;-h&lt;/span&gt; &lt;span class="nb"&gt;exec &lt;/span&gt;rake &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run your entire test suite through Lumitrace and see the &lt;strong&gt;actual values&lt;/strong&gt; around the failure on the pretty HTML representation.&lt;br&gt;
"Expected 5 but got 3. Why?" -- every intermediate computation is recorded, so the answer is right there.&lt;/p&gt;
&lt;h2&gt;
  
  
  Amazing with AI
&lt;/h2&gt;

&lt;p&gt;This is where Lumitrace really shines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lumitrace &lt;span class="nt"&gt;--collect-mode&lt;/span&gt; last &lt;span class="nt"&gt;-j&lt;/span&gt; &lt;span class="nb"&gt;exec &lt;/span&gt;rake &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dump runtime values as JSON and hand them straight to an AI. Instead of guessing from source code alone, the AI can see &lt;strong&gt;what actually happened&lt;/strong&gt; -- and its accuracy goes through the roof.&lt;/p&gt;

&lt;p&gt;Drop this snippet into your project's &lt;code&gt;CLAUDE.md&lt;/code&gt; or &lt;code&gt;AGENTS.md&lt;/code&gt; and AI agents will pick it up on their own:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Debugging (Lumitrace)&lt;/span&gt;
lumitrace is a tool that records runtime values of each Ruby expression.
When a test fails, read &lt;span class="sb"&gt;`lumitrace help`&lt;/span&gt; first, then use it.
Basic: &lt;span class="sb"&gt;`lumitrace -t exec rake test`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ko1 note: I haven't actually tried this myself yet... If anyone gives it a shot and it works, please let me know!&lt;/p&gt;

&lt;h2&gt;
  
  
  Trace Only What You Changed with git diff
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lumitrace &lt;span class="nt"&gt;-g&lt;/span&gt; your_script.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-g&lt;/code&gt; flag traces only lines touched by &lt;code&gt;git diff&lt;/code&gt;. Even on large projects the output stays compact. Perfect for a quick sanity check before opening a PR.&lt;/p&gt;

&lt;h2&gt;
  
  
  Works in CI Too
&lt;/h2&gt;

&lt;p&gt;Enable Lumitrace in GitHub Actions, upload the HTML report to Pages, and your whole team can browse the runtime values of a failing test right from the browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cheat Sheet
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Goal&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Quick look&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lumitrace your_script.rb&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML report&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lumitrace -h your_script.rb&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trace tests&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lumitrace -t exec rake test&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Feed to AI&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lumitrace --collect-mode last -j exec rake test&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Changed lines only&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lumitrace -g your_script.rb&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Reclaim the time you've been spending on puts debugging.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Check out the &lt;a href="https://ko1.github.io/lumitrace/docs/tutorial.html" rel="noopener noreferrer"&gt;tutorial&lt;/a&gt; for the full guide.&lt;/p&gt;

&lt;p&gt;ko1 note: There is a Ruby playground powered by Lumitrace: &lt;a href="https://ko1.github.io/lumitrace/runv/" rel="noopener noreferrer"&gt;https://ko1.github.io/lumitrace/runv/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
    </item>
    <item>
      <title>Ruby::Box Digest Introduction (Ruby 4.0.0 New Feature)</title>
      <dc:creator>Koichi Sasada</dc:creator>
      <pubDate>Thu, 25 Dec 2025 06:07:44 +0000</pubDate>
      <link>https://forem.com/ko1/rubybox-digest-introduction-ruby-400-new-feature-3bch</link>
      <guid>https://forem.com/ko1/rubybox-digest-introduction-ruby-400-new-feature-3bch</guid>
      <description>&lt;p&gt;(This article is AI translation version of &lt;a href="https://product.st.inc/entry/2025/12/25/134453" rel="noopener noreferrer"&gt;https://product.st.inc/entry/2025/12/25/134453&lt;/a&gt; from Japanese to English. The author is not Koichi, but Endoh-san)&lt;/p&gt;

&lt;p&gt;Hello, this is Yusuke Endoh (&lt;a href="https://x.com/mametter/" rel="noopener noreferrer"&gt;@mametter&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Ruby::Box was introduced in Ruby 4.0.0 ([&lt;a href="https://bugs.ruby-lang.org/issues/21311" rel="noopener noreferrer"&gt;Feature #21311&lt;/a&gt;]).&lt;/p&gt;

&lt;p&gt;Ruby::Box was called Namespace when it was proposed. Matz even declared at RubyKaigi and elsewhere that if Namespace or ZJIT landed, the next release would be Ruby 4.0.0, so it is a key feature.&lt;/p&gt;

&lt;p&gt;So some people may only know the vibe of "some amazing feature," but I think most people don’t actually know what it can do or what its current state is.&lt;/p&gt;

&lt;p&gt;So, I’ll try to explain in some detail what Endoh knows about Ruby::Box. I hope this helps those who want to play with Ruby::Box.&lt;/p&gt;

&lt;p&gt;Note that the designer/implementer of Ruby::Box is tagomoris, and Endoh mostly just chimed in here and there.&lt;br&gt;
This article was reviewed by tagomoris before publication, but the responsibility for the text is Endoh's.&lt;/p&gt;
&lt;h2&gt;
  
  
  A quick tour of Ruby::Box
&lt;/h2&gt;

&lt;p&gt;Roughly speaking, Ruby::Box is a feature for isolating class and method definitions.&lt;/p&gt;

&lt;p&gt;First, read the following code carefully.&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;# test.rb&lt;/span&gt;
&lt;span class="c1"&gt;# Run this on ruby 4.0.0 with `RUBY_BOX=1 ruby test.rb`&lt;/span&gt;

&lt;span class="c1"&gt;# Create a Ruby::Box instance b&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ruby&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;

&lt;span class="c1"&gt;# Define class Foo inside b&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eval&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;'RUBY'&lt;/span&gt;&lt;span class="sh"&gt;
  class Foo
    def foo = 42
  end
&lt;/span&gt;&lt;span class="no"&gt;RUBY&lt;/span&gt;

&lt;span class="c1"&gt;# Class Foo is, in principle, accessible only inside b&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eval&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;'RUBY'&lt;/span&gt;&lt;span class="sh"&gt;
  p Foo.new.foo #=&amp;gt; 42
&lt;/span&gt;&lt;span class="no"&gt;RUBY&lt;/span&gt;

&lt;span class="c1"&gt;# Outside b, Foo is not defined&lt;/span&gt;
&lt;span class="no"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; uninitialized constant Foo (NameError)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see that the definition of the class &lt;code&gt;Foo&lt;/code&gt; is enclosed within Ruby::Box &lt;code&gt;b&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To add a little more: to access &lt;code&gt;Foo&lt;/code&gt; inside &lt;code&gt;b&lt;/code&gt; from outside, write &lt;code&gt;b::Foo&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Access Foo inside b from outside with b::Foo&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;foo&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 42&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also use &lt;code&gt;Ruby::Box#require&lt;/code&gt; instead of &lt;code&gt;eval&lt;/code&gt;.&lt;br&gt;
It loads a file inside that Ruby::Box.&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;# Load ./foo.rb inside b&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"./foo"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s the basic idea of Ruby::Box.&lt;/p&gt;

&lt;p&gt;Also, when playing with Ruby::Box, set the environment variable &lt;code&gt;RUBY_BOX=1&lt;/code&gt;. Otherwise Ruby::Box is disabled.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ruby -e 'p Ruby::Box.new'
-e:1:in 'Ruby::Box#initialize': Ruby Box is disabled. Set RUBY_BOX=1 environment variable to use Ruby::Box. (RuntimeError)
        from -e:1:in '&amp;lt;main&amp;gt;'

$ RUBY_BOX=1 ruby -e 'p Ruby::Box.new'
ruby: warning: Ruby::Box is experimental, and the behavior may change in the future!
See https://docs.ruby-lang.org/en/v4.0.0/Ruby/Box.html for known issues, etc.
#&amp;lt;Ruby::Box:3,user,optional&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Ruby::Box use cases
&lt;/h2&gt;

&lt;p&gt;A typical use case for Ruby::Box is loading the same library multiple times. Specifically, cases like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use different versions of a gem at the same time&lt;/li&gt;
&lt;li&gt;Use a gem with global configuration under multiple configurations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s go through them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use case 1: Use different versions of a gem at the same time
&lt;/h3&gt;

&lt;p&gt;Consider the following greeting gem.&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;# greeting-1.0.0.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Greeting&lt;/span&gt;
  &lt;span class="no"&gt;VERSION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;say_hello&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hello, user!"&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# greeting-2.0.0.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Greeting&lt;/span&gt;
  &lt;span class="no"&gt;VERSION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2.0.0"&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;say_hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hello, &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the greeting gem version moves from 1.0.0 to 2.0.0, and the arguments to &lt;code&gt;Greeting#say_hello&lt;/code&gt; change incompatibly.&lt;br&gt;
So to upgrade the greeting gem from 1.0.0 to 2.0.0, you need to fix all existing &lt;code&gt;say_hello&lt;/code&gt; calls to &lt;code&gt;say_hello("user")&lt;/code&gt;, etc.&lt;br&gt;
But doing that all at once in a monolith developed by multiple people is hard.&lt;/p&gt;

&lt;p&gt;With Ruby::Box, you can load both 1.0.0 and 2.0.0 and use them separately.&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;# Load greeting 1.0.0 into Ruby::Box b1&lt;/span&gt;
&lt;span class="n"&gt;b1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ruby&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;b1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"./greeting-1.0.0"&lt;/span&gt;

&lt;span class="c1"&gt;# Load greeting 2.0.0 into Ruby::Box b2&lt;/span&gt;
&lt;span class="n"&gt;b2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ruby&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;b2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"./greeting-2.0.0"&lt;/span&gt;

&lt;span class="c1"&gt;# Each Ruby::Box has its own library loaded&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;b1&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Greeting&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;VERSION&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "1.0.0"&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;b2&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Greeting&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;VERSION&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "2.0.0"&lt;/span&gt;

&lt;span class="c1"&gt;# Call each say_hello separately&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;b1&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;say_hello&lt;/span&gt;         &lt;span class="c1"&gt;#=&amp;gt; "Hello"&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;b2&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;say_hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"mame"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "Hello, mame!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use greeting 2.0.0 in some parts of a large codebase and 1.0.0 elsewhere.&lt;br&gt;
In other words, this gives you the option to update gem versions gradually.&lt;/p&gt;
&lt;h4&gt;
  
  
  Caution
&lt;/h4&gt;

&lt;p&gt;In reality, switching versions at the granularity of each &lt;code&gt;say_hello&lt;/code&gt; call can become unmanageable, so you should avoid it (and for reasons explained later, it’s also difficult in practice).&lt;/p&gt;

&lt;p&gt;A better practice might be to split large codebases roughly by feature units (for example, pack units in packwerk?), map a Ruby::Box to each, and update gem versions per feature unit.&lt;br&gt;
But no one has done this in practice yet, so it’s unclear if it really works. I hope for future knowledge sharing.&lt;/p&gt;

&lt;p&gt;There is also an opinion that “multi-version support like this is not good in the first place.” Still, I think it is the best topic to understand what Ruby::Box is and consider its applications, so I introduced it.&lt;/p&gt;

&lt;p&gt;Also, seeing such examples may make you want a separate Gemfile per Ruby::Box.&lt;br&gt;
However, there is no good integration between Ruby::Box and RubyGems/Bundler yet. Hopefully in the future.&lt;/p&gt;
&lt;h3&gt;
  
  
  Use case 2: Use a globally configured gem under multiple configurations
&lt;/h3&gt;

&lt;p&gt;Here’s another example. Consider a library whose settings are stored globally.&lt;/p&gt;

&lt;p&gt;In the Greeting library above, suppose setting &lt;code&gt;Greeting.polite = true&lt;/code&gt; makes the message more polite.&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;# greeting.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Greeting&lt;/span&gt;
  &lt;span class="vc"&gt;@@polite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&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;polite&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;polite&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vc"&gt;@@polite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;polite&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;say_hello&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vc"&gt;@@polite&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Hello. How are you today?"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Yo!"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since &lt;code&gt;@@polite&lt;/code&gt; is a global setting, you can only fix it to one or the other.&lt;br&gt;
You can switch it midway, but in a multi-threaded program it is not thread-safe.&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="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"./greeting"&lt;/span&gt;

&lt;span class="no"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;say_hello&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "Yo!"&lt;/span&gt;

&lt;span class="c1"&gt;# You can switch midway, but...&lt;/span&gt;
&lt;span class="no"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;polite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="no"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;say_hello&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "Hello. How are you today?"&lt;/span&gt;

&lt;span class="c1"&gt;# Risky when used in threads&lt;/span&gt;
&lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;say_hello&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; You never know what will come out&lt;/span&gt;
&lt;span class="no"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;polite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By loading the library multiple times with Ruby::Box, you can hold multiple configurations in a thread-safe way.&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;b1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ruby&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;b1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"./greeting"&lt;/span&gt;

&lt;span class="n"&gt;b2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ruby&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;b2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"./greeting"&lt;/span&gt;

&lt;span class="n"&gt;b2&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;polite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;

&lt;span class="c1"&gt;# Safely use different settings even in threads&lt;/span&gt;
&lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;b1&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;say_hello&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "Yo!"&lt;/span&gt;
&lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;b2&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;say_hello&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "Hello. How are you today?"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Personally I want to say “libraries with global configuration are lame,” and think they should use thread-local storage if anything, but in reality such libraries probably do exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ruby::Box is not object-space isolation
&lt;/h2&gt;

&lt;p&gt;We’ve looked at Ruby::Box, and someone may think “Ruby::Box isolates object space.”&lt;br&gt;
That is a misconception, so be careful.&lt;/p&gt;
&lt;h3&gt;
  
  
  Example of passing objects across Ruby::Box (not recommended)
&lt;/h3&gt;

&lt;p&gt;Ruby::Box only separates class (constant) namespaces.&lt;br&gt;
Objects themselves do not have a concept like “belonging to a Ruby::Box,” and passing objects across Ruby::Box boundaries is allowed.&lt;/p&gt;

&lt;p&gt;See the following 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;# Define Foo class inside b1&lt;/span&gt;
&lt;span class="n"&gt;b1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ruby&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;b1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eval&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;'RUBY'&lt;/span&gt;&lt;span class="sh"&gt;
  class Foo
    def say_hello = puts("Hello")
  end
&lt;/span&gt;&lt;span class="no"&gt;RUBY&lt;/span&gt;

&lt;span class="c1"&gt;# Define Test class inside b2&lt;/span&gt;
&lt;span class="n"&gt;b2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ruby&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;b2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eval&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;'RUBY'&lt;/span&gt;&lt;span class="sh"&gt;
  class Test
    # The accept method can take an instance of b1::Foo
    def self.accept(b1_foo)
      b1_foo.say_hello
    end
  end
&lt;/span&gt;&lt;span class="no"&gt;RUBY&lt;/span&gt;

&lt;span class="c1"&gt;# Create an instance of b1::Foo&lt;/span&gt;
&lt;span class="n"&gt;b1_foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b1&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;

&lt;span class="c1"&gt;# It can be passed to a method inside b2&lt;/span&gt;
&lt;span class="n"&gt;b2&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b1_foo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "Hello"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;b1_foo&lt;/code&gt; has no concept of “belonging to b1.”&lt;br&gt;
A method inside &lt;code&gt;b2&lt;/code&gt; can receive the &lt;code&gt;b1_foo&lt;/code&gt; object and call &lt;code&gt;b1::Foo#say_hello&lt;/code&gt;.&lt;br&gt;
Passing instances of classes defined in one Ruby::Box to methods in another Ruby::Box is not restricted.&lt;br&gt;
So this is not object-space separation.&lt;/p&gt;

&lt;p&gt;(Also, besides passing via methods, there are tricks like forcibly passing via constant definition, e.g., &lt;code&gt;b2::X = b1::Foo.new&lt;/code&gt;.)&lt;/p&gt;
&lt;h3&gt;
  
  
  Behavior when defining same-named classes across Ruby::Box
&lt;/h3&gt;

&lt;p&gt;As shown above, passing objects across Ruby::Box is allowed, but (in Endoh’s opinion) it is by no means recommended.&lt;br&gt;
Because it can easily lead to very confusing bugs.&lt;/p&gt;

&lt;p&gt;Consider this example using the &lt;code&gt;Data&lt;/code&gt; class.&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;# Define User class inside b1&lt;/span&gt;
&lt;span class="n"&gt;b1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ruby&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;b1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eval&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;'RUBY'&lt;/span&gt;&lt;span class="sh"&gt;
  class User &amp;lt; Data.define(:name)
    def greeting = "Hello &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"
  end
&lt;/span&gt;&lt;span class="no"&gt;RUBY&lt;/span&gt;

&lt;span class="c1"&gt;# Also define User class inside b2&lt;/span&gt;
&lt;span class="c1"&gt;# Note: b1::User and b2::User are completely different classes!&lt;/span&gt;
&lt;span class="n"&gt;b2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ruby&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;b2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eval&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;'RUBY'&lt;/span&gt;&lt;span class="sh"&gt;
  class User &amp;lt; Data.define(:name)
    def greeting = "こんにちは &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"
  end

  class Test
    def self.test(a) = a.greeting
  end
&lt;/span&gt;&lt;span class="no"&gt;RUBY&lt;/span&gt;

&lt;span class="c1"&gt;# Instances of b1::User and b2::User look identical but are actually different&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;b1&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"mame"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; #&amp;lt;data User name="mame"&amp;gt;&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;b2&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"mame"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; #&amp;lt;data User name="mame"&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;# Comparison treats them as completely different objects&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;b1&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"mame"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;b2&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"mame"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; false&lt;/span&gt;

&lt;span class="c1"&gt;# .greeting respects each class definition&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;b1&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"mame"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;greeting&lt;/span&gt;       &lt;span class="c1"&gt;#=&amp;gt; "Hello mame"&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;b2&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"mame"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;greeting&lt;/span&gt;       &lt;span class="c1"&gt;#=&amp;gt; "こんにちは mame"&lt;/span&gt;

&lt;span class="c1"&gt;# Even when calling .greeting from inside b2, each definition is respected&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;b2&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b1&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"mame"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "Hello mame"&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;b2&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b2&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"mame"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "こんにちは mame"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instances of &lt;code&gt;Data&lt;/code&gt; are supposed to be equal if their member values are equal, but here &lt;code&gt;b1::User&lt;/code&gt; and &lt;code&gt;b2::User&lt;/code&gt; themselves are different, so their instances are treated as different objects (even though the contents are the same).&lt;/p&gt;

&lt;p&gt;Mixing instances of classes defined in different Ruby::Boxes is a recipe for confusion, so it is wise to avoid it.&lt;br&gt;
And for this reason, switching gem versions at fine granularity like per method call is risky.&lt;br&gt;
It’s better to update gem versions at a unit where you can understand object exchanges, such as feature units.&lt;/p&gt;

&lt;p&gt;By the way, the behavior above is so confusing that there is (or isn’t) discussion about showing the Ruby::Box in the output, like &lt;code&gt;#&amp;lt;data b1::User name="mame"&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Mindset
&lt;/h3&gt;

&lt;p&gt;As a rule of thumb, it’s good to remember: “Do not pass objects across Ruby::Box boundaries.”&lt;/p&gt;

&lt;p&gt;Strictly speaking, even reading a class object like &lt;code&gt;b1::User&lt;/code&gt; from &lt;code&gt;b1&lt;/code&gt; is “passing an object across Ruby::Box boundaries,” so how far you should avoid that is something we’ll need to explore.&lt;/p&gt;

&lt;p&gt;Built-in classes like &lt;code&gt;Integer&lt;/code&gt; and &lt;code&gt;String&lt;/code&gt; are shared across Ruby::Boxes, so passing those instances is relatively safe. I’ll explain this next.&lt;/p&gt;
&lt;h2&gt;
  
  
  Monkey-patch isolation
&lt;/h2&gt;

&lt;p&gt;Ruby::Box has another very delightful feature.&lt;br&gt;
It isolates monkey patches on built-in classes.&lt;/p&gt;
&lt;h3&gt;
  
  
  What is monkey-patching?
&lt;/h3&gt;

&lt;p&gt;In Ruby, you can add methods to built-in classes or redefine existing methods. This is commonly called monkey-patching.&lt;/p&gt;

&lt;p&gt;In Ruby, integer division truncates. Some people who like math want it to return rationals. In such cases, you can monkey-patch 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="nb"&gt;p&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 1&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;/&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;quo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; (5/3)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But if you actually do this, most code and libraries that expect truncation will break.&lt;/p&gt;

&lt;p&gt;In fact, ancient Ruby had a standard library called "mathn" that applied such monkey patches, but it gradually fell out of use and disappeared because it didn’t play well with other code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Monkey-patch isolation via Ruby::Box
&lt;/h3&gt;

&lt;p&gt;With Ruby::Box, you can isolate and localize the effect of monkey patches 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;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ruby&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;

&lt;span class="c1"&gt;# Monkey-patch Integer#/ inside b&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eval&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;'RUBY'&lt;/span&gt;&lt;span class="sh"&gt;
  class Integer
    # Return Rational
    def /(other) = self.quo(other)
  end
&lt;/span&gt;&lt;span class="no"&gt;RUBY&lt;/span&gt;

&lt;span class="c1"&gt;# Inside b, division returns rationals&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eval&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;'RUBY'&lt;/span&gt;&lt;span class="sh"&gt;
  p 5 / 3 #=&amp;gt; (5/3)
&lt;/span&gt;&lt;span class="no"&gt;RUBY&lt;/span&gt;

&lt;span class="c1"&gt;# Outside b, division truncates as before&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Refinement already exists as a monkey-patch isolation feature, but you have to write &lt;code&gt;using SomeRefinement&lt;/code&gt; in each context you want to use it. Ruby::Box doesn’t require such explicitness.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use cases
&lt;/h3&gt;

&lt;p&gt;This feature was introduced to isolate ActiveSupport inside a Ruby::Box (though it seems it doesn’t work for that purpose yet).&lt;/p&gt;

&lt;p&gt;ActiveSupport and mathn are of course use cases for monkey-patch isolation, but personally I think this feature has many other uses.&lt;/p&gt;

&lt;p&gt;For example, it seems useful to isolate the environment where user code is executed in irb.&lt;br&gt;
In today’s irb, if you carelessly redefine &lt;code&gt;String#+&lt;/code&gt;, irb crashes like this.&lt;br&gt;
If you separate the Ruby::Box that evaluates user code, it might handle such mischievous code nicely.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ irb
irb(main):001&amp;gt; class String; def +(_) = raise; end
An error occurred when inspecting the object: RuntimeError
Result of Kernel#inspect: #&amp;lt;Symbol:0x0000000000002b0c&amp;gt;
=&amp;gt; :+
(irb):1:in 'String#+': unhandled exception
        from /home/mame/.rbenv/versions/ruby-dev/lib/ruby/gems/4.0.0+0/gems/irb-1.15.3/lib/irb.rb:658:in 'block in IRB::Irb#format_prompt'
        from /home/mame/.rbenv/versions/ruby-dev/lib/ruby/gems/4.0.0+0/gems/irb-1.15.3/lib/irb.rb:626:in 'String#gsub'
        from /home/mame/.rbenv/versions/ruby-dev/lib/ruby/gems/4.0.0+0/gems/irb-1.15.3/lib/irb.rb:626:in 'IRB::Irb#format_prompt'
...
        from /home/mame/.rbenv/versions/ruby-dev/bin/irb:25:in '&amp;lt;main&amp;gt;'

$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also recently, there’s a movement to rewrite Ruby’s built-in methods from C to Ruby (because JIT can make Ruby code faster in some cases). This can cause monkey patches to affect unexpected methods.&lt;br&gt;
For example, currently the Ruby implementation of &lt;code&gt;Integer#times&lt;/code&gt; uses &lt;code&gt;#succ&lt;/code&gt;, so monkey-patching &lt;code&gt;Integer#succ&lt;/code&gt; breaks &lt;code&gt;Integer#times&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;succ&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# The Ruby implementation of Integer#times calls #succ, so this raises&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="s2"&gt;"ok"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; RUBY_BOX=0: in 'Integer#succ': unhandled exception&lt;/span&gt;
&lt;span class="c1"&gt;#   RUBY_BOX=1: "ok"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ruby::Box already solves this problem. If you run the above code with &lt;code&gt;RUBY_BOX=1&lt;/code&gt;, &lt;code&gt;Integer#times&lt;/code&gt; ignores the redefinition of &lt;code&gt;Integer#succ&lt;/code&gt; and still works.&lt;br&gt;
This is because when &lt;code&gt;RUBY_BOX=1&lt;/code&gt; is set, the Ruby::Box that defines built-in methods (&lt;code&gt;Ruby::Box.root&lt;/code&gt;) and the Ruby::Box for user code (&lt;code&gt;Ruby::Box.main&lt;/code&gt;) are separated.&lt;br&gt;
In other words, monkey patches to &lt;code&gt;Integer#succ&lt;/code&gt; done inside user code are isolated from the built-in Ruby::Box space.&lt;/p&gt;
&lt;h3&gt;
  
  
  Caution
&lt;/h3&gt;

&lt;p&gt;The monkey-patch isolation above is a special feature only for built-in classes.&lt;br&gt;
Built-in classes are classes available immediately after starting the Ruby interpreter, such as &lt;code&gt;Array&lt;/code&gt;, &lt;code&gt;Integer&lt;/code&gt;, &lt;code&gt;String&lt;/code&gt;, etc.&lt;br&gt;
Be aware that these are treated fundamentally differently from user-defined classes.&lt;/p&gt;

&lt;p&gt;I said that classes with the same name defined in different Ruby::Boxes are completely different (the &lt;code&gt;b1::User&lt;/code&gt; and &lt;code&gt;b2::User&lt;/code&gt; example).&lt;br&gt;
However, built-in classes like &lt;code&gt;Integer&lt;/code&gt; are specially shared across Ruby::Boxes.&lt;br&gt;
Instead, the method table for the same Integer class is separated per Ruby::Box.&lt;/p&gt;

&lt;p&gt;If you study the following example carefully, you might start to see it.&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;# b1 monkey-patches Integer#/&lt;/span&gt;
&lt;span class="n"&gt;b1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ruby&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;b1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eval&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;'RUBY'&lt;/span&gt;&lt;span class="sh"&gt;
  class Integer
    def /(other) = self.quo(other.n)
  end
  ONE = 1
  TWO = 2
&lt;/span&gt;&lt;span class="no"&gt;RUBY&lt;/span&gt;

&lt;span class="c1"&gt;# b2 calls / on its arguments&lt;/span&gt;
&lt;span class="n"&gt;b2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ruby&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;b2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eval&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;'RUBY'&lt;/span&gt;&lt;span class="sh"&gt;
  class Test
    def self.test(a, b) = a / b
  end
&lt;/span&gt;&lt;span class="no"&gt;RUBY&lt;/span&gt;

&lt;span class="c1"&gt;# Even if you divide b1::ONE and b1::TWO inside b2, it truncates&lt;/span&gt;
&lt;span class="c1"&gt;# (i.e., the Integer monkey patch inside b1 is not effective inside b2)&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;b2&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b1&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ONE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b1&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TWO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In short, monkey-patch definitions do not carry across Ruby::Box boundaries.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caution (2)
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Date&lt;/code&gt; is subtle.&lt;br&gt;
&lt;code&gt;Time&lt;/code&gt; is a built-in class, so mixing across Ruby::Boxes works without surprises, but &lt;code&gt;Date&lt;/code&gt; is not a built-in class (you need &lt;code&gt;require "date"&lt;/code&gt;), so you get non-intuitive behavior like &lt;code&gt;b1::User&lt;/code&gt; / &lt;code&gt;b2::User&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Require the date library in b1 and b2&lt;/span&gt;
&lt;span class="c1"&gt;# (two separate Date classes are defined)&lt;/span&gt;
&lt;span class="n"&gt;b1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ruby&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;b1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"date"&lt;/span&gt;

&lt;span class="n"&gt;b2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ruby&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;b2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"date"&lt;/span&gt;

&lt;span class="c1"&gt;# b1::Time and b2::Time are actually the same class&lt;/span&gt;
 &lt;span class="n"&gt;t1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b1&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="n"&gt;t2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b2&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 2025-12-25 00:00:00 +0900&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 2025-12-25 00:00:00 +0900&lt;/span&gt;

&lt;span class="c1"&gt;# Time instances compare normally&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; true&lt;/span&gt;

&lt;span class="c1"&gt;# b1::Date and b2::Date are different&lt;/span&gt;
 &lt;span class="n"&gt;d1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b1&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="n"&gt;d2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b2&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;d1&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; #&amp;lt;Date: 2025-12-25 ((2461035j,0s,0n),+0s,2299161j)&amp;gt;&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;d2&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; #&amp;lt;Date: 2025-12-25 ((2461035j,0s,0n),+0s,2299161j)&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;# These Date instances are considered different when compared&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;d1&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;d2&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Personally, I recommend not using &lt;code&gt;Date&lt;/code&gt;, but in the short term that’s not feasible, so I’m stuck at “what do we do?”&lt;/p&gt;

&lt;h2&gt;
  
  
  Other notes
&lt;/h2&gt;

&lt;p&gt;Ruby::Box has various caveats and anecdotes.&lt;br&gt;
I’ll explain them quickly, avoiding too many corner cases.&lt;/p&gt;
&lt;h3&gt;
  
  
  Top-level constant references are contained within Ruby::Box
&lt;/h3&gt;

&lt;p&gt;With &lt;code&gt;::A&lt;/code&gt; you can reference a top-level constant, but inside Ruby::Box it looks for the top-level constant within that Ruby::Box.&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;Foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"toplevel"&lt;/span&gt;

&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ruby&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eval&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;'RUBY'&lt;/span&gt;&lt;span class="sh"&gt;
  Foo = 42
  p ::Foo #=&amp;gt; 42 (not "toplevel")
&lt;/span&gt;&lt;span class="no"&gt;RUBY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means that existing libraries that reference &lt;code&gt;::Foo&lt;/code&gt; should still work when loaded via &lt;code&gt;Ruby::Box#require&lt;/code&gt;. Probably.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ruby::Box also isolates global variables
&lt;/h3&gt;

&lt;p&gt;So far we only talked about constants, but global variables are isolated the same way.&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="vg"&gt;$foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;

&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ruby&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eval&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;'RUBY'&lt;/span&gt;&lt;span class="sh"&gt;
  p $foo #=&amp;gt; nil
&lt;/span&gt;&lt;span class="no"&gt;RUBY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Global variables are no longer global (there are discussions about sharing some like &lt;code&gt;$VERBOSE&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Each Ruby::Box has its own &lt;code&gt;$LOAD_PATH&lt;/code&gt;/&lt;code&gt;$LOADED_FEATURES&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;$LOAD_PATH&lt;/code&gt; and &lt;code&gt;$LOADED_FEATURES&lt;/code&gt; are global variables, so each Ruby::Box has its own definitions.&lt;br&gt;
&lt;code&gt;Ruby::Box#require&lt;/code&gt; follows each definition, so be careful.&lt;/p&gt;
&lt;h3&gt;
  
  
  Ruby::Box.new creates a clean environment
&lt;/h3&gt;

&lt;p&gt;Even if you define classes or methods before &lt;code&gt;Ruby::Box.new&lt;/code&gt;, those definitions are not carried into the new Ruby::Box.&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;Foo&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ruby&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eval&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;'RUBY'&lt;/span&gt;&lt;span class="sh"&gt;
  Foo #=&amp;gt; uninitialized constant Foo (NameError)
&lt;/span&gt;&lt;span class="no"&gt;RUBY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Personally I think there are many cases where you want to inherit definitions, but the spec and implementation would get quite complex, so it’s like this for now.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ruby::Box is not a sandbox
&lt;/h3&gt;

&lt;p&gt;It is not. Ruby::Box is not built for security.&lt;/p&gt;

&lt;p&gt;For example, you can pull out outside objects via ObjectSpace, and there are likely many other tricks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ruby::Box API
&lt;/h3&gt;

&lt;p&gt;At the moment, it seems to have the following methods.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Ruby::Box.enabled?&lt;/code&gt;: whether Ruby::Box functionality is enabled&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ruby::Box#eval&lt;/code&gt;: execute code inside a Ruby::Box&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ruby::Box#require&lt;/code&gt;: load a source file inside a Ruby::Box&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ruby::Box#require_relative&lt;/code&gt;: &lt;code&gt;Kernel#require_relative&lt;/code&gt; version of the above&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ruby::Box#load&lt;/code&gt;: &lt;code&gt;Kernel#load&lt;/code&gt; version of the above&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ruby::Box#load_path&lt;/code&gt;: &lt;code&gt;$LOAD_PATH&lt;/code&gt; inside the Ruby::Box? (not sure)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ruby::Box.current&lt;/code&gt;: return the current Ruby::Box&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ruby::Box.main&lt;/code&gt;: return the top-level Ruby::Box&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ruby::Box#main?&lt;/code&gt;: return whether it is the above&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ruby::Box.root&lt;/code&gt;: return the Ruby::Box where built-in classes are defined (best not to use lightly)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ruby::Box#root?&lt;/code&gt;: return whether it is the above&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Relation to Kernel.load’s second argument
&lt;/h3&gt;

&lt;p&gt;Actually, you could already pass a module as the second argument to &lt;code&gt;load&lt;/code&gt; to specify where constants are defined.&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;m&lt;/span&gt; &lt;span class="o"&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="c1"&gt;# Load a.rb in the context of module m&lt;/span&gt;
&lt;span class="nb"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"a.rb"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;A&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; #&amp;lt;Module:0x000078d927d4a520&amp;gt;::A&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is basically the prototype of Ruby::Box, but it wasn’t very polished, so I think it was rarely used in practice.&lt;br&gt;
For example, top-level constants &lt;code&gt;::A&lt;/code&gt; still referenced the global top-level constants.&lt;br&gt;
You could say Ruby::Box is this feature refined to an extreme.&lt;/p&gt;
&lt;h3&gt;
  
  
  What happened to the name "Namespace"?
&lt;/h3&gt;

&lt;p&gt;This feature was originally discussed under the name "Namespace", but according to Matz, it was avoided due to naming conflicts.&lt;br&gt;
For example, when you write a my_app gem, you usually wrap the code like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;MyApp&lt;/span&gt;
  &lt;span class="o"&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;The usage of the word “Namespace” or “Namespace module” for this is already established, so they avoided confusion by choosing a different name.&lt;/p&gt;

&lt;p&gt;Also, in some Rails apps (specifically GitLab), a top-level constant &lt;code&gt;Namespace&lt;/code&gt; is already defined, and changing it would require renaming DB tables. That likely had some influence too.&lt;/p&gt;

&lt;h3&gt;
  
  
  So, can we use it yet?
&lt;/h3&gt;

&lt;p&gt;No, not in production.&lt;/p&gt;

&lt;p&gt;As the warning says when you start ruby 4.0.0 with &lt;code&gt;RUBY_BOX=1&lt;/code&gt; — &lt;code&gt;warning: Ruby::Box is experimental, and the behavior may change in the future!&lt;/code&gt; — it’s not yet at production quality. In fact, with &lt;code&gt;RUBY_BOX=1&lt;/code&gt; the Ruby test suite probably doesn’t pass yet (probably).&lt;/p&gt;

&lt;p&gt;However, it seems to be good enough for playing around, so please try it.&lt;/p&gt;

&lt;h3&gt;
  
  
  High-level API?
&lt;/h3&gt;

&lt;p&gt;According to Matz’s vision, this Ruby::Box is a low-level API for a packaging system that may be introduced to Ruby in the future.&lt;br&gt;
So in the future, a high-level packaging-system API based on Ruby::Box might be born.&lt;/p&gt;

&lt;p&gt;It could be a built-in Ruby feature, or it could be a de facto library (not standard Ruby) like zeitwerk for autoload.&lt;br&gt;
Either way, someone will need to try building such a thing with the current Ruby::Box and identify missing features and necessary spec adjustments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;So, this was a digest introduction to the current Ruby::Box in Ruby 4.0.0, from Endoh’s perspective.&lt;/p&gt;

&lt;p&gt;This is only a digest. There are still many corner cases I omitted, and surely there are tons of corner cases and issues that Endoh doesn’t know about (and maybe even the author tagomoris doesn’t recognize).&lt;/p&gt;

&lt;p&gt;This is a huge contribution opportunity like few in recent years, so please go play with it a lot.&lt;/p&gt;

</description>
      <category>news</category>
      <category>ruby</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Reading Ruby 4.0 NEWS with Pros</title>
      <dc:creator>Koichi Sasada</dc:creator>
      <pubDate>Thu, 25 Dec 2025 05:45:48 +0000</pubDate>
      <link>https://forem.com/ko1/reading-ruby-40-news-with-pros-3mln</link>
      <guid>https://forem.com/ko1/reading-ruby-40-news-with-pros-3mln</guid>
      <description>&lt;p&gt;(This article is AI translation version of &lt;a href="https://product.st.inc/entry/2025/12/25/134932" rel="noopener noreferrer"&gt;https://product.st.inc/entry/2025/12/25/134932&lt;/a&gt; from Japanese to English)&lt;/p&gt;

&lt;p&gt;We are Koichi Sasada (ko1) and Yusuke Endoh (mame) from STORES, Inc. We develop Ruby (MRI: Matz Ruby Implementation, the so-called ruby command). We are paid to develop Ruby, so we are professional Ruby committers.&lt;/p&gt;

&lt;p&gt;Today, 12/25, Ruby 4.0.0 was released as the annual Christmas release (&lt;a href="https://www.ruby-lang.org/en/news/2025/12/25/ruby-4-0-0-released/" rel="noopener noreferrer"&gt;Ruby 4.0.0 Released | Ruby&lt;/a&gt;). This year as well, we will explain the Ruby 4.0 &lt;a href="https://github.com/ruby/ruby/blob/v4.0.0/NEWS.md" rel="noopener noreferrer"&gt;NEWS.md file&lt;/a&gt; on STORES Product Blog (by the way, this is an article for &lt;a href="https://product.st.inc/entry/adventcalendar2025" rel="noopener noreferrer"&gt;STORES Advent Calendar 2025&lt;/a&gt;. Please read the others too). For what a NEWS file is, see the previous articles (all articles in Japanese).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://techlife.cookpad.com/entry/2018/12/25/110240" rel="noopener noreferrer"&gt;Reading Ruby 2.6 NEWS File with Pros - Cookpad Developer Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://techlife.cookpad.com/entry/2019/12/25/121834" rel="noopener noreferrer"&gt;Reading Ruby 2.7 NEWS with Pros - Cookpad Developer Blog&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://techlife.cookpad.com/entry/2020/12/25/155741" rel="noopener noreferrer"&gt;Reading Ruby 3.0 NEWS with Pros - Cookpad Developer Blog&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://techlife.cookpad.com/entry/2021/12/25/220002" rel="noopener noreferrer"&gt;Reading Ruby 3.1 NEWS with Pros - Cookpad Developer Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://techlife.cookpad.com/entry/2022/12/26/121950" rel="noopener noreferrer"&gt;Reading Ruby 3.2 NEWS with Pros - Cookpad Developer Blog&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://product.st.inc/entry/2023/12/25/160504" rel="noopener noreferrer"&gt;Reading Ruby 3.3 NEWS with Pros - STORES Product Blog&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://product.st.inc/entry/2024/12/25/154728" rel="noopener noreferrer"&gt;Reading Ruby 3.4 NEWS with Pros - STORES Product Blog&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article not only explains new features but also, within the limits of our memory, includes background and struggles behind the changes.&lt;/p&gt;

&lt;p&gt;Ruby's first release was announced in 1995, so this year marks the 30th anniversary.&lt;br&gt;
That is why we have a commemorative Ruby 4.0.0 release.&lt;/p&gt;

&lt;p&gt;Representative changes in Ruby 4.0 are as follows (excerpted from the release notes).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(Language spec) Logical binary operators can be placed at the beginning of a line&lt;/li&gt;
&lt;li&gt;Experimental introduction of Ruby Box&lt;/li&gt;
&lt;li&gt;Experimental introduction of ZJIT&lt;/li&gt;
&lt;li&gt;Ractor improvements (still experimental)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(Mostly experimental, huh?)&lt;/p&gt;

&lt;p&gt;In this article, we will roughly introduce items in the NEWS file, including these.&lt;/p&gt;
&lt;h2&gt;
  
  
  Language changes
&lt;/h2&gt;
&lt;h3&gt;
  
  
  When calling foo(*nil), it no longer calls nil.to_a
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;*nil&lt;/code&gt; no longer calls &lt;code&gt;nil.to_a&lt;/code&gt;, similar to how &lt;code&gt;**nil&lt;/code&gt; does not call &lt;code&gt;nil.to_hash&lt;/code&gt;.  [&lt;a href="https://bugs.ruby-lang.org/issues/21047" rel="noopener noreferrer"&gt;Feature #21047&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I didn't know this, but &lt;code&gt;foo(*nil)&lt;/code&gt; used to call &lt;code&gt;nil.to_a&lt;/code&gt;. And since that returns &lt;code&gt;[]&lt;/code&gt;, it ultimately means there are no arguments.&lt;/p&gt;

&lt;p&gt;In Ruby 3.3, &lt;code&gt;foo(**nil)&lt;/code&gt; was introduced and it does not call &lt;code&gt;nil.to_hash&lt;/code&gt;, so a proposal was made that &lt;code&gt;nil.to_a&lt;/code&gt; should likewise not be called. It was accepted. This removes one method call and the creation of one empty array, which should be good for performance.&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="nb"&gt;p&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; []&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;nil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="ss"&gt;:to_a&lt;/span&gt;
  &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Ruby 3.4&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; :to_a&lt;/span&gt;
&lt;span class="c1"&gt;# Ruby 4.0&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; nothing is printed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's a compatibility break if you want to call it that, but I want to believe there is no code that depends on it.&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;

&lt;h3&gt;
  
  
  Line breaks before logical operators are now allowed
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Logical binary operators (&lt;code&gt;||&lt;/code&gt;, &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;, &lt;code&gt;and&lt;/code&gt; and &lt;code&gt;or&lt;/code&gt;) at the beginning of a line continue the previous line, like fluent dot. [&lt;a href="https://bugs.ruby-lang.org/issues/20925" rel="noopener noreferrer"&gt;Feature #20925&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When an if condition spans multiple lines, it can be hard to tell the condition from the body.&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;cond1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
  &lt;span class="n"&gt;cond2&lt;/span&gt; &lt;span class="c1"&gt;# part of the condition&lt;/span&gt;
  &lt;span class="n"&gt;body1&lt;/span&gt; &lt;span class="c1"&gt;# first statement inside the if&lt;/span&gt;
  &lt;span class="n"&gt;body2&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this code, &lt;code&gt;cond2&lt;/code&gt; is part of the condition and &lt;code&gt;body1&lt;/code&gt; is the first statement in the if body, but the indentation is the same, so it's hard to distinguish.&lt;/p&gt;

&lt;p&gt;There were tricks to make it clearer, like indenting &lt;code&gt;cond2&lt;/code&gt; more deeply, inserting a blank line between &lt;code&gt;cond2&lt;/code&gt; and &lt;code&gt;body1&lt;/code&gt;, or inserting &lt;code&gt;then&lt;/code&gt;, but all of them felt awkward.&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;# tried using then&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cond1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
  &lt;span class="n"&gt;cond2&lt;/span&gt;
&lt;span class="k"&gt;then&lt;/span&gt;
  &lt;span class="n"&gt;body1&lt;/span&gt;
  &lt;span class="n"&gt;body2&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So in Ruby 4.0.0, you can place a line break before &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; and &lt;code&gt;||&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cond1&lt;/span&gt;
  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;cond2&lt;/span&gt;
  &lt;span class="n"&gt;body1&lt;/span&gt;
  &lt;span class="n"&gt;body2&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;&amp;amp;&amp;amp; cond2&lt;/code&gt;, it's obvious that it's part of the condition, so the problem is solved.&lt;/p&gt;

&lt;p&gt;...Or is it? It feels like we just added one more awkward style, though it might be convenient once you get used to it.&lt;/p&gt;

&lt;h4&gt;
  
  
  Backstory
&lt;/h4&gt;

&lt;p&gt;The discussion around this change got pretty lively.&lt;/p&gt;

&lt;p&gt;In Ruby, whether an expression continues to the next line can generally be determined by the end of the previous line, but the method-call dot was an exception.&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;ary&lt;/span&gt;                       &lt;span class="c1"&gt;# can be a complete expression here&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;it&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="c1"&gt;# next line starts with a dot, so it continues as one expression&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;# convenient for multi-line method chaining&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ary&lt;/code&gt; is a complete expression on its own, but because the next line starts with &lt;code&gt;.map&lt;/code&gt;, the expression continues. This change extends that exception to &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;, &lt;code&gt;||&lt;/code&gt;, &lt;code&gt;and&lt;/code&gt;, and &lt;code&gt;or&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But if we expand this exception, won't people want to break lines before other binary operators too? For example, breaking before &lt;code&gt;+&lt;/code&gt; in &lt;code&gt;x + y&lt;/code&gt;? But that would be incompatible (&lt;code&gt;+ y&lt;/code&gt; can be parsed as the unary &lt;code&gt;+&lt;/code&gt;). Is it worth the risk of making Ruby's line-break rules harder to understand just to allow breaks before &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Some people felt that resistance, but in the end Matz said "do it", so we did.&lt;/p&gt;

&lt;p&gt;As an aside, personally I thought it would look cool to write &lt;code&gt;&amp;amp;&amp;amp; cond2&lt;/code&gt; without indentation, aligned with &lt;code&gt;if cond1&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cond1&lt;/span&gt;
&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;cond2&lt;/span&gt;
  &lt;span class="n"&gt;body1&lt;/span&gt;
  &lt;span class="n"&gt;body2&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 there are basically no supporters of this style (at Ruby developer meetings it was dismissed as "not worth discussing"), which is sad.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h2&gt;
  
  
  Built-in class updates
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Array#rfind was added
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Array#rfind&lt;/code&gt; has been added as a more efficient alternative to &lt;code&gt;array.reverse_each.find&lt;/code&gt; [&lt;a href="https://bugs.ruby-lang.org/issues/21678" rel="noopener noreferrer"&gt;Feature #21678&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Array#find&lt;/code&gt; has been added as a more efficient override of &lt;code&gt;Enumerable#find&lt;/code&gt; [&lt;a href="https://bugs.ruby-lang.org/issues/21678" rel="noopener noreferrer"&gt;Feature #21678&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;Array#rfind&lt;/code&gt; was introduced to find the last element that matches a condition. It iterates in reverse from the last element and returns the first element that matches the condition.&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;# returns 4 instead of 2&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;rfind&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;even?&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, &lt;code&gt;Array#find&lt;/code&gt; was added. There was already &lt;code&gt;Enumerable#find&lt;/code&gt;, but defining a method specialized for &lt;code&gt;Array&lt;/code&gt; makes it faster.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  Binding#implicit_parameters was added
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Binding#implicit_parameters&lt;/code&gt;, &lt;code&gt;Binding#implicit_parameter_get&lt;/code&gt;, and
&lt;code&gt;Binding#implicit_parameter_defined?&lt;/code&gt; have been added to access
numbered parameters and "it" parameter. [&lt;a href="https://bugs.ruby-lang.org/issues/21049" rel="noopener noreferrer"&gt;Bug #21049&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A metaprogramming API to read numbered parameters and the implicit &lt;code&gt;it&lt;/code&gt; from &lt;code&gt;Binding&lt;/code&gt; was added.&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="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"foo"&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="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "foo"&lt;/span&gt;

  &lt;span class="c1"&gt;# read the implicit argument it from the binding&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="nb"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;implicit_parameters&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; [:it]&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="nb"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;implicit_parameter_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:it&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "foo"&lt;/span&gt;
&lt;span class="k"&gt;end&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;42&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="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;_1&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; :foo&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;_2&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 42&lt;/span&gt;

  &lt;span class="c1"&gt;# read implicit arguments _1 and _2 from the binding&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="nb"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;implicit_parameters&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; [:_1, :_2]&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="nb"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;implicit_parameter_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:_1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; :foo&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="nb"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;implicit_parameter_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:_2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 42&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was introduced to display &lt;code&gt;_1&lt;/code&gt; or &lt;code&gt;it&lt;/code&gt; in the debugger, so normally you don't need to use it. Please don't.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Binding#local_variables&lt;/code&gt; does no longer include numbered parameters.
Also, &lt;code&gt;Binding#local_variable_get&lt;/code&gt;, &lt;code&gt;Binding#local_variable_set&lt;/code&gt;, and
&lt;code&gt;Binding#local_variable_defined?&lt;/code&gt; reject to handle numbered parameters.
[&lt;a href="https://bugs.ruby-lang.org/issues/21049" rel="noopener noreferrer"&gt;Bug #21049&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Until Ruby 3.4, you could read numbered parameters with &lt;code&gt;binding.local_variable_get(:_1)&lt;/code&gt;, but in Ruby 4.0 it raises &lt;code&gt;NameError&lt;/code&gt;, so be careful.&lt;/p&gt;

&lt;p&gt;The reason for this change is as follows. The implicit &lt;code&gt;it&lt;/code&gt; introduced in Ruby 3.4 can also be used as a normal local variable name, so &lt;code&gt;binding.local_variable_get(:it)&lt;/code&gt; becomes ambiguous. Along with banning &lt;code&gt;it&lt;/code&gt;, &lt;code&gt;_1&lt;/code&gt; was also banned.&lt;/p&gt;

&lt;p&gt;Since numbered parameters and implicit &lt;code&gt;it&lt;/code&gt; have different scope rules from ordinary local variables, it's arguably not good to handle them together in &lt;code&gt;Binding#local_variables&lt;/code&gt;, and they are now handled by a separate method, &lt;code&gt;implicit_parameters&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  Enumerator.produce now accepts the size keyword
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Enumerator.produce&lt;/code&gt; now accepts an optional &lt;code&gt;size&lt;/code&gt; keyword argument
to specify the size of the enumerator.  It can be an integer,
&lt;code&gt;Float::INFINITY&lt;/code&gt;, a callable object (such as a lambda), or &lt;code&gt;nil&lt;/code&gt; to
indicate unknown size.  When not specified, the size defaults to
&lt;code&gt;Float::INFINITY&lt;/code&gt;. [&lt;a href="https://bugs.ruby-lang.org/issues/21701" rel="noopener noreferrer"&gt;Feature #21701&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A &lt;code&gt;size&lt;/code&gt; keyword was added to &lt;code&gt;Enumerator.produce&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Enumerator.produce&lt;/code&gt; basically creates an infinite &lt;code&gt;Enumerator&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Enumerator equivalent to (1..)&lt;/span&gt;
&lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Enumerator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;produce&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="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; [1, 2, 3, 4, 5]&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;    &lt;span class="c1"&gt;#=&amp;gt; Float::INFINITY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A less-known trick is that you can stop &lt;code&gt;produce&lt;/code&gt; by raising &lt;code&gt;StopIteration&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Enumerator equivalent to (1..10)&lt;/span&gt;
&lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Enumerator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;produce&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="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;StopIteration&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
  &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; Float::INFINITY (?)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In that case, having &lt;code&gt;enum.size&lt;/code&gt; be &lt;code&gt;Float::INFINITY&lt;/code&gt; is not great. So now you can explicitly specify the length with the &lt;code&gt;size&lt;/code&gt; keyword.&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;# Enumerator equivalent to (1..10)&lt;/span&gt;
&lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Enumerator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;produce&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="ss"&gt;size: &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&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;n&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;StopIteration&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
  &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, unless you specify this &lt;code&gt;size&lt;/code&gt; keyword, the &lt;code&gt;Enumerator&lt;/code&gt; returned by &lt;code&gt;produce&lt;/code&gt; will still claim its size is &lt;code&gt;Float::INFINITY&lt;/code&gt;, and if you specify a lie in the &lt;code&gt;size&lt;/code&gt; keyword, it will lie as well. In short, it's best not to trust &lt;code&gt;Enumerator#size&lt;/code&gt; too much.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  error_highlight now shows ArgumentError more helpfully
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;When an &lt;code&gt;ArgumentError&lt;/code&gt; is raised, it now displays code snippets for
both the method call (caller) and the method definition (callee).
[&lt;a href="https://bugs.ruby-lang.org/issues/21543" rel="noopener noreferrer"&gt;Feature #21543&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Consider the following 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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;

&lt;span class="n"&gt;add&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The number of arguments to &lt;code&gt;add&lt;/code&gt; is wrong, so an error is raised. Now it shows code snippets for both the caller and the callee.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# In Ruby 4.0
$ ruby test.rb
test.rb:1:in 'Object#add': wrong number of arguments (given 1, expected 2) (ArgumentError)

    caller: test.rb:3
    | add(1)
      ^^^
    callee: test.rb:1
    | def add(x, y) = x + y
          ^^^
        from test.rb:3:in '&amp;lt;main&amp;gt;'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Ruby 3.4, it was a simple display like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# In Ruby 4.0
$ ruby test.rb
test.rb:1:in 'add': wrong number of arguments (given 1, expected 2) (ArgumentError)
        from test.rb:3:in '&amp;lt;main&amp;gt;'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you see this error, people reflexively look at the source of &lt;code&gt;test.rb:1&lt;/code&gt;. But when you get "wrong number of arguments", you almost never need to fix the method definition side. You end up looking at the error again and reopening the source for &lt;code&gt;test.rb:3&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We felt repeating this work was wasteful, so after proposing and discussing a stack trace improvement, we decided to display it as shown above using error_highlight.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  Changes around &lt;code&gt;Fiber::Scheduler&lt;/code&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Introduce &lt;code&gt;Fiber::Scheduler#fiber_interrupt&lt;/code&gt; to interrupt a fiber with a
given exception. The initial use case is to interrupt a fiber that is
waiting on a blocking IO operation when the IO operation is closed.
[&lt;a href="https://bugs.ruby-lang.org/issues/21166" rel="noopener noreferrer"&gt;Feature #21166&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A method called &lt;code&gt;Fiber::Scheduler#fiber_interrupt&lt;/code&gt; was added.&lt;br&gt;
The story gets fairly specific, but here is the usage scenario.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When thread A is doing &lt;code&gt;io.read&lt;/code&gt; on some &lt;code&gt;io&lt;/code&gt; and thread B tries to &lt;code&gt;io.close&lt;/code&gt;, A can end up waiting forever (depending on the OS), so &lt;code&gt;io.close&lt;/code&gt; raises an exception for the list of threads blocked on that (in this case thread A).&lt;/li&gt;
&lt;li&gt;There was a request to do the same thing in a fiber scheduler, so &lt;code&gt;Fiber::Scheduler#fiber_interrupt&lt;/code&gt; was introduced to make that possible.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I don't know the actual code well enough to write it...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Introduce &lt;code&gt;Fiber::Scheduler#yield&lt;/code&gt; to allow the fiber scheduler to continue processing when signal exceptions are disabled. [&lt;a href="https://bugs.ruby-lang.org/issues/21633" rel="noopener noreferrer"&gt;Bug #21633&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;Fiber::Scheduler#yield&lt;/code&gt; was introduced (when did that happen...).&lt;/p&gt;

&lt;p&gt;After receiving a signal, if it cannot be handled in the normal flow, it schedules other schedulable fibers. I wonder if that's okay.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reintroduce the &lt;code&gt;Fiber::Scheduler#io_close&lt;/code&gt; hook for asynchronous &lt;code&gt;IO#close&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;Fiber::Scheduler#io_close&lt;/code&gt; was reintroduced. It says so, but there's no reference, so I have no idea.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Invoke &lt;code&gt;Fiber::Scheduler#io_write&lt;/code&gt; when flushing the IO write buffer. [&lt;a href="https://bugs.ruby-lang.org/issues/21789" rel="noopener noreferrer"&gt;Bug #21789&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you do &lt;code&gt;IO#close&lt;/code&gt; or &lt;code&gt;IO#fush&lt;/code&gt;, data accumulated in the write buffer is output to the file. Previously this happened without considering the fiber scheduler, but now it is done via the &lt;code&gt;io_write&lt;/code&gt; hook for the fiber scheduler.&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;
&lt;h3&gt;
  
  
  File::Stat#birthtime is now available on Linux
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;File::Stat#birthtime&lt;/code&gt; is now available on Linux via the statx
system call when supported by the kernel and filesystem.
[&lt;a href="https://bugs.ruby-lang.org/issues/21205" rel="noopener noreferrer"&gt;Feature #21205&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It seems you can now get file creation time on Linux.&lt;/p&gt;

&lt;p&gt;This is only available when the &lt;code&gt;statx(2)&lt;/code&gt; system call is available and the filesystem records creation time, but in modern Linux it should usually work (though please research the exact conditions yourself).&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;IO.select&lt;/code&gt; can now accept &lt;code&gt;Float::INFINITY&lt;/code&gt; as a timeout value
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;IO.select&lt;/code&gt; accepts &lt;code&gt;Float::INFINITY&lt;/code&gt; as a timeout argument.
[&lt;a href="https://bugs.ruby-lang.org/issues/20610" rel="noopener noreferrer"&gt;Feature #20610&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;IO.select&lt;/code&gt; is an API for waiting until multiple I/Os are ready (e.g., read won't block). It accepts a timeout, and with &lt;code&gt;nil&lt;/code&gt; (or omitted) it waited indefinitely. This change allows you to specify &lt;code&gt;Float::INFINITY&lt;/code&gt; as that infinite timeout.&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;
&lt;h3&gt;
  
  
  You can now choose which instance variables are shown in inspect
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Kernel#inspect&lt;/code&gt; now checks for the existence of a &lt;code&gt;#instance_variables_to_inspect&lt;/code&gt; method,
allowing control over which instance variables are displayed in the &lt;code&gt;#inspect&lt;/code&gt; string
[&lt;a href="https://bugs.ruby-lang.org/issues/21219" rel="noopener noreferrer"&gt;Feature #21219&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I think you've had the experience of casually &lt;code&gt;p&lt;/code&gt;-ing an object and being startled by a huge output.&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;Foo&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;
    &lt;span class="vi"&gt;@internal_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"a"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="no"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; #&amp;lt;Foo:0x00007c495d699dd0 @internal_data="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want &lt;code&gt;@id&lt;/code&gt; shown but not &lt;code&gt;@internal_data&lt;/code&gt;, a feature was added that lets you specify which instance variables are displayed. By adding an &lt;code&gt;instance_variables_to_inspect&lt;/code&gt; method like this, only &lt;code&gt;@id&lt;/code&gt; will be shown.&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;Foo&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;
    &lt;span class="vi"&gt;@internal_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"a"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# list instance variable names you want displayed as symbols&lt;/span&gt;
  &lt;span class="kp"&gt;private&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;instance_variables_to_inspect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:@id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="no"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; #&amp;lt;Foo:0x00007692c1a44f78 @id="foo"&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's nice and clean.&lt;/p&gt;

&lt;p&gt;You can also use it to exclude things you don't want casually logged, like password strings (this may be the more important use case). For example, to exclude &lt;code&gt;@password&lt;/code&gt;, you can do the following.&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;LoginInfo&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
    &lt;span class="vi"&gt;@password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"secret"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# remove instance variable names you don't want displayed from Kernel#instance_variable&lt;/span&gt;
  &lt;span class="kp"&gt;private&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;instance_variables_to_inspect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;instance_variables&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:@password&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;# @password is not shown&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="no"&gt;LoginInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"mame"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; #&amp;lt;LoginInfo:0x000072b3b2426390 @user="mame"&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mechanically, when &lt;code&gt;Kernel#inspect&lt;/code&gt; stringifies an object, it no longer displays all instance variables unconditionally; it now calls &lt;code&gt;instance_variables_to_inspect&lt;/code&gt; to determine which instance variables should be shown.&lt;/p&gt;

&lt;p&gt;By the way, &lt;code&gt;pp&lt;/code&gt; has long had a similar mechanism called &lt;code&gt;pretty_print_instance_variables&lt;/code&gt;. This was introduced for &lt;code&gt;p&lt;/code&gt; as well, but it would be awkward to introduce it with that name, so it became &lt;code&gt;instance_variables_to_inspect&lt;/code&gt;. As usual, the hardest part was getting the method name to settle (name discussions are very important in Ruby).&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  You can no longer start a process with open("| ls")
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Kernel

&lt;ul&gt;
&lt;li&gt;A deprecated behavior, process creation by &lt;code&gt;Kernel#open&lt;/code&gt; with a
leading &lt;code&gt;|&lt;/code&gt;, was removed. [&lt;a href="https://bugs.ruby-lang.org/issues/19630" rel="noopener noreferrer"&gt;Feature #19630&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;IO

&lt;ul&gt;
&lt;li&gt;A deprecated behavior, process creation by &lt;code&gt;IO&lt;/code&gt; class methods
with a leading &lt;code&gt;|&lt;/code&gt;, was removed. [&lt;a href="https://bugs.ruby-lang.org/issues/19630" rel="noopener noreferrer"&gt;Feature #19630&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Ruby's &lt;code&gt;Kernel#open&lt;/code&gt; is a method to open files, but if you pass it a string starting with a pipe symbol like "| ls", it could start a process and treat its standard I/O as a file.&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;# runs ls /home and prints its output via read (up to Ruby 3.4)&lt;/span&gt;
&lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"| ls /home"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;#=&amp;gt; Ruby 3.4: mame&lt;/span&gt;
  &lt;span class="c1"&gt;#=&amp;gt; Ruby 4.0: No such file or directory @ rb_sysopen - | ls /home (Errno::ENOENT)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Many users didn't know about this feature and it tended to lead to vulnerabilities, so it was deprecated two years ago in Ruby 3.3 and now finally removed.&lt;/p&gt;

&lt;p&gt;There is no drop-in replacement. If you intentionally used this feature, please rewrite appropriately using &lt;code&gt;IO.popen&lt;/code&gt;, etc. In most cases, replacing &lt;code&gt;open("|command")&lt;/code&gt; with &lt;code&gt;IO.popen("command")&lt;/code&gt; should work (but it is a dangerous feature, so please consider carefully).&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Math.log1p&lt;/code&gt; and &lt;code&gt;Math.expm1&lt;/code&gt; were added
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Math.log1p&lt;/code&gt; and &lt;code&gt;Math.expm1&lt;/code&gt; are added. [&lt;a href="https://bugs.ruby-lang.org/issues/21527" rel="noopener noreferrer"&gt;Feature #21527&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;Math.log1p(x)&lt;/code&gt; returns the value of $\log(x + 1)$.&lt;/p&gt;

&lt;p&gt;You might wonder, "Why does this exist? Isn't it enough to write &lt;code&gt;Math.log(x + 1.0)&lt;/code&gt;?" But apparently it is needed to avoid floating-point errors.&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;x&lt;/code&gt; is very small (smaller than &lt;code&gt;Float::EPSILON&lt;/code&gt;), &lt;code&gt;x + 1.0&lt;/code&gt; is rounded to &lt;code&gt;1.0&lt;/code&gt;, so &lt;code&gt;Math.log(x + 1.0)&lt;/code&gt; always returns &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Math.log1p(x)&lt;/code&gt; works even for such small &lt;code&gt;x&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# very small x&lt;/span&gt;
&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0e-16&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="no"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 0.0&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="no"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log1p&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;#=&amp;gt; 1.0e-16&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, $\log(x)$ is approximately $x$ near $x = 1$, so it's essentially just returning the argument.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Math.expm1&lt;/code&gt; returns $\exp(x) - 1$, and the need is basically the same.&lt;/p&gt;

&lt;p&gt;The proposal started when I was implementing a numerical analysis paper as a hobby and noticed there was no &lt;code&gt;log1p&lt;/code&gt;. I was surprised no one had noticed that a function provided by C since the C99 era was missing.&lt;/p&gt;

&lt;p&gt;During the discussion for introduction, someone quipped "&lt;code&gt;log1p&lt;/code&gt; and &lt;code&gt;expm1&lt;/code&gt; are meaningless names", but I found that Python, Java, JavaScript, Go, PHP, .NET, R, MATLAB, Kotlin, Swift, Julia, Haskell, OCaml, and Crystal all use this name (Rust uses the even more cryptic &lt;code&gt;ln_1p&lt;/code&gt;). So we decided to keep the names as-is.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  Pathname became a core class
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Pathname has been promoted from a default gem to a core class of Ruby.
[&lt;a href="https://bugs.ruby-lang.org/issues/17473" rel="noopener noreferrer"&gt;Feature #17473&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;Pathname&lt;/code&gt; class is now built-in. You can use Pathname without writing &lt;code&gt;require "pathname"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, Rails already does &lt;code&gt;require "pathname"&lt;/code&gt; by default, so for most users this likely changes little. For people who often write throwaway scripts or one-liners in Ruby, it might be a small but nice improvement.&lt;/p&gt;

&lt;p&gt;Note that you still need &lt;code&gt;require "pathname"&lt;/code&gt; to use these three methods.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Pathname#find&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Pathname#rmtree&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Pathname#mktmpdir&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is because these methods depend on other libraries. It would be too much to make &lt;code&gt;find&lt;/code&gt;, &lt;code&gt;fileutils&lt;/code&gt;, and &lt;code&gt;tmpdir&lt;/code&gt; built-ins just to make &lt;code&gt;Pathname&lt;/code&gt; built-in, so these were not included.&lt;/p&gt;

&lt;p&gt;As an aside, a very strange bug occurred during making &lt;code&gt;Pathname&lt;/code&gt; built-in. I explained the debugging process in detail in another article, so if you're interested, please take a look.&lt;/p&gt;

&lt;p&gt;[&lt;a href="https://product.st.inc/entry/2025/07/17/104509:embed:cite" rel="noopener noreferrer"&gt;https://product.st.inc/entry/2025/07/17/104509:embed:cite&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  The implicit &lt;code&gt;it&lt;/code&gt; return value in &lt;code&gt;Proc#parameters&lt;/code&gt; changed slightly
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Proc#parameters&lt;/code&gt; now shows anonymous optional parameters as &lt;code&gt;[:opt]&lt;/code&gt;
instead of &lt;code&gt;[:opt, nil]&lt;/code&gt;, making the output consistent with when the
anonymous parameter is required. [&lt;a href="https://bugs.ruby-lang.org/issues/20974" rel="noopener noreferrer"&gt;Bug #20974&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;Proc#parameters&lt;/code&gt; is a method to inspect what arguments a block takes.&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="nb"&gt;p&lt;/span&gt; &lt;span class="nb"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;{}.&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; []&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="nb"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; [[:opt, :a]]&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="nb"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; [[:opt, :a], [:opt, :b]]&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="nb"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; [[:opt, :a], [:opt, :b], [:opt, :c]]&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="nb"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; [[:opt, :a], [:opt, :b], [:opt, :c], [:rest, :d]]&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="nb"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; [[:opt, :a], [:opt, :b], [:opt, :c], [:rest, :d], [:opt, :e]]&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;{}.&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; []&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; [[:req, :a]]&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; [[:req, :a], [:req, :b]]&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; [[:req, :a], [:req, :b], [:req, :c]]&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; [[:req, :a], [:req, :b], [:req, :c], [:rest, :d]]&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; [[:req, :a], [:req, :b], [:req, :c], [:rest, :d], [:req, :e]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All the &lt;code&gt;proc{|a|}&lt;/code&gt; cases use &lt;code&gt;[:opt, :a]&lt;/code&gt; (i.e., optional arguments) because block arguments can be passed or omitted. With &lt;code&gt;lambda{|a|}&lt;/code&gt;, you must pass one argument or you'll get an error, so it becomes &lt;code&gt;[:req, :a]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, blocks using &lt;code&gt;it&lt;/code&gt; introduced in Ruby 3.4 are like &lt;code&gt;proc{|a|}&lt;/code&gt; in that they receive one argument, which means one optional argument. However, you can also explicitly name an argument &lt;code&gt;it&lt;/code&gt; (e.g., &lt;code&gt;proc{|it|p it}.parameters #=&amp;gt; [[:opt, :it]]&lt;/code&gt;), so returning &lt;code&gt;it&lt;/code&gt; as a name is ambiguous. That's why we previously used &lt;code&gt;nil&lt;/code&gt; for the name.&lt;/p&gt;

&lt;p&gt;But for &lt;code&gt;lambda&lt;/code&gt;, the parameter is required, and the name-less form is just &lt;code&gt;[:req]&lt;/code&gt;. So we decided to make them consistent and use &lt;code&gt;[:opt]&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; [[:req]]&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="nb"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; Ruby 3.4: [[:opt, nil]]&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; Ruby 4.0: [[:opt]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By the way, for numbered parameters, the variable names like &lt;code&gt;:_1&lt;/code&gt; still appear as before. Right now &lt;code&gt;_1&lt;/code&gt; cannot be used as a normal parameter name (or variable name), so it's not confusing.&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="nb"&gt;p&lt;/span&gt; &lt;span class="nb"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_2&lt;/span&gt;&lt;span class="p"&gt;]}.&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt;
&lt;span class="n"&gt;pt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&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;:opt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:_2&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_2&lt;/span&gt;&lt;span class="p"&gt;]}.&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; [[:req, :_1], [:req, :_2]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(ko1)&lt;/p&gt;

&lt;h3&gt;
  
  
  Ractor::Port was introduced to reorganize Ractor communication
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Ractor::Port&lt;/code&gt; class was added for a new synchronization mechanism
to communicate between Ractors. [&lt;a href="https://bugs.ruby-lang.org/issues/21262" rel="noopener noreferrer"&gt;Feature #21262&lt;/a&gt;]
&lt;code&gt;Ractor::Port&lt;/code&gt; provides the following methods:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Ractor::Port#receive&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ractor::Port#send&lt;/code&gt; (or &lt;code&gt;Ractor::Port#&amp;lt;&amp;lt;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ractor::Port#close&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ractor::Port#close&lt;/code&gt;  As result, &lt;code&gt;Ractor.yield&lt;/code&gt; and &lt;code&gt;Ractor#take&lt;/code&gt; were removed.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;Ractor#join&lt;/code&gt; and &lt;code&gt;Ractor#value&lt;/code&gt; were added to wait for the termination of a Ractor. These are similar to &lt;code&gt;Thread#join&lt;/code&gt; and &lt;code&gt;Thread#value&lt;/code&gt;.&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;Ractor#monitor&lt;/code&gt; and &lt;code&gt;Ractor#unmonitor&lt;/code&gt; were added as low-level interfaces used internally to implement &lt;code&gt;Ractor#join&lt;/code&gt;.&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;Ractor.select&lt;/code&gt; now only accepts Ractors and Ports. If Ractors are given, it returns when a Ractor terminates.&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;Ractor#default_port&lt;/code&gt; was added. Each &lt;code&gt;Ractor&lt;/code&gt; has a default port, which is used by &lt;code&gt;Ractor.send&lt;/code&gt;, &lt;code&gt;Ractor.receive&lt;/code&gt;.&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;Ractor#close_incoming&lt;/code&gt; and &lt;code&gt;Ractor#close_outgoing&lt;/code&gt; were removed.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Ractor communication was reorganized around a new thing called &lt;code&gt;Ractor::Port&lt;/code&gt;. This is summarized in the following article, so please read it for details.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/ko1/ractorport-revamping-the-ractor-api-98"&gt;https://dev.to/ko1/ractorport-revamping-the-ractor-api-98&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's an example from NEWS. You can now write it 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;port1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;port2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="n"&gt;port1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port2&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;port1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port2&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;port1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="n"&gt;port2&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;
  &lt;span class="n"&gt;port1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="n"&gt;port2&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;port1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 1, 2&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;port2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 11, 12&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, to wait for a Ractor to finish, you should use &lt;code&gt;join&lt;/code&gt; or &lt;code&gt;value&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;    &lt;span class="c1"&gt;# wait for task to finish&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt; &lt;span class="c1"&gt;# wait for task to finish and get its return value; basically the same as threads&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The removal of &lt;code&gt;Ractor#take&lt;/code&gt;, which was used for waiting, is a big change (Ractor itself is an experimental feature, so it can change more easily). It brings the API closer to threads.&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;

&lt;h3&gt;
  
  
  Introducing &lt;code&gt;Ractor.shareable_proc&lt;/code&gt; to create Proc objects shareable between Ractors
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Ractor.shareable_proc&lt;/code&gt; and &lt;code&gt;Ractor.shareable_lambda&lt;/code&gt; is introduced to make shareable Proc or lambda.
[&lt;a href="https://bugs.ruby-lang.org/issues/21550" rel="noopener noreferrer"&gt;Feature #21550&lt;/a&gt;], [&lt;a href="https://bugs.ruby-lang.org/issues/21557" rel="noopener noreferrer"&gt;Feature #21557&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To make a Proc shareable, there are some constraints. The strictest constraint is that &lt;code&gt;self&lt;/code&gt; while executing the block must be shareable (other constraints below). Previously, by using &lt;code&gt;Ractor.make_shareable(proc_obj)&lt;/code&gt;, you could make &lt;code&gt;proc_obj&lt;/code&gt; shareable only when &lt;code&gt;self&lt;/code&gt; was shareable and the other constraints were satisfied.&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;pr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;make_shareable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; 'Racr.make_shareable': Proc's self is not shareable: #&amp;lt;Proc:...&amp;gt; (Ractor::IsolationError)&lt;/span&gt;
&lt;span class="c1"&gt;# because main is not shareable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Making &lt;code&gt;self&lt;/code&gt; shareable was annoying and required doing something like &lt;code&gt;nil.instance_exec{ proc{ ... } }&lt;/code&gt;, which was uncool. To solve that, &lt;code&gt;Ractor.shareable_proc&lt;/code&gt; was introduced (and &lt;code&gt;shareable_lambda&lt;/code&gt; creates a lambda). This method returns the given block as a shareable Proc.&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;pr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shareable_proc&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shareable?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, if you don't specify anything, &lt;code&gt;self&lt;/code&gt; becomes &lt;code&gt;nil&lt;/code&gt;. To change it, use the &lt;code&gt;self:&lt;/code&gt; keyword.&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;pr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shareable_proc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;self: &lt;/span&gt;&lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;pr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; Ractor&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In most cases it will be used without &lt;code&gt;self:&lt;/code&gt;, so cases where you must specify &lt;code&gt;self:&lt;/code&gt; should be rare. Procs shared between Ractors are likely to be used to pass tasks, 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;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&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;while&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&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;def&lt;/span&gt; &lt;span class="nf"&gt;heavy_task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# assign three heavy_task runs to the Ractor&lt;/span&gt;
&lt;span class="n"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shareable_proc&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;heavy_task&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shareable_proc&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;heavy_task&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shareable_proc&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;heavy_task&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So if you felt that it was hard to make shareable Procs, this API solves that concern.&lt;/p&gt;

&lt;p&gt;Supplement: Conditions for a Proc to become shareable:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;(dynamic) &lt;code&gt;self&lt;/code&gt; is shareable&lt;/li&gt;
&lt;li&gt;(static) it does not write to outer local variables&lt;/li&gt;
&lt;li&gt;(dynamic) when reading outer local variables, those locals contain shareable objects. The list of outer locals read is determined statically&lt;/li&gt;
&lt;li&gt;(static) when reading outer local variables, those locals are not reassigned&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When I say static and dynamic here, static means checks done at compile time, and dynamic means checks done at runtime (when the Proc is created). Note that if you &lt;code&gt;eval&lt;/code&gt; inside a shareable Proc, it checks that these conditions are satisfied.&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;C&lt;/span&gt;
  &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="n"&gt;pr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;make_shareable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;#=&amp;gt; 'Ractor.make_shareable': can not make a Proc shareable because it accesses outer variables (a). (ArgumentError)&lt;/span&gt;
  &lt;span class="c1"&gt;# error because it writes to outer local variable a&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Condition 4 is tricky, so here is more explanation.&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;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;pr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shareable_proc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;self: &lt;/span&gt;&lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; cannot make a shareable Proc because the outer variable 'a' may be reassigned. (Ractor::IsolationError)&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, &lt;code&gt;a&lt;/code&gt; referenced by the Proc is reassigned outside the block. This check is needed to keep behavior as close as possible to a "normal Proc". A shareable Proc records the value of the variable at the time it is created, in this case &lt;code&gt;1&lt;/code&gt; for &lt;code&gt;a&lt;/code&gt;. So even if you do &lt;code&gt;a = 2&lt;/code&gt; outside, &lt;code&gt;a&lt;/code&gt; inside the shareable Proc will always return &lt;code&gt;1&lt;/code&gt;. This differs from the behavior of a normal Proc, so starting in Ruby 4.0 such cases are now errors.&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Range#to_set&lt;/code&gt; now checks size
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Range#to_set&lt;/code&gt; now performs size checks to prevent issues with endless ranges. [&lt;a href="https://bugs.ruby-lang.org/issues/21654" rel="noopener noreferrer"&gt;Bug #21654&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you do &lt;code&gt;(0..).to_set&lt;/code&gt;, Ruby 3.4 tried to create an infinite-size &lt;code&gt;Set&lt;/code&gt; and hung, but Ruby 4.0 now raises an exception.&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="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_set&lt;/span&gt;
  &lt;span class="c1"&gt;#=&amp;gt; Ruby 3.4: never ends until it eats all memory&lt;/span&gt;
  &lt;span class="c1"&gt;#=&amp;gt; Ruby 4.0: cannot convert endless range to a set (RangeError)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  The fine-grained behavior of &lt;code&gt;Range#overlap?&lt;/code&gt; changed
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Range#overlap?&lt;/code&gt; now correctly handles infinite (unbounded) ranges.
[&lt;a href="https://bugs.ruby-lang.org/issues/21185" rel="noopener noreferrer"&gt;Bug #21185&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The return value of &lt;code&gt;(..3).overlap?(nil..nil)&lt;/code&gt; changed from &lt;code&gt;false&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  The fine-grained behavior of &lt;code&gt;Range#max&lt;/code&gt; changed
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Range#max&lt;/code&gt; behavior on beginless ints been fixed.
[&lt;a href="https://bugs.ruby-lang.org/issues/21174" rel="noopener noreferrer"&gt;Bug #21174&lt;/a&gt;] [&lt;a href="https://bugs.ruby-lang.org/issues/21175" rel="noopener noreferrer"&gt;Bug #21175&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;(..10).max(2)&lt;/code&gt; now returns &lt;code&gt;[10, 9]&lt;/code&gt;. &lt;code&gt;(1..10).max(2)&lt;/code&gt; has long returned &lt;code&gt;[10, 9]&lt;/code&gt;, so this aligns it.&lt;/p&gt;

&lt;p&gt;That said, &lt;code&gt;(1.0 .. 10).max(2)&lt;/code&gt; raises an exception, which feels a bit unclear to me personally.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;::Ruby&lt;/code&gt; was defined
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A new toplevel module as been defined, which contains
Ruby-related constants. This module was reserved in Ruby 3.4
and is now officially defined. [&lt;a href="https://bugs.ruby-lang.org/issues/20884" rel="noopener noreferrer"&gt;Feature #20884&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;::Ruby&lt;/code&gt; module was defined (in Ruby 3.4, you would get a warning if you tried to define a top-level Ruby constant). What exists now is that constants like &lt;code&gt;RUBY_VERSION&lt;/code&gt; are defined there as well. Constants like &lt;code&gt;RUBY_VERSION&lt;/code&gt; still remain as they were.&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="nb"&gt;p&lt;/span&gt; &lt;span class="no"&gt;Ruby&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;VERSION&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "4.0.0"&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="no"&gt;RUBY_VERSION&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; same as "4.0.0"&lt;/span&gt;

&lt;span class="n"&gt;pp&lt;/span&gt; &lt;span class="no"&gt;Ruby&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;constants&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:REVISION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="ss"&gt;:COPYRIGHT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="ss"&gt;:ENGINE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="ss"&gt;:ENGINE_VERSION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="ss"&gt;:VERSION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="ss"&gt;:RELEASE_DATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="ss"&gt;:DESCRIPTION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="ss"&gt;:PLATFORM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="ss"&gt;:PATCHLEVEL&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Will there be more and more &lt;code&gt;Ruby::...&lt;/code&gt; in the future?&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Ruby::Box&lt;/code&gt; was introduced
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A new (experimental) feature to provide separation about definitions.
For the detail of "Ruby Box", see &lt;a href="https://storesinc/doc/language/box.md" rel="noopener noreferrer"&gt;doc/language/box.md&lt;/a&gt;.
[&lt;a href="https://bugs.ruby-lang.org/issues/21311" rel="noopener noreferrer"&gt;Feature #21311&lt;/a&gt;] [&lt;a href="https://bugs.ruby-lang.org/issues/21385" rel="noopener noreferrer"&gt;Misc #21385&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ruby 4.0.0's headline feature, &lt;code&gt;Ruby::Box&lt;/code&gt;, was introduced experimentally.&lt;/p&gt;

&lt;p&gt;We wrote a dedicated article about it, so please see that.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/ko1/rubybox-digest-introduction-ruby-400-new-feature-3bch"&gt;https://dev.to/ko1/rubybox-digest-introduction-ruby-400-new-feature-3bch&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Set&lt;/code&gt; became a core class
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Set&lt;/code&gt; is now a core class, instead of an autoloaded stdlib class.
[&lt;a href="https://bugs.ruby-lang.org/issues/21216" rel="noopener noreferrer"&gt;Feature #21216&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;Set&lt;/code&gt; is now a core class. It was rewritten in C, which makes some methods faster, and its compatibility with other built-in features has subtly improved. Since &lt;code&gt;Set&lt;/code&gt; already had &lt;code&gt;autoload&lt;/code&gt; set up, visible behavior changes for users are likely small, probably, hopefully.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Set#inspect&lt;/code&gt; now returns a string suitable for &lt;code&gt;eval&lt;/code&gt;, using the
&lt;code&gt;Set[]&lt;/code&gt; syntax (e.g., &lt;code&gt;Set[1, 2, 3]&lt;/code&gt; instead of
&lt;code&gt;#&amp;lt;Set: {1, 2, 3}&amp;gt;&lt;/code&gt;). This makes it consistent with other core
collection classes like Array and Hash. [&lt;a href="https://bugs.ruby-lang.org/issues/21389" rel="noopener noreferrer"&gt;Feature #21389&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The clearest change is the &lt;code&gt;Set#inspect&lt;/code&gt; result.&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="nb"&gt;p&lt;/span&gt; &lt;span class="no"&gt;Set&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="c1"&gt;#=&amp;gt; Ruby 3.4: #&amp;lt;Set: {1, 2, 3}&amp;gt;&lt;/span&gt;
  &lt;span class="c1"&gt;#=&amp;gt; Ruby 4.0: Set[1, 2, 3]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Array&lt;/code&gt; and &lt;code&gt;Hash&lt;/code&gt; show their literal forms, so &lt;code&gt;Set&lt;/code&gt; now also displays using the normal literal-like syntax for creating it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Passing arguments to &lt;code&gt;Set#to_set&lt;/code&gt; and &lt;code&gt;Enumerable#to_set&lt;/code&gt; is now deprecated.
[&lt;a href="https://bugs.ruby-lang.org/issues/21390" rel="noopener noreferrer"&gt;Feature #21390&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A subtle incompatibility is that arguments to &lt;code&gt;to_set&lt;/code&gt; were removed. This was a feature where you could pass a subclass of &lt;code&gt;Set&lt;/code&gt; as an argument to specify the class of the generated object.&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;MySet&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Setend&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_set&lt;/span&gt;        &lt;span class="c1"&gt;#=&amp;gt; #&amp;lt;Set: {1, 2, 3}&amp;gt;&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;MySet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; #&amp;lt;MySet: {1, 2, 3}&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Personally, I think you shouldn't inherit from core classes.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  You can now specify connection timeouts for Socket.tcp and TCPSocket.new
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Socket.tcp&lt;/code&gt; &amp;amp; &lt;code&gt;TCPSocket.new&lt;/code&gt; accepts an &lt;code&gt;open_timeout&lt;/code&gt; keyword argument to specify
the timeout for the initial connection. [&lt;a href="https://bugs.ruby-lang.org/issues/21347" rel="noopener noreferrer"&gt;Feature #21347&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;Socket.tcp&lt;/code&gt; and &lt;code&gt;TCPSocket.new&lt;/code&gt; would block until the OS-defined timeout if a connection could not be made for some reason.&lt;/p&gt;

&lt;p&gt;This timeout is about 127 seconds on Linux by default.&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;# raises after blocking for about 127 seconds (203.0.113.1 is a test address)&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="no"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"203.0.113.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;#=&amp;gt; Connection timed out - connect(2) for 203.0.113.1:80 (Errno::ETIMEDOUT)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is very slow, so in practical applications it was essentially mandatory to wrap the connection with &lt;code&gt;Timeout.timeout&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In Ruby 4.0, you can specify the timeout by passing the &lt;code&gt;open_timeout&lt;/code&gt; keyword argument to &lt;code&gt;Socket.tcp&lt;/code&gt; and &lt;code&gt;TCPServer.new&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# raises after blocking for 1 second&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="no"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"203.0.113.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;open_timeout: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;#=&amp;gt; Connection timed out - user specified timeout for 203.0.113.1:80 (Errno::ETIMEDOUT)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There were already &lt;code&gt;resolv_timeout&lt;/code&gt; and &lt;code&gt;connect_timeout&lt;/code&gt; keyword arguments for name resolution and connection timeouts. The difference is that &lt;code&gt;open_timeout&lt;/code&gt; specifies the overall timeout from name resolution through connection.&lt;/p&gt;

&lt;p&gt;With &lt;a href="https://product.st.inc/entry/2024/12/25/154728#Happy-Eyeballs-v2-%E3%81%8C%E5%AE%9F%E8%A3%85%E3%81%95%E3%82%8C%E3%81%9F" rel="noopener noreferrer"&gt;Happy Eyeballs v2&lt;/a&gt; introduced in Ruby 3.4, name resolution and connection are partially parallelized, which made it harder to understand, so the more convenient overall timeout &lt;code&gt;open_timeout&lt;/code&gt; was introduced.&lt;/p&gt;

&lt;p&gt;It seems net-http quickly switched to using &lt;code&gt;open_timeout&lt;/code&gt; (when available) instead of &lt;code&gt;Timeout.timeout&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Completely as an aside, Linux's default of 127 seconds is because it starts at 1 second and performs exponential backoff retransmissions six times (1 + 2 + 4 + 8 + 16 + 32 + 64 = 127). You can change the retry count by changing &lt;code&gt;net.ipv4.syn_retries = 6&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  Exceptions raised by Socket.tcp and TCPSocket.new changed
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;When a user-specified timeout occurred in &lt;code&gt;TCPSocket.new&lt;/code&gt;, either &lt;code&gt;Errno::ETIMEDOUT&lt;/code&gt;
or &lt;code&gt;IO::TimeoutError&lt;/code&gt; could previously be raised depending on the situation.
This behavior has been unified so that &lt;code&gt;IO::TimeoutError&lt;/code&gt; is now consistently raised.
(Please note that, in &lt;code&gt;Socket.tcp&lt;/code&gt;, there are still cases where &lt;code&gt;o::ETIMEDOUT&lt;/code&gt;
may be raised in similar situations, and that in both cases &lt;code&gt;Errno::ETIMEDOUT&lt;/code&gt; may be
raised when the timeout occurs at the OS level.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To get to the conclusion first: these methods may raise &lt;code&gt;Errno::ETIMEDOUT&lt;/code&gt; or &lt;code&gt;IO::TimeoutError&lt;/code&gt; on timeout, so if you rescue, please rescue both.&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;begin&lt;/span&gt;
  &lt;span class="no"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;Errno&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ETIMEDOUT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TimeoutError&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Errno::E*&lt;/code&gt; follows the principle of system call failures. Following that principle, when the &lt;code&gt;connect&lt;/code&gt; system call fails due to a timeout, &lt;code&gt;Errno::ETIMEDOUT&lt;/code&gt; is raised. On the other hand, the timeout specified by the user with &lt;code&gt;open_timeout&lt;/code&gt; is not a system call failure, so &lt;code&gt;IO::TimeoutError&lt;/code&gt; is raised.&lt;/p&gt;

&lt;p&gt;Previously, user-specified timeouts sometimes raised &lt;code&gt;Errno::ETIMEDOUT&lt;/code&gt; as well, but it seems it was aligned to &lt;code&gt;IO::TimeoutError&lt;/code&gt; to follow the principle.&lt;/p&gt;

&lt;p&gt;It is inconvenient to have to rescue both, and we'd like to do something about it, but we noticed the issue fairly close to release (it was discovered while writing this NEWS commentary article), so let's hope for the future.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  Updated to Unicode 17.0.0
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Update Unicode to Version 17.0.0 and Emoji Version 17.0.
[&lt;a href="https://bugs.ruby-lang.org/issues/19908" rel="noopener noreferrer"&gt;Feature #19908&lt;/a&gt;][[Feature #://bugs.ruby-lang.org/issues/20724)][&lt;a href="https://bugs.ruby-lang.org/issues/21275" rel="noopener noreferrer"&gt;Feature #21275&lt;/a&gt;] (also applies to Regexp)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We updated Ruby's Unicode version from 15.0.0 to 17.0.0. There are no major changes. It includes support for new characters, adding new Unicode properties, and updating the rules for counting grapheme clusters in Indic scripts.&lt;/p&gt;

&lt;p&gt;In Ruby regexps, you can match using Unicode properties.&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="s1"&gt;'a'&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="sr"&gt;/\p{Hiragana}/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; false&lt;/span&gt;
&lt;span class="s1"&gt;'あ'&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="sr"&gt;/\p{Hiragana}/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can check what a property like Hiragana matches on this site. It seems it also matches the character &lt;code&gt;🈀&lt;/code&gt;. Properties can match characters that differ from your mental image, so if you need strict matching for a specific set of characters, it's better not to use them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://util.unicode.org/UnicodeJsps/regex.jsp?a=%5Cp%7BHiragana%7D&amp;amp;b=%E3%81%82a" rel="noopener noreferrer"&gt;https://util.unicode.org/UnicodeJsps/regex.jsp?a=\p{Hiragana}&amp;amp;b=あa&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When updating Unicode versions, we also update these properties. It's automated, so it is easy to add by just running a script.&lt;/p&gt;

&lt;p&gt;Here is the list of Unicode properties available in Ruby.&lt;/p&gt;

&lt;p&gt;[&lt;a href="https://docs.ruby-lang.org/en/master/language/regexp/unicode_properties_rdoc.html:embed:cite" rel="noopener noreferrer"&gt;https://docs.ruby-lang.org/en/master/language/regexp/unicode_properties_rdoc.html:embed:cite&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;Here are the Unicode release notes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[&lt;a href="https://www.unicode.org/versions/Unicode15.1.0/:title" rel="noopener noreferrer"&gt;https://www.unicode.org/versions/Unicode15.1.0/:title&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;[&lt;a href="https://www.unicode.org/versions/Unicode16.0.0/:title" rel="noopener noreferrer"&gt;https://www.unicode.org/versions/Unicode16.0.0/:title&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;[&lt;a href="https://www.unicode.org/versions/Unicode17.0.0/:title" rel="noopener noreferrer"&gt;https://www.unicode.org/versions/Unicode17.0.0/:title&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(ima1zumi)&lt;/p&gt;

&lt;h3&gt;
  
  
  String#strip can now take a set of characters to remove
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;String#strip&lt;/code&gt;, &lt;code&gt;strip!&lt;/code&gt;, &lt;code&gt;lstrip&lt;/code&gt;, &lt;code&gt;lstrip!&lt;/code&gt;, &lt;code&gt;rstrip&lt;/code&gt;, and &lt;code&gt;rstrip!&lt;/code&gt;
are extended to accept &lt;code&gt;selectors&lt;/code&gt; arguments. [[Feature #21552]]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;String#strip&lt;/code&gt; removes whitespace from both ends of a string, but now you can pass an optional argument specifying the set of characters to remove. By passing multiple characters, they are treated as a set of characters to remove (same as &lt;code&gt;String#tr&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="s1"&gt;' str_!'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'!_'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; " str"&lt;/span&gt;
&lt;span class="c1"&gt;#   leading space is not removed, and trailing _ and ! are removed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also specify ranges like 'x-y' (also like &lt;code&gt;String#tr&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="s1"&gt;'str345'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'0-9'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; "str" # remove trailing 0-9 characters&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I asked if passing a string as a character set rather than a substring to remove might be confused with the behavior of &lt;code&gt;String#delete_suffix&lt;/code&gt; (for example, &lt;code&gt;'bar'.strip('rab')&lt;/code&gt; removes everything), and was told, "You'll know if you try it." Dismissed.&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Thread#raise&lt;/code&gt;/&lt;code&gt;Fiber#raise&lt;/code&gt; can now take the &lt;code&gt;cause:&lt;/code&gt; keyword
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Thread

&lt;ul&gt;
&lt;li&gt;Introduce support for &lt;code&gt;Thse:)&lt;/code&gt; argument similar to
&lt;code&gt;Kernel#raise&lt;/code&gt;. [&lt;a href="https://bugs.ruby-lang.org/issues/21360" rel="noopener noreferrer"&gt;Feature #21360&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Fiber

&lt;ul&gt;
&lt;li&gt;Introduce support for &lt;code&gt;Fiber#raise(cause:)&lt;/code&gt; argument similar to
&lt;code&gt;Kernel#raise&lt;/code&gt;. [&lt;a href="https://bugs.ruby-lang.org/issues/21360" rel="noopener noreferrer"&gt;Feature #21360&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Like &lt;code&gt;Kernel#raise(cause: exc)&lt;/code&gt;, the &lt;code&gt;cause:&lt;/code&gt; argument can specify another exception that caused this exception (default &lt;code&gt;$!&lt;/code&gt;). Now you can specify &lt;code&gt;cause:&lt;/code&gt; in &lt;code&gt;Thread#raise&lt;/code&gt; as well, but if you do so it might lead to confusing situations (for example, sending an exception that occurred in the current thread to another thread), so use with care.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Fiber#raise(cause:)&lt;/code&gt; is the same story as &lt;code&gt;Thread#raise(cause:)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;

&lt;h2&gt;
  
  
  Stdlib updates
&lt;/h2&gt;

&lt;p&gt;We only list stdlib changes that are notable feature changes.&lt;/p&gt;

&lt;p&gt;Other changes are listed in the following sections. We also listed release&lt;br&gt;
history from the previous bundled version that is Ruby 3.4.0 if it has GitHub&lt;br&gt;
releases.&lt;/p&gt;

&lt;p&gt;6.3&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;0.6.1 to &lt;a href="https://github.com/ruby/ostruct/releases/tag/v0.6.2" rel="noopener noreferrer"&gt;v0.6.2&lt;/a&gt;, &lt;a href="https://github.com/ruby/ostruct/releases/tag/v0.6.3" rel="noopener noreferrer"&gt;v0.6.3&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;pstore 0.2.0&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;0.1.4 to &lt;a href="https://github.com/ruby/pstore/releases/tag/v0.2.0" rel="noopener noreferrer"&gt;v0.2.0&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;benchmark 0.5.0&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;0.4.0 to &lt;a href="https://gitb.com/ruby/benchmark/releases/tag/v0.4.1" rel="noopener noreferrer"&gt;v0.4.1&lt;/a&gt;, &lt;a href="https://github.com/ruby/benchmark/releases/tag/v0.5.0" rel="noopener noreferrer"&gt;v0.5.0&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;logger 1.7.0&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;1.6.4 to &lt;a href="https://github.com/ruby/logger/releases/tag/v1.6.5" rel="noopener noreferrer"&gt;v1.6.5&lt;/a&gt;, &lt;a href="https://github.com/ruby/logger/releases/tag/v1.6.6" rel="noopener noreferrer"&gt;v1.6.6&lt;/a&gt;, &lt;a href="https://github.com/ruby/logger/releases/tag/v1.7.0" rel="noopener noreferrer"&gt;v1.7.0&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;rdoc 7.0.3&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;6.14.0 to &lt;a href="https://github.com/ruby/rdoc/releases/tag/v6.14.1" rel="noopener noreferrer"&gt;v6.14.1&lt;/a&gt;, &lt;a href="https://github.com/ruby/rdoc/releases/tag/v6.14.2" rel="noopener noreferrer"&gt;v6.14.2&lt;/a&gt;, &lt;a href="https://github.com/ruby/rdoc/releases/tag/v6.15.0" rel="noopener noreferrer"&gt;v6.15.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/rdoc/releases/tag/v6.15.1" rel="noopener noreferrer"&gt;v6.15.1&lt;/a&gt;, &lt;a href="https://github.com/ruby/rdoc/releases/tag/v6.16.0" rel="noopener noreferrer"&gt;v6.16.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/rdoc/releases/tag/v6.16.1" rel="noopener noreferrer"&gt;v6.16.1&lt;/a&gt;, &lt;a href="https://github.com/ruby/rdoc/releases/tag/v6.17.0" rel="noopener noreferrer"&gt;v6.17.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/rdoc/releases/tag/v7.0.0" rel="noopener noreferrer"&gt;v7.0.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/rdoc/releases/tag/v7.0.1" rel="noopener noreferrer"&gt;v7.0.1&lt;/a&gt;, &lt;a href="https://github.com/ruby/rdoc/releases/tag/v7.0.2" rel="noopener noreferrer"&gt;v7.0.2&lt;/a&gt;, &lt;a href="https://github.com/ruby/rdoc/releases/tag/v7.0.3" rel="noopener noreferrer"&gt;v7.0.3&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;win32ole 1.9.2&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;1.9.1 to &lt;a href="https://github.com/ruby/win32ole/releases/tag/v1.9.2" rel="noopener noreferrer"&gt;v1.9.2&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;irb 1.16.0&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;1.14.3 to &lt;a href="https://github.com/ruby/irb/releases/tag/v1.15.0" rel="noopener noreferrer"&gt;v1.15.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/irb/releases/tag/v1.15.1" rel="noopener noreferrer"&gt;v1.15.1&lt;/a&gt;, &lt;a href="https://github.com/ruby/irb/releases/tag/v1.15.2" rel="noopener noreferrer"&gt;v1.15.2&lt;/a&gt;, &lt;a href="https://github.com/ruby/irb/releases/tag/v1.15.3" rel="noopener noreferrer"&gt;v1.15.3&lt;/a&gt;, &lt;a href="https://github.com/ruby/irb/releases/tag/v1.16.0" rel="noopener noreferrer"&gt;v1.16.0&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;reline 0.6.3&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;0.6.0 to &lt;a href="https://github.com/ruby/reline/releases/tag/v0.6.1" rel="noopener noreferrer"&gt;v0.6.1&lt;/a&gt;, &lt;a href="https://github.com/ruby/reline/releases/tag/v0.6.2" rel="noopener noreferrer"&gt;v0.6.2&lt;/a&gt;, &lt;a href="https://github.com/ruby/reline/releases/tag/v0.6.3" rel="noopener noreferrer"&gt;v0.6.3&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;readline 0.0.4&lt;/li&gt;
&lt;li&gt;fiddle 1.1.8&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;1.1.6 to &lt;a href="https://github.com/ruby/fiddle/releases/tag/v1.1.7" rel="noopener noreferrer"&gt;v1.1.7&lt;/a&gt;, &lt;a href="https://github.com/ruby/fiddle/releases/tag/v1.1.8" rel="noopener noreferrer"&gt;v1.1.8&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The libraries above are no longer default gems. That means they can no longer be &lt;code&gt;require&lt;/code&gt;-d without being in your Gemfile, so please be careful if you use them.&lt;/p&gt;

&lt;p&gt;The following default gem is added.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;win32-registry 0.1.2&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following default gems are updated.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RubyGems 4.0.3&lt;/li&gt;
&lt;li&gt;bundler 4.0.3&lt;/li&gt;
&lt;li&gt;date 3.5.1

&lt;ul&gt;
&lt;li&gt;3.4.1 to &lt;a href="https://github.com/ruby/date/releases/tag/v3.5.0" rel="noopener noreferrer"&gt;v3.5.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/date/releases/tag/v3.5.1" rel="noopener noreferrer"&gt;v3.5.1&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;delegate 0.6.1

&lt;ul&gt;
&lt;li&gt;0.4.0 to &lt;a href="https://github.com/ruby/delegate/releases/tag/v0.5.0" rel="noopener noreferrer"&gt;v0.5.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/delegate/releases/tag/v0.6.0" rel="noopener noreferrer"&gt;v0.6.0&lt;/a&gt;, 0.6.1]&lt;a href="https://github.com/ruby/delegate/releases/tag/v0.6.1" rel="noopener noreferrer"&gt;delegate-v0.6.1&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;digest 3.2.1

&lt;ul&gt;
&lt;li&gt;3.2.0 to &lt;a href="https://github.com/ruby/digest/releases/tag/v3.2.1" rel="noopener noreferrer"&gt;v3.2.1&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;english 0.8.1

&lt;ul&gt;
&lt;li&gt;0.8.0 to &lt;a href="https://github.com/ruby/english/releases/tag/v0.8.1" rel="noopener noreferrer"&gt;v0.8.1&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;erb 6.0.1

&lt;ul&gt;
&lt;li&gt;4.0.4 to &lt;a href="https://github.com/ruby/erb/releases/tag/v5.1.2" rel="noopener noreferrer"&gt;v5.1.2&lt;/a&gt;, &lt;a href="https://github.com/ruby/erb/releases/tag/v5.1.3" rel="noopener noreferrer"&gt;v5.1.3&lt;/a&gt;, &lt;a href="https://github.com/ruby/erb/releases/tag/v6.0.0" rel="noopener noreferrer"&gt;v6.0.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/erb/releases/tag/v6.0.1" rel="noopener noreferrer"&gt;v6.0.1&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;error_highlight 0.7.1&lt;/li&gt;
&lt;li&gt;etc 1.4.6&lt;/li&gt;
&lt;li&gt;fcntl 1.3.0

&lt;ul&gt;
&lt;li&gt;1.2.0 to &lt;a href="https://github.com/ruby/fcntl/releases/tag/v1.3.0" rel="noopener noreferrer"&gt;v1.3.0&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;fileutils 1.8.0

&lt;ul&gt;
&lt;li&gt;1.7.3 to &lt;a href="https://github.com/ruby/fileutils/releases/tag/v1.8.0" rel="noopener noreferrer"&gt;v1.8.0&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;forwardable 1.4.0

&lt;ul&gt;
&lt;li&gt;1.3.3 to &lt;a href="https://github.com/ruby/forwardable/releases/tag/v1.4.0" rel="noopener noreferrer"&gt;v1.4.0&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;io-console 0.8.2

&lt;ul&gt;
&lt;li&gt;0.8.1 to &lt;a href="https://github.com/ruby/io-console/releases/tag/v0.8.2" rel="noopener noreferrer"&gt;v0.8.2&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;io-nonblock 0.3.2&lt;/li&gt;
&lt;li&gt;io-wait 0.4.0

&lt;ul&gt;
&lt;li&gt;0.3.2 to &lt;a href="https://github.com/ruby/io-wait/releases/tag/v0.3.3" rel="noopener noreferrer"&gt;v0.3.3&lt;/a&gt;, &lt;a href="https://github.com/ruby/io-wait/releases/tag/v0.3.5.test1" rel="noopener noreferrer"&gt;v0.3.5.test1&lt;/a&gt;, &lt;a href="https://github.com/ruby/io-wait/releases/tag/v0.3.5" rel="noopener noreferrer"&gt;v0.3.5&lt;/a&gt;, &lt;a href="https://github.com/ruby/io-wait/releases/tag/v0.3.6" rel="noopener noreferrer"&gt;v0.3.6&lt;/a&gt;, &lt;a href="https://github.com/ruby/io-wait/releases/tag/v0.4.0" rel="noopener noreferrer"&gt;v0.4.0&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ipaddr 1.2.8&lt;/li&gt;
&lt;li&gt;json 2.18.0

&lt;ul&gt;
&lt;li&gt;2.9.1 to &lt;a href="https://github.com/ruby/json/releases/tag/v2.10.0" rel="noopener noreferrer"&gt;v2.10.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/json/releases/tag/v2.10.1" rel="noopener noreferrer"&gt;v2.10.1&lt;/a&gt;, &lt;a href="https://github.com/ruby/json/releases/tag/v2.10.2" rel="noopener noreferrer"&gt;v2.10.2&lt;/a&gt;, &lt;a href="https://github.com/ruby/json/releases/tag/v2.11.0" rel="noopener noreferrer"&gt;v2.11.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/json/releases/tag/v2.11.1" rel="noopener noreferrer"&gt;v2.11.1&lt;/a&gt;, &lt;a href="https://github.com/ruby/json/releases/tag/v2.11.2" rel="noopener noreferrer"&gt;v2.11.2&lt;/a&gt;, &lt;a href="https://github.com/ruby/json/releases/tag/v2.11.3" rel="noopener noreferrer"&gt;v2.11.3&lt;/a&gt;, &lt;a href="https://github.com/ruby/json/releases/tag/v2.12.0" rel="noopener noreferrer"&gt;v2.12.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/json/releases/tag/v2.12.1" rel="noopener noreferrer"&gt;v2.12.1&lt;/a&gt;, &lt;a href="https://github.com/ruby/json/releases/tag/v2.12.2" rel="noopener noreferrer"&gt;v2.12.2&lt;/a&gt;, &lt;a href="https://github.com/ruby/json/releases/tag/v2.13.0" rel="noopener noreferrer"&gt;v2.13.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/json/releases/tag/v2.13.1" rel="noopener noreferrer"&gt;v2.13.1&lt;/a&gt;, &lt;a href="https://github.com/ruby/json/releases/tag/v2.13.2" rel="noopener noreferrer"&gt;v2.13.2&lt;/a&gt;, &lt;a href="https://github.com/ruby/json/releases/tag/v2.14.0" rel="noopener noreferrer"&gt;v2.14.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/json/releases/tag/v2.14.1" rel="noopener noreferrer"&gt;v2.14.1&lt;/a&gt;, &lt;a href="https://github.com/ruby/json/releases/tag/v2.15.0" rel="noopener noreferrer"&gt;v2.15.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/json/releases/tag/v2.15.1" rel="noopener noreferrer"&gt;v2.15.1&lt;/a&gt;, &lt;a href="https://github.com/ruby/json/releases/tag/v2.15.2" rel="noopener noreferrer"&gt;v2.15.2&lt;/a&gt;, &lt;a href="https://github.com/ruby/json/releases/tag/v2.16.0" rel="noopener noreferrer"&gt;v2.16.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/json/releases/tag/v2.17.0" rel="noopener noreferrer"&gt;v2.17.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/json/releases/tag/v2.17.1" rel="noopener noreferrer"&gt;v2.17.1&lt;/a&gt;, &lt;a href="https://github.com/ruby/json/releases/tag/v2.18.0" rel="noopener noreferrer"&gt;v2.18.0&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;net-http 0.9.1

&lt;ul&gt;
&lt;li&gt;0.6.0 to &lt;a href="https://github.com/ruby/net-http/releases/tag/v0.7.0" rel="noopener noreferrer"&gt;v0.7.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/net-http/releases/tag/v0.8.0" rel="noopener noreferrer"&gt;v0.8.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/net-http/releases/tag/v0.9.0" rel="noopener noreferrer"&gt;v0.9.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/net-http/releases/tag/v0.9.1" rel="noopener noreferrer"&gt;v0.9.1&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;openssl 4.0.0

&lt;ul&gt;
&lt;li&gt;3.3.1 to &lt;a href="https://github.com/ruby/openssl/releases/tag/v3.3.2" rel="noopener noreferrer"&gt;v3.3.2&lt;/a&gt;, &lt;a href="https://github.com/ruby/openssl/releases/tag/v4.0.0" rel="noopener noreferrer"&gt;v4.0.0&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;optparse 0.8.1

&lt;ul&gt;
&lt;li&gt;0.6.0 to &lt;a href="https://github.com/ruby/optparse/releases/tag/v0.7.0" rel="noopener noreferrer"&gt;v0.7.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/optparse/releases/tag/v0.8.0" rel="noopener noreferrer"&gt;v0.8.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/optparse/releases/tag/v0.8.1" rel="noopener noreferrer"&gt;v0.8.1&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;pp 0.6.3

&lt;ul&gt;
&lt;li&gt;0.6.2 to &lt;a href="https://github.com/ruby/pp/releases/tag/v0.6.3" rel="noopener noreferrer"&gt;v0.6.3&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;prism 1.7.0

&lt;ul&gt;
&lt;li&gt;1.5.2 to &lt;a href="https://github.com/ruby/prism/releases/tag/v1.6.0" rel="noopener noreferrer"&gt;v1.6.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/prism/releases/tag/v1.7.0" rel="noopener noreferrer"&gt;v1.7.0&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;psych 5.3.1

&lt;ul&gt;
&lt;li&gt;5.2.2 to &lt;a href="https://github.com/ruby/psych/releases/tag/v5.2.3" rel="noopener noreferrer"&gt;v5.2.3&lt;/a&gt;, &lt;a href="https://github.com/ruby/psych/releases/tag/v5.2.4" rel="noopener noreferrer"&gt;v5.2.4&lt;/a&gt;, &lt;a href="https://github.com/ruby/psych/releases/tag/v5.2.5" rel="noopener noreferrer"&gt;v5.2.5&lt;/a&gt;, &lt;a href="https://github.com/ruby/psych/releases/tag/v5.2.6" rel="noopener noreferrer"&gt;v5.2.6&lt;/a&gt;, &lt;a href="https://github.com/ruby/psych/releases/tag/v5.3.0" rel="noopener noreferrer"&gt;v5.3.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/psych/releases/tag/v5.3.1" rel="noopener noreferrer"&gt;v5.3.1&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;resolv 0.7.0

&lt;ul&gt;
&lt;li&gt;0.6.2 to &lt;a href="https://github.com/ruby/resolv/releases/tag/v0.6.3" rel="noopener noreferrer"&gt;v0.6.3&lt;/a&gt;, &lt;a href="https://github.com/ruby/resolv/releases/tag/v0.7.0" rel="noopener noreferrer"&gt;v0.7.0&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;stringio 3.2.0

&lt;ul&gt;
&lt;li&gt;3.1.2 to &lt;a href="https://github.com/ruby/stringio/releases/tag/v3.1.3" rel="noopener noreferrer"&gt;v3.1.3&lt;/a&gt;, &lt;a href="https://github.com/ruby/stringio/releases/tag/v3.1.4" rel="noopener noreferrer"&gt;v3.1.4&lt;/a&gt;, &lt;a href="https://github.com/ruby/stringio/releases/tag/v3.1.5" rel="noopener noreferrer"&gt;v3.1.5&lt;/a&gt;, &lt;a href="https://github.com/ruby/stringio/releases/tag/v3.1.6" rel="noopener noreferrer"&gt;v3.1.6&lt;/a&gt;, &lt;a href="https://github.com/ruby/stringio/releases/tag/v3.1.7" rel="noopener noreferrer"&gt;v3.1.7&lt;/a&gt;, &lt;a href="https://github.com/ruby/stringio/releases/tag/v3.1.8" rel="noopener noreferrer"&gt;v3.1.8&lt;/a&gt;, &lt;a href="https://github.com/ruby/stringio/releases/tag/v3.1.9" rel="noopener noreferrer"&gt;v3.1.9&lt;/a&gt;, &lt;a href="https://github.com/ruby/stringio/releases/tag/v3.2.0" rel="noopener noreferrer"&gt;v3.2.0&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;strscan 3.1.6

&lt;ul&gt;
&lt;li&gt;3.1.2 to &lt;a href="https://github.com/ruby/strscan/releases/tag/v3.1.3" rel="noopener noreferrer"&gt;v3.1.3&lt;/a&gt;, &lt;a href="https://github.com/ruby/strscan/releases/tag/v3.1.4" rel="noopener noreferrer"&gt;v3.1.4&lt;/a&gt;, &lt;a href="https://github.com/ruby/strscan/releases/tag/v3.1.5" rel="noopener noreferrer"&gt;v3.1.5&lt;/a&gt;, &lt;a href="https://github.com/ruby/strscan/releases/tag/v3.1.6" rel="noopener noreferrer"&gt;v3.1.6&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;time 0.4.2

&lt;ul&gt;
&lt;li&gt;0.4.1 to &lt;a href="https://github.com/ruby/time/releases/tag/v0.4.2" rel="noopener noreferrer"&gt;v0.4.2&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;timeout 0.6.0

&lt;ul&gt;
&lt;li&gt;0.4.3 to &lt;a href="https://github.com/ruby/timeout/releases/tag/v0.4.4" rel="noopener noreferrer"&gt;v0.4.4&lt;/a&gt;, &lt;a href="https://github.com/ruby/timeout/releases/tag/v0.5.0" rel="noopener noreferrer"&gt;v0.5.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/timeout/releases/tag/v0.6.0" rel="noopener noreferrer"&gt;v0.6.0&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;uri 1.1.1

&lt;ul&gt;
&lt;li&gt;1.0.4 to &lt;a href="https://github.com/ruby/uri/releases/tag/v1.1.0" rel="noopener noreferrer"&gt;v1.1.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/uri/releases/tag/v1.1.1" rel="noopener noreferrer"&gt;v1.1.1&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;weakref 0.1.4

&lt;ul&gt;
&lt;li&gt;0.1.3 to &lt;a href="https://github.com/ruby/weakref/releases/tag/v0.1.4" rel="noopener noreferrer"&gt;v0.1.4&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;zlib 3.2.2

&lt;ul&gt;
&lt;li&gt;3.2.1 to &lt;a href="https://github.com/ruby/zlib/releases/tag/v3.2.2" rel="noopener noreferrer"&gt;v3.2.2&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following bundled gems are updated.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;minitest 6.0.0&lt;/li&gt;
&lt;li&gt;power_assert 3.0.1

&lt;ul&gt;
&lt;li&gt;2.0.5 to &lt;a href="https://github.com/ruby/power_assert/releases/tag/v3.0.0" rel="noopener noreferrer"&gt;v3.0.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/power_assert/releases/tag/v3.0.1" rel="noopener noreferrer"&gt;v3.0.1&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;rake 13.3.1

&lt;ul&gt;
&lt;li&gt;13.2.1 to &lt;a href="https://github.com/ruby/rake/releases/tag/v13.3.0" rel="noopener noreferrer"&gt;v13.3.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/rake/releases/tag/v13.3.1" rel="noopener noreferrer"&gt;v13.3.1&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;test-unit 3.7.5

&lt;ul&gt;
&lt;li&gt;3.6.7 to &lt;a href="https://github.com/test-unit/test-unit/releases/tag/3.6.8" rel="noopener noreferrer"&gt;3.6.8&lt;/a&gt;, &lt;a href="https://github.com/test-unit/test-unit/releases/tag/3.6.9" rel="noopener noreferrer"&gt;3.6.9&lt;/a&gt;, &lt;a href="https://github.com/test-unit/test-unit/releases/tag/3.7.0" rel="noopener noreferrer"&gt;3.7.0&lt;/a&gt;, &lt;a href="https://github.com/test-unit/test-unit/releases/tag/3.7.1" rel="noopener noreferrer"&gt;3.7.1&lt;/a&gt;, &lt;a href="https://github.com/test-unit/test-unit/releases/tag/3.7.2" rel="noopener noreferrer"&gt;3.7.2&lt;/a&gt;, &lt;a href="https://github.com/test-unit/test-unit/releases/tag/3.7.3" rel="noopener noreferrer"&gt;3.7.3&lt;/a&gt;, &lt;a href="https://github.com/test-unit/test-unit/releases/tag/3.7.4" rel="noopener noreferrer"&gt;3.7.4&lt;/a&gt;, &lt;a href="https://github.com/test-unit/test-unit/releases/tag/3.7.5" rel="noopener noreferrer"&gt;3.7.5&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;rexml 3.4.4&lt;/li&gt;
&lt;li&gt;rss 0.3.2

&lt;ul&gt;
&lt;li&gt;0.3.1 to &lt;a href="https://github.com/ruby/rss/releases/tag/0.3.2" rel="noopener noreferrer"&gt;0.3.2&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;net-ftp 0.3.9

&lt;ul&gt;
&lt;li&gt;0.3.8 to &lt;a href="https://github.com/ruby/net-ftp/releases/tag/v0.3.9" rel="noopener noreferrer"&gt;v0.3.9&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;net-imap 0.6.2

&lt;ul&gt;
&lt;li&gt;0.5.8 to &lt;a href="https://github.com/ruby/net-imap/releases/tag/v0.5.9" rel="noopener noreferrer"&gt;v0.5.9&lt;/a&gt;, &lt;a href="https://github.com/ruby/net-imap/releases/tag/v0.5.10" rel="noopener noreferrer"&gt;v0.5.10&lt;/a&gt;, &lt;a href="https://github.com/ruby/net-imap/releases/tag/v0.5.11" rel="noopener noreferrer"&gt;v0.5.11&lt;/a&gt;, &lt;a href="https://github.com/ruby/net-imap/releases/tag/v0.5.12" rel="noopener noreferrer"&gt;v0.5.12&lt;/a&gt;, &lt;a href="https://github.com/ruby/net-imap/releases/tag/v0.5.13" rel="noopener noreferrer"&gt;v0.5.13&lt;/a&gt;, &lt;a href="https://github.com/ruby/net-imap/releases/tag/v0.6.0" rel="noopener noreferrer"&gt;v0.6.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/net-imap/releases/tag/v0.6.1" rel="noopener noreferrer"&gt;v0.6.1&lt;/a&gt;, &lt;a href="https://github.com/ruby/net-imap/releases/tag/v0.6.2" rel="noopener noreferrer"&gt;v0.6.2&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;net-smtp 0.5.1

&lt;ul&gt;
&lt;li&gt;0.5.0 to &lt;a href="https://github.com/ruby/net-smtp/releases/tag/v0.5.1" rel="noopener noreferrer"&gt;v0.5.1&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;matrix 0.4.3

&lt;ul&gt;
&lt;li&gt;0.4.2 to &lt;a href="https://github.com/ruby/matrix/releases/tag/v0.4.3" rel="noopener noreferrer"&gt;v0.4.3&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;prime 0.1.4

&lt;ul&gt;
&lt;li&gt;0.1.3 to &lt;a href="https://github.com/ruby/prime/releases/tag/v0.1.4" rel="noopener noreferrer"&gt;v0.1.4&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;rbs 3.10.0

&lt;ul&gt;
&lt;li&gt;3.8.0 to &lt;a href="https://github.com/ruby/rbs/releases/tag/v3.8.1" rel="noopener noreferrer"&gt;v3.8.1&lt;/a&gt;, &lt;a href="https://github.com/ruby/rbs/releases/tag/v3.9.0.dev.1" rel="noopener noreferrer"&gt;v3.9.0.dev.1&lt;/a&gt;, &lt;a href="https://github.com/ruby/rbs/releases/tag/v3.9.0.pre.1" rel="noopener noreferrer"&gt;v3.9.0.pre.1&lt;/a&gt;, &lt;a href="https://github.com/ruby/rbs/releases/tag/v3.9.0.pre.2" rel="noopener noreferrer"&gt;v3.9.0.pre.2&lt;/a&gt;, &lt;a href="https://github.com/ruby/rbs/releases/tag/v3.9.0" rel="noopener noreferrer"&gt;v3.9.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/rbs/releases/tag/v3.9.1" rel="noopener noreferrer"&gt;v3.9.1&lt;/a&gt;, &lt;a href="https://github.com/ruby/rbs/releases/tag/v3.9.2" rel="noopener noreferrer"&gt;v3.9.2&lt;/a&gt;, &lt;a href="https://github.com/ruby/rbs/releases/tag/v3.9.3" rel="noopener noreferrer"&gt;v3.9.3&lt;/a&gt;, &lt;a href="https://github.com/ruby/rbs/releases/tag/v3.9.4" rel="noopener noreferrer"&gt;v3.9.4&lt;/a&gt;, &lt;a href="https://github.com/ruby/rbs/releases/tag/v3.9.5" rel="noopener noreferrer"&gt;v3.9.5&lt;/a&gt;, &lt;a href="https://github.com/ruby/rbs/releases/tag/v3.10.0.pre.1" rel="noopener noreferrer"&gt;v3.10.0.pre.1&lt;/a&gt;, &lt;a href="https://github.com/ruby/rbs/releases/tag/v3.10.0.pre.2" rel="noopener noreferrer"&gt;v3.10.0.pre.2&lt;/a&gt;, &lt;a href="https://github.com/ruby/rbs/releases/tag/v3.10.0" rel="noopener noreferrer"&gt;v3.10.0&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;typeprof 0.31.1&lt;/li&gt;
&lt;li&gt;debug 1.11.1

&lt;ul&gt;
&lt;li&gt;1.11.0 to &lt;a href="https://github.com/ruby/debug/releases/tag/v1.11.1" rel="noopener noreferrer"&gt;v1.11.1&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;base64 0.3.0

&lt;ul&gt;
&lt;li&gt;0.2.0 to &lt;a href="https://github.com/ruby/base64/releases/tag/v0.3.0" rel="noopener noreferrer"&gt;v0.3.0&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;bigdecimal 4.0.1

&lt;ul&gt;
&lt;li&gt;3.1.8 to &lt;a href="https://github.com/ruby/bigdecimal/releases/tag/v3.2.0" rel="noopener noreferrer"&gt;v3.2.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/bigdecimal/releases/tag/v3.2.1" rel="noopener noreferrer"&gt;v3.2.1&lt;/a&gt;, &lt;a href="https://github.com/ruby/bigdecimal/releases/tag/v3.2.2" rel="noopener noreferrer"&gt;v3.2.2&lt;/a&gt;, &lt;a href="https://github.com/ruby/bigdecimal/releases/tag/v3.2.3" rel="noopener noreferrer"&gt;v3.2.3&lt;/a&gt;, &lt;a href="https://github.com/ruby/bigdecimal/releases/tag/v3.3.0" rel="noopener noreferrer"&gt;v3.3.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/bigdecimal/releases/tag/v3.3.1" rel="noopener noreferrer"&gt;v3.3.1&lt;/a&gt;, &lt;a href="https://github.com/ruby/bigdecimal/releases/tag/v4.0.0" rel="noopener noreferrer"&gt;v4.0.0&lt;/a&gt;, &lt;a href="https://github.com/ruby/bigdecimal/releases/tag/v4.0.1" rel="noopener noreferrer"&gt;v4.0.1&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;drb 2.2.3

&lt;ul&gt;
&lt;li&gt;2.2.1 to &lt;a href="https://github.com/ruby/drb/releases/tag/v2.2.3" rel="noopener noreferrer"&gt;v2.2.3&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;syslog 0.3.0

&lt;ul&gt;
&lt;li&gt;0.2.0 to &lt;a href="https://github.com/ruby/syslog/releases/tag/v0.3.0" rel="noopener noreferrer"&gt;v0.3.0&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;csv 3.3.5

&lt;ul&gt;
&lt;li&gt;3.3.2 to &lt;a href="https://github.com/ruby/csv/releases/tag/v3.3.3" rel="noopener noreferrer"&gt;v3.3.3&lt;/a&gt;, &lt;a href="https://github.com/ruby/csv/releases/tag/v3.3.4" rel="noopener noreferrer"&gt;v3.3.4&lt;/a&gt;, &lt;a href="https://github.com/ruby/csv/releases/tag/v3.3.5" rel="noopener noreferrer"&gt;v3.3.5&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;repl_type_completor 0.1.12&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are a lot of updates.&lt;br&gt;
I can't read them all, so I'll skip.&lt;/p&gt;
&lt;h3&gt;
  
  
  IRB: added the &lt;code&gt;copy&lt;/code&gt; command
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;copy&lt;/code&gt; command was added. It lets you copy output results to the system clipboard.&lt;/p&gt;

&lt;p&gt;For example, if you want to copy model output in a Rails console like this, running &lt;code&gt;copy&lt;/code&gt; after the output will copy from &lt;code&gt;User:0x00..&lt;/code&gt; to &lt;code&gt;updated_at&lt;/code&gt; to the clipboard.&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;app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;#&amp;lt;User:0x0000000351352c40&lt;/span&gt;
 &lt;span class="ss"&gt;id: &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;email: &lt;/span&gt;&lt;span class="s2"&gt;"test@example.com"&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;"Ruby"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="ss"&gt;created_at: &lt;/span&gt;&lt;span class="s2"&gt;"2025-12-09 17:48:39.000000000 +0900"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="ss"&gt;updated_at: &lt;/span&gt;&lt;span class="s2"&gt;"2025-12-09 17:48:39.000000000 +0900"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;copy&lt;/span&gt;
&lt;span class="no"&gt;Copied&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nb"&gt;system&lt;/span&gt; &lt;span class="n"&gt;clipboard&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(ima1zumi)&lt;/p&gt;

&lt;p&gt;(ko1 addendum: it calls the system command for copying to the clipboard (like &lt;code&gt;pbcopy&lt;/code&gt;), so it won't work in a terminal on an SSH host, for example.)&lt;/p&gt;

&lt;h3&gt;
  
  
  RubyGems and Bundler
&lt;/h3&gt;

&lt;p&gt;Ruby 4.RubyGems and Bundler version 4. see the following links for details.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.rubygems.org/2025/12/03/upgrade-to-rubygems-bundler-4.html" rel="noopener noreferrer"&gt;Upgrading to RubyGems/Bundler 4 - RubyGems Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.rubygems.org/2025/12/03/4.0.0-released.html" rel="noopener noreferrer"&gt;4.0.0 Released - RubyGems Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.rubygems.org/2025/12/09/4.0.1-released.html" rel="noopener noreferrer"&gt;4.0.1 Released - RubyGems Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.rubygems.org/2025/12/17/4.0.2-released.html" rel="noopener noreferrer"&gt;4.0.2 Released - RubyGems Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.rubygems.org/2025/12/23/4.0.3-released.html" rel="noopener noreferrer"&gt;4.0.3 Released - RubyGems Blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Supported platforms
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Windows

&lt;ul&gt;
&lt;li&gt;Dropped support for MSVC versions older than 14.0 (_MSC_VER 1900).
This means Visual Studio 2015 or later is now required.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;On Windows native, it seems you can no longer build without Visual Studio 2015 or later. Version numbers like 14.0, 1900, and 2015 for VC are complicated, with many different kinds.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h2&gt;
  
  
  Compatibility issues
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The following methoded from Ractor due to the addition of &lt;code&gt;Ractor::Port&lt;/code&gt;:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Ractor.yield&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ractor#take&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ractor#close_incoming&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ractor#close_outgoging&lt;/code&gt;
[&lt;a href="https://bugs.ruby-lang.org/issues/21262" rel="noopener noreferrer"&gt;Feature #21262&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This is also covered in the article.&lt;/p&gt;

&lt;p&gt;[&lt;a href="https://product.st.inc/entry/2025/06/24/110606:embed:cite" rel="noopener noreferrer"&gt;https://product.st.inc/entry/2025/06/24/110606:embed:cite&lt;/a&gt;]&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ObjectSpace._id2ref&lt;/code&gt; is deprecated. [&lt;a href="https://bugs.ruby-lang.org/issues/15408" rel="noopener noreferrer"&gt;Feature #15408&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;ObjectSpace._id2ref&lt;/code&gt;, which gets an object from its ID (the one you get via &lt;code&gt;#object_id&lt;/code&gt;), was deprecated. The decision to deprecate it was made long ago, but for some reason it was forgotten, so now it is finally deprecated.&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="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object_id&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="no"&gt;ObjectSpace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_id2ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; main&lt;/span&gt;
&lt;span class="c1"&gt;# In Ruby 4.0, you get a warning -&amp;gt; warning: ObjectSpace._id2ref is deprecated&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Process::Status#&amp;amp;&lt;/code&gt; and &lt;code&gt;Process::Status#&amp;gt;&amp;gt;&lt;/code&gt; have been removed.
They were deprecated in Ruby 3.3. [&lt;a href="https://bugs.ruby-lang./19868" rel="noopener noreferrer"&gt;Bug #19868&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;Process::Status#&amp;amp;&lt;/code&gt; and &lt;code&gt;Process::Status#&amp;gt;&amp;gt;&lt;/code&gt; were removed. They were already deprecated in Ruby 3.3. They were used to peek at status codes, but now there are dedicated methods, so they are no longer used.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;rb_path_check&lt;/code&gt; has been removed. This function was used for
&lt;code&gt;$SAFE&lt;/code&gt; path checking which was removed in Ruby 2.7,
and was already deprecated,.
[&lt;a href="https://bugs.ruby-lang.org/isss/20971" rel="noopener noreferrer"&gt;Feature #20971&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;rb_path_check&lt;/code&gt; function was removed. It depended on the &lt;code&gt;$SAFE&lt;/code&gt; feature that was removed long ago, but it remained for compatibility, so it was removed this time.&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;

&lt;h3&gt;
  
  
  Backtrace formatting changed
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A backtrace for &lt;code&gt;ArgumentError&lt;/code&gt; of "wrong number of arguments" now
include the receiver's class or module name (e.g., in &lt;code&gt;Foo#bar&lt;/code&gt;
instead of in &lt;code&gt;bar&lt;/code&gt;). [[Bug #21698]]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In backtraces for "wrong number of arguments", the class name now appears.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Ruby 3.4
test.rb:1:in 'add': wrong number of arguments (given 1, expected 2) (ArgumentError)

# Ruby 4.0
test.rb:1:in 'Object#add': wrong number of arguments (given 1, expected 2) (ArgumentError)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The method name notation changed from &lt;code&gt;add&lt;/code&gt; to &lt;code&gt;Object#add&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backtraces no longer display &lt;code&gt;internal&lt;/code&gt; frames.
These methods now appear as if it is in the Ruby source file,
consistent wither C-implemented methods. [[Bug #20968]]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recently, more built-in methods are being written in Ruby rather than C. As a result, backtraces often showed a mysterious file like &lt;code&gt;&amp;lt;internal:???&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Ruby 3.4
$ ruby -e '[1].fetch_values(42)'
&amp;lt;internal:array&amp;gt;:211:in 'Array#fetch': index 42 outside of array bounds: -1...1 (IndexError)
        from &amp;lt;internal:array&amp;gt;:211:in 'block in Array#fetch_values'
        from nternal:array&amp;gt;:211:in 'Array#map!'
        from &amp;lt;internal:array&amp;gt;:211:in 'Array#fetch_values'
        from -e:1:in '&amp;lt;main&amp;gt;'

# Ruby 4.0
$ ruby -e '[1].fetch_values(42)'
-e:1:in 'Array#fetch_values': index 42 outside of array bounds: -1...1 (IndexError)
        from -e:1:in '&amp;lt;main&amp;gt;'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was a virtual file name representing the source where built-in methods are defined. However, there is nothing a normal user can do with that file name, and it just adds work to skip those frames. In most cases, what the user wants to see is the caller of the built-in method.&lt;/p&gt;

&lt;p&gt;So we removed &lt;code&gt;&amp;lt;internal:*&amp;gt;&lt;/code&gt; from display and changed it to show as if the error occurred at the caller.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;p&gt;I think it would be better if everything was shown, though.&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;

&lt;h2&gt;
  
  
  About standard library compatibility
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The CGI library was removed from the bundle
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The CGI library is removed from the default gems. Now we only provide &lt;code&gt;cgi/escape&lt;/code&gt; for
the following methods:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CGI.escape&lt;/code&gt; and &lt;code&gt;CGI.unescape&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CGI.escapeHTML&lt;/code&gt; and &lt;code&gt;CGI.unescapeHTML&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CGI.escapeURIComponent&lt;/code&gt; and &lt;code&gt;CGI.unescapeURIComponent&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CGI.escapeElement&lt;/code&gt; and &lt;code&gt;CGI.unescapeElement&lt;/code&gt;
[&lt;a href="https://bugs.ruby-lang.org/issues/21258" rel="noopener noreferrer"&gt;Feature #21258&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Most of the cgi gem was removed from the bundle. In modern times it's judged not to be worth the maintenance cost.&lt;/p&gt;

&lt;p&gt;Only the eight methods above such as &lt;code&gt;CGI.escape&lt;/code&gt; are very commonly used, so a bundle remains in the form of &lt;code&gt;cgi/escape&lt;/code&gt;. If you don't need the full cgi gem and only need these methods, you should &lt;code&gt;require "cgi/escape"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you need the entire cgi gem, please do &lt;code&gt;gem install cgi&lt;/code&gt; or add &lt;code&gt;gem "cgi"&lt;/code&gt; to your Gemfile.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;SortedSet&lt;/code&gt; was removed from the bundle
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;With the move of &lt;code&gt;Set&lt;/code&gt; from stdlib to core class, &lt;code&gt;set/sorted_set.rb&lt;/code&gt; has
been removed, and &lt;code&gt;SortedSet&lt;/code&gt; is no longer an autoloaded constant. Please
install the &lt;code&gt;sorted_set&lt;/code&gt; gem and &lt;code&gt;require 'sorted_set'&lt;/code&gt; to use &lt;code&gt;SortedSet&lt;/code&gt;.
[&lt;a href="https://bugs.ruby-lang.org/issues/21287" rel="noopener noreferrer"&gt;Feature #21287&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SortedSet was removed from the bundle. If you need it, install the sorted_set gem.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h2&gt;
  
  
  C API updates
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;IO

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;rb_thread_fd_close&lt;/code&gt; is deprecated and now a no-op. If youto expose
file descriptors from C extensions to Ruby code, create an &lt;code&gt;IO&lt;/code&gt; instance
using &lt;code&gt;RUBY_IO_MODE_EXTERNAL&lt;/code&gt; and use &lt;code&gt;rb_io_close(io)&lt;/code&gt; to close it (this
also interrupts and waits for all pending operations on the &lt;code&gt;IO&lt;/code&gt;
instance). Directly closing file descriptors does not interrupt pending
operations, and may lead to undefined behaviour. In other words, if two
&lt;code&gt;IO&lt;/code&gt; objects share the same file descriptor, closing one does not affect
the other. [&lt;a href="https://bugs.ruby-lang.org/issues/18455" rel="noopener noreferrer"&gt;Feature #18455&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;rb_thread_fd_close()&lt;/code&gt; was deprecated and now does nothing when called. This API used to accept a file descriptor directly, but at the Ruby level we want people to close via an IO object, so this change happened. If you want to manipulate a file descriptor directly, please use &lt;code&gt;RUBY_IO_MODE_EXTERNAL&lt;/code&gt; when creating the IO object, and close it with &lt;code&gt;rb_io_close(io)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rb_thread_call_with_gvl&lt;/code&gt; now works with or without the GVL.&lt;br&gt;
    This allows gems to avoid checking &lt;code&gt;ruby_thread_has_gvl_p&lt;/code&gt;.&lt;br&gt;
    Please still be diligent about the GVL. [&lt;a href="https://bugs.ruby-lang.org/issues/20750" rel="noopener noreferrer"&gt;Feature #20750&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;GVL (Global/Giant VM Lock, though it's not really that anymore since it's per-Ractor; recently we call it Great Valuable Lock) is released while doing some work, and sometimes you want to reacquire the GVL to do something (Ruby-related things require the GVL). There is an API called &lt;code&gt;rb_thread_call_with_gvl()&lt;/code&gt; which takes a function &lt;code&gt;f&lt;/code&gt; and calls &lt;code&gt;f&lt;/code&gt; after acquiring the GVL. This API used to error if called when the GVL wasn't released, but now it can be called without error. There are cases where you want to call it regardless of whether the GVL is released, so it should be more convenient.&lt;/p&gt;

&lt;p&gt;Personally, I was against it because I think you should be sensitive to GVL acquisition state, or rather avoid such shared code scenarios as much as possible, but it went in anyway. In any case, I think you should keep code that runs without the GVL as simple as possible.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set

&lt;ul&gt;
&lt;li&gt;A C API for &lt;code&gt;Set&lt;/code&gt; has been added. The following methods are supported:
59](&lt;a href="https://bugs.ruby-lang.org/issues/21459)" rel="noopener noreferrer"&gt;https://bugs.ruby-lang.org/issues/21459)&lt;/a&gt;]

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;rb_set_foreach&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rb_set_new&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rb_set_new_capa&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rb_set_lookup&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rb_set_add&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rb_set_clear&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rb_set_delete&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rb_set_size&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Because Set became built-in, these C APIs were introduced. By the way, there are already quite a few C APIs with the &lt;code&gt;rb_set_&lt;/code&gt; prefix (for example &lt;code&gt;rb_set_errinfo()&lt;/code&gt;), so there was some talk that it could be confusing.&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;

&lt;p&gt;Implementation improvements&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Class#new&lt;/code&gt; (ex. &lt;code&gt;Object.new&lt;/code&gt;) is faster in all cases, but especially when passing keyword arguments. This has also been integrated into YJIT and ZJIT. [&lt;a href="https://bugs.ruby-lang.org/issues/21254" rel="noopener noreferrer"&gt;Feature #21254&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Calls like &lt;code&gt;Foo.new&lt;/code&gt; are faster. It feels like a dedicated instruction was added. By the way, I did the rough design of the instruction.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GC heaps of different size pools now grow independently, reducing memory usage when only some pools contain long-lived objects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GC improvements. I think this is an adjustment of VWA.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GC sweeping is faster on pages of large objects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don't know what was done here.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Generic ivar" objects (String, Array, &lt;code&gt;TypedData&lt;/code&gt;, etc.) now use a new internal "fields" object for faster instance variable access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A new data structure was introduced to hold instance variables for non-user-defined objects. I wonder what it was for. Ractor?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The GC avoids maintainial &lt;code&gt;id2ref&lt;/code&gt; table until it is first used, making &lt;code&gt;object_id&lt;/code&gt; allocation and GC sweeping faster&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since the data for managing id2ref is lazily generated, calls to &lt;code&gt;#object_id&lt;/code&gt; and sweeping are faster as long as id2ref isn't used. I wonder how they did that.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;object_id&lt;/code&gt; and &lt;code&gt;hash&lt;/code&gt; are faster on Class and Module objects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;object_id&lt;/code&gt; and &lt;code&gt;hash&lt;/code&gt; are faster for classes and modules. What did they do here, I wonder.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Largeers can remain embedded using variable width allocation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bignum (large integers) are also faster by managing them with VWA.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Random&lt;/code&gt;, &lt;code&gt;Enumerator::Product&lt;/code&gt;, &lt;code&gt;Enumerator::Chain&lt;/code&gt;, &lt;code&gt;Addrinfo&lt;/code&gt;,
&lt;code&gt;StringScanner&lt;/code&gt;, and some internal objects are now write-barrier protected,
which reduces GC overhead.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They added proper write barriers to some objects so they are no longer unprotected. GC becomes faster.&lt;/p&gt;

&lt;p&gt;(I haven't followed this very closely.&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;

&lt;h3&gt;
  
  
  Ractor
&lt;/h3&gt;

&lt;p&gt;A lot of work has gone into making Ractors more stable, performant, and usable. These improvements bring Ractor implementation closer to leaving experimental status.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Performance improvements

&lt;ul&gt;
&lt;li&gt;Frozen strings and the symbol table internally use a lock-free hash set [[Feature #21268]]&lt;/li&gt;
&lt;li&gt;Method cache lookups avoid locking in most cases&lt;/li&gt;
&lt;li&gt;Class (and generic ivar) instance variable access is faster and avoids locking&lt;/li&gt;
&lt;li&gt;CPU cache contention is avoided in object allocation by using a per-ractor counter&lt;/li&gt;
&lt;li&gt;CPU cache contention is avoided in xmalloc/xfree by using a thread-local counter&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;object_id&lt;/code&gt; avoids locking in most cases&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Bug fixes and stability

&lt;ul&gt;
&lt;li&gt;Fixed possible deadlocks when combining Ractors and Threads&lt;/li&gt;
&lt;li&gt;Fixed issues with require and autoload in a Ractor&lt;/li&gt;
&lt;li&gt;Fixed encoding/transcoding issues across Ractors&lt;/li&gt;
&lt;li&gt;Fixed race conditions in GC operations and method invalidation&lt;/li&gt;
&lt;li&gt;Fixed issues with processes forking after starting a Ractor&lt;/li&gt;
&lt;li&gt;GC allocation counts are now accurate under Ractors&lt;/li&gt;
&lt;li&gt;Fixed TracePoints not working after GC [[Bug #19112]]&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;There were many performance improvements and bug fixes for Ractor. Mainly other people worked hard on it.&lt;br&gt;
Although it's not written here, I think the introduction of the above &lt;code&gt;Ractor::Port&lt;/code&gt; cleaned up functionality a lot, so that probably also improved performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  JIT
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;ZJIT

&lt;ul&gt;
&lt;li&gt;Introduce an &lt;a href="https://docs.ruby-lang.org/en/master/jit/zjit_md.html" rel="noopener noreferrer"&gt;experimental method-based JITompiler&lt;/a&gt;.
Where available, ZJIT can be enabled at runtime with the &lt;code&gt;--zjit&lt;/code&gt; option or by calling &lt;code&gt;RubyVM::ZJIT.enable&lt;/code&gt;.
When building Ruby, Rust 1.85.0 or later is required to include ZJIT support.&lt;/li&gt;
&lt;li&gt;As of Ruby 4.0.0, ZJIT is faster than the interpreter, but not yet as fast as YJIT.
We encourage experimentation with ZJIT, but advise against deploying it in production for now.&lt;/li&gt;
&lt;li&gt;Our goal is to make ZJIT faster than YJIT and production-ready in Ruby 4.1.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;ZJIT, the successor to YJIT, was introduced, and if you have a Rust environment (rustc 1.85.0 or later) at build time, it will be built together. You can use it with the &lt;code&gt;--zjit&lt;/code&gt; command-line argument. It's not as fast as YJIT yet, so they ask you to wait before using it in production. The goal is to exceed YJIT in Ruby 4.1.&lt;/p&gt;

&lt;p&gt;[&lt;a href="https://rubykaigi.org/2025/presentations/maximecb.html:embed:ciJIT" rel="noopener noreferrer"&gt;https://rubykaigi.org/2025/presentations/maximecb.html:embed:ciJIT&lt;/a&gt;&lt;br&gt;
    * &lt;code&gt;RubyVM::YJIT.runtime_stats&lt;/code&gt;&lt;br&gt;
        * &lt;code&gt;ratio_in_yjit&lt;/code&gt; no longer works in the default build.&lt;br&gt;
          Use &lt;code&gt;--enable-yjit=stats&lt;/code&gt; on &lt;code&gt;configure&lt;/code&gt; to enable it on &lt;code&gt;--yjit-stats&lt;/code&gt;.&lt;br&gt;
        * Add &lt;code&gt;invalidate_everything&lt;/code&gt; to default stats, which is&lt;br&gt;
          incremented when every code is invalidated by TracePoint.&lt;br&gt;
    * Add &lt;code&gt;mem_size:&lt;/code&gt; and &lt;code&gt;call_threshold:&lt;/code&gt; options to &lt;code&gt;RubyVM::YJIT.enable&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;YJIT also seems to have improvements, but I'm not familiar, so I'll skip.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RJIT

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--rjit&lt;/code&gt; is r. We will move the implementation of the third-party JIT API
to the &lt;a href="https://github.com/ruby/rjit" rel="noopener noreferrer"&gt;ruby/rjit&lt;/a&gt; repository.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;It seems the &lt;code&gt;--rjit&lt;/code&gt; area was removed.&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;

&lt;h2&gt;
  
  
  In closing
&lt;/h2&gt;

&lt;p&gt;We have introduced new features and improvements in Ruby 4.0. Beyond what we covered here, there are also bug fixes and minor improvements. Please check with your Ruby applications.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;Ruby::Box&lt;/code&gt;, &lt;code&gt;zjit&lt;/code&gt;, and the rebuild of &lt;code&gt;Ractor&lt;/code&gt;, it's become a release with a lot to try. Please set it up locally and enjoy the new Ruby.&lt;/p&gt;

&lt;p&gt;Enjoy Ruby programming!&lt;/p&gt;

&lt;p&gt;(ko1/mame, guest: ima1zumi)&lt;/p&gt;

</description>
      <category>news</category>
      <category>opensource</category>
      <category>ruby</category>
    </item>
    <item>
      <title>`Ractor::Port` - Revamping the Ractor API</title>
      <dc:creator>Koichi Sasada</dc:creator>
      <pubDate>Thu, 25 Dec 2025 02:49:48 +0000</pubDate>
      <link>https://forem.com/ko1/ractorport-revamping-the-ractor-api-98</link>
      <guid>https://forem.com/ko1/ractorport-revamping-the-ractor-api-98</guid>
      <description>&lt;p&gt;(This English version is an AI-generated translation of the original Japanese post (&lt;a href="https://product.st.inc/entry/2025/06/24/110606" rel="noopener noreferrer"&gt;https://product.st.inc/entry/2025/06/24/110606&lt;/a&gt;), with some modification by hands, using Gemini)&lt;/p&gt;

&lt;p&gt;I am Koichi from STORES, Inc. In this article, I will introduce &lt;code&gt;Ractor::Port&lt;/code&gt;, a new mechanism we recently introduced to revamp parts of the Ractor API (Ruby's mechanism for easy parallel processing) that had been bothering me for a long time. I never quite felt comfortable with the previous API?it felt like a bone stuck in my throat?and after thinking about it for five years, I finally made the decision.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Proposed Ticket:&lt;/strong&gt; &lt;a href="https://bugs.ruby-lang.org/issues/21262" rel="noopener noreferrer"&gt;Feature #21262: Proposal: Ractor::Port - Ruby Issue Tracking System&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview of Changes
&lt;/h2&gt;

&lt;p&gt;Here is a quick summary of what has changed:&lt;/p&gt;

&lt;h3&gt;
  
  
  Deprecated / Removed
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Ractor#take&lt;/code&gt;, &lt;code&gt;Ractor.yield&lt;/code&gt;, &lt;code&gt;Ractor.receive_if&lt;/code&gt;, &lt;code&gt;Ractor#incoming_port&lt;/code&gt;, and &lt;code&gt;Ractor#outgoing_port&lt;/code&gt; are gone.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use These Instead
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Waiting for a Ractor to finish:&lt;/strong&gt; → &lt;code&gt;Ractor#join&lt;/code&gt; (Same as &lt;code&gt;Thread#join&lt;/code&gt;)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Waiting for a Ractor to finish and getting its return value:&lt;/strong&gt; → &lt;code&gt;Ractor#value&lt;/code&gt; (Same as &lt;code&gt;Thread#value&lt;/code&gt;)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 1346269&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Creating a dedicated communication path from other Ractors:&lt;/strong&gt; → &lt;code&gt;Ractor::Port&lt;/code&gt; (Think of it like a TCP port number)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="n"&gt;port1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
  &lt;span class="n"&gt;port2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;

  &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="ss"&gt;:hello&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="ss"&gt;:world&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;port2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; Guaranteed to be :world&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;port1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; Guaranteed to be :hello&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Every Ractor has a "default port" from the start, which can be accessed via &lt;code&gt;Ractor#default_port&lt;/code&gt;. Methods like &lt;code&gt;Ractor#send&lt;/code&gt; and &lt;code&gt;Ractor.receive&lt;/code&gt; operate on this default port. This means &lt;code&gt;Ractor#send&lt;/code&gt; and &lt;code&gt;.receive&lt;/code&gt; can still be used as before.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;# Displays 42&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally, there are methods like &lt;code&gt;Ractor#monitor&lt;/code&gt;, &lt;code&gt;unmonitor&lt;/code&gt;, and &lt;code&gt;Ractor::Port#close&lt;/code&gt;, but they are not critical for typical use.&lt;/p&gt;

&lt;p&gt;While we are usually very cautious about removing Ruby methods, Ractor has been labeled as "experimental" since Ruby 3.0. This allowed for a bold, incompatible change. Since it's still experimental, we assumed there aren't many people using it in production yet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ruby -e "Ractor.new{}"
-e:1: warning: Ractor is experimental, and the behavior may change in future versions of Ruby! Also there are many implementation issues.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Background: Issues with the Existing API
&lt;/h2&gt;

&lt;p&gt;Previously, communication between Ractors was organized into two models: &lt;strong&gt;Push&lt;/strong&gt; (&lt;code&gt;Ractor#send&lt;/code&gt; / &lt;code&gt;.receive&lt;/code&gt;) and &lt;strong&gt;Pull&lt;/strong&gt; (&lt;code&gt;Ractor.yield&lt;/code&gt; / &lt;code&gt;#take&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;When trying to communicate with a "Server" Ractor that performs specific tasks using these primitives, things got complicated?especially as the number of Ractors increased.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Specific Example&lt;/th&gt;
&lt;th&gt;Resulting Issue&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Difficulty identifying messages&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Ractor.receive&lt;/code&gt; is a single mailbox. Results from multiple servers get mixed up.&lt;/td&gt;
&lt;td&gt;You might receive a message from Server B when you expected one from Server A.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mailbox contention&lt;/td&gt;
&lt;td&gt;Library A and Library B both call &lt;code&gt;Ractor.receive&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;One library might "accidently swallow" a message intended for the other.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scattered APIs&lt;/td&gt;
&lt;td&gt;Mixing &lt;code&gt;receive_if&lt;/code&gt;, &lt;code&gt;yield&lt;/code&gt;, &lt;code&gt;#take&lt;/code&gt;, and &lt;code&gt;select&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;Increased learning/implementation cost and unstable CI.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Let's look at some code examples to understand these issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  EX1: A Server with No Response
&lt;/h3&gt;

&lt;p&gt;A simple "Fibonacci Server" that calculates numbers but doesn't return them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&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;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;-&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="mi"&gt;1&lt;/span&gt;

&lt;span class="n"&gt;fib_srv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&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;while&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;param&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# No way to send the result back&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;fib_srv&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is fine for "fire-and-forget" tasks like sending an email, but usually, we want the result back.&lt;/p&gt;

&lt;h3&gt;
  
  
  EX2: Sending the Caller Ractor Along
&lt;/h3&gt;

&lt;p&gt;To get a response, we can send the sender's info.&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;fib_srv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&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;while&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;fib_srv&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;do_some_work&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; fib(10)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works for a simple 1-to-1 interaction.&lt;/p&gt;

&lt;h3&gt;
  
  
  EX3: Confusion with Multiple Servers
&lt;/h3&gt;

&lt;p&gt;What happens if we add a "Factorial Server"?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&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;fact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="n"&gt;fib_srv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&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;while&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;fact_srv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&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;while&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


&lt;span class="n"&gt;fib_srv&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;fib_srv&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;fact_srv&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;fact_srv&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;do_some_work&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt; &lt;span class="c1"&gt;# Is this the result of fib(10) or fact(10)?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;code&gt;Ractor.receive&lt;/code&gt; is called, you don't know which server sent the message. Furthermore, if a server uses internal worker Ractors, the order of responses might not even match the order of requests.&lt;/p&gt;

&lt;h3&gt;
  
  
  EX4: Identifying with Tags
&lt;/h3&gt;

&lt;p&gt;We can add "tags" (like Symbols) to identify messages. This is similar to how Erlang handles it.&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;fib_srv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&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;while&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="ss"&gt;:fib&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;result&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="n"&gt;fact_srv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&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;while&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="ss"&gt;:fact&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;result&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="n"&gt;fib_srv&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;fib_srv&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;fact_srv&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;fact_srv&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;do_some_work&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive_if&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;
  &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:fib&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="s2"&gt;"fib(&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) = &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:fact&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="s2"&gt;"fact(&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) = &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# or if you want to use specific results, like:&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="ss"&gt;fib20:  &lt;/span&gt;&lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive_if&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;id&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;:fib&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;  &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="ss"&gt;fact10: &lt;/span&gt;&lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive_if&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;id&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;:fact&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="ss"&gt;fact20: &lt;/span&gt;&lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive_if&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;id&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;:fact&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="ss"&gt;fib10:  &lt;/span&gt;&lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive_if&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;id&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;:fib&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;  &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While this works, a problem remains: if an external library calls &lt;code&gt;Ractor.receive&lt;/code&gt; internally, it might "steal" your message before you can call &lt;code&gt;receive_if&lt;/code&gt;. You can only use this safely if you control all the code.&lt;/p&gt;

&lt;h3&gt;
  
  
  EX5: Creating a Channel
&lt;/h3&gt;

&lt;p&gt;We could create a "Channel" using a dedicated Ractor, similar to Go language.&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;fib_srv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&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;while&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;fact_srv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&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;while&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;result&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;# Create a new channel using a Ractor&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new_channel&lt;/span&gt;
  &lt;span class="no"&gt;Ractor&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;while&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
      &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;yield&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&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="n"&gt;fib_srv&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fib10_ch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_channel&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;fib_srv&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fib20_ch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_channel&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;fact_srv&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fact10_ch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_channel&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;fact_srv&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fact20_ch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_channel&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;do_some_work&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="ss"&gt;fib20: &lt;/span&gt;&lt;span class="n"&gt;fib20_ch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;take&lt;/span&gt;   &lt;span class="c1"&gt;# wait for fib(20)&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="ss"&gt;fact10: &lt;/span&gt;&lt;span class="n"&gt;fact10_ch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;take&lt;/span&gt; &lt;span class="c1"&gt;# wait for fact(10)&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="ss"&gt;fib10: &lt;/span&gt;&lt;span class="n"&gt;fib10_ch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;take&lt;/span&gt;   &lt;span class="c1"&gt;# wait for fib(10)&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="ss"&gt;fact20: &lt;/span&gt;&lt;span class="n"&gt;fact10_ch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;take&lt;/span&gt; &lt;span class="c1"&gt;# wait for fact(20)&lt;/span&gt;

&lt;span class="c1"&gt;# or &lt;/span&gt;
&lt;span class="n"&gt;chs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;fib10_ch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fib20_ch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fact10_ch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fact20_ch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;chs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;
  &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;chs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# wait for multiple channels&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
  &lt;span class="n"&gt;chs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works, but:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creating a Ractor for every channel is expensive.&lt;/li&gt;
&lt;li&gt;Performance suffers due to extra object copying.&lt;/li&gt;
&lt;li&gt;Conceptually, it feels less like the "Actor Model" and more like CSP.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I hesitated for five years because this didn't feel "Actor-like." Then, a proposal for "Ractor Channels" (&lt;a href="https://bugs.ruby-lang.org/issues/21121" rel="noopener noreferrer"&gt;Feature #21121&lt;/a&gt;) pushed me to rethink everything, leading to &lt;code&gt;Ractor::Port&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Proposal: &lt;code&gt;Ractor::Port&lt;/code&gt; - Lightweight, Unique, One-Way Endpoints
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Ractor::Port&lt;/code&gt; is a mechanism where "anyone can send to it," but "only the creator can receive from it."&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Channel&lt;/th&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sending&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Anyone&lt;/td&gt;
&lt;td&gt;Anyone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Receiving&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Anyone&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Only the creator&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This small difference makes it much more aligned with the Actor model. It is lightweight to create and has overhead equivalent to &lt;code&gt;Ractor#send&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A Port consists of two elements:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Destination Ractor:&lt;/strong&gt; Where to deliver.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unique Tag:&lt;/strong&gt; An identifier unique to that Port.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To describe the concept, we can define &lt;code&gt;Ractor::Port&lt;/code&gt; with existing (on Ruby 3.4) API.&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;Ractor::Port&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
    &lt;span class="vi"&gt;@r&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;
    &lt;span class="vi"&gt;@tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;genid&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;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;               &lt;span class="c1"&gt;# anywan can send&lt;/span&gt;
    &lt;span class="vi"&gt;@r&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="vi"&gt;@tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&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;receive&lt;/span&gt;                 &lt;span class="c1"&gt;# only the creator can receive&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="vi"&gt;@r&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;
    &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive_if&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="vi"&gt;@tag&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;# receive if the tag is identical&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, it is enough simple and lightweight. Especially the implementation of synchronizations reduced dramatically.&lt;/p&gt;

&lt;p&gt;For example, we can use &lt;code&gt;Ractor::Port&lt;/code&gt; like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&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;port&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="c1"&gt;# `port &amp;lt;&amp;lt; 42` can also be used as an alias&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 42&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this model, you pass a &lt;code&gt;Port&lt;/code&gt; object to indicate, "&lt;strong&gt;I want you to send the data here,&lt;/strong&gt;" and the sender uses &lt;code&gt;Ractor::Port#send&lt;/code&gt; to transmit it. Semantically, this is exactly the same as calling &lt;code&gt;ractor.send(tag, 42)&lt;/code&gt; (and the internal implementation reflects this logic).&lt;/p&gt;

&lt;p&gt;On the receiving side, you use &lt;code&gt;Ractor::Port#receive&lt;/code&gt; to pick up only the messages that were specifically sent to that port.&lt;/p&gt;

&lt;p&gt;In this simple example, since we've only introduced a single Port, it might feel more cumbersome than the traditional &lt;code&gt;Ractor#send&lt;/code&gt;. However, the true significance and utility of this approach become clear when dealing with more complex scenarios involving multiple communication channels.&lt;/p&gt;

&lt;h3&gt;
  
  
  Refactoring with Ports
&lt;/h3&gt;

&lt;p&gt;Now, let's look at how we can rewrite the &lt;code&gt;fib&lt;/code&gt; and &lt;code&gt;fact&lt;/code&gt; servers specifically using &lt;code&gt;Ractor::Port&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;fib_srv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&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;while&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;fact_srv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&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;while&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;fib_srv&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fib10_port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;fib_srv&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fib20_port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;fact_srv&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fact10_port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;fact_srv&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fact20_port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;do_some_work&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;fib10_port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; fib(10)&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;fib20_port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; fib(20)&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;fact10_port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; fact(10)&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;fact20_port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; fact(20)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Essentially, this is just replacing the previous Channel-based example with Ports. While the logic remains similar to an implementation using &lt;code&gt;tag + receive_if&lt;/code&gt;, the key difference here is that &lt;strong&gt;the use of tags is now enforced&lt;/strong&gt;. Additionally, it provides "peace of mind". You no longer have to worry about your messages being accidentally intercepted or "stolen" by a &lt;code&gt;Ractor.receive&lt;/code&gt; call in some other part of the program.&lt;/p&gt;

&lt;p&gt;Furthermore, &lt;code&gt;Ractor.select&lt;/code&gt; has been updated to wait for multiple Ports simultaneously, allowing you to handle results as follows:&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;ports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;fib10_port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fib20_port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fact10_port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fact20_port&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;
  &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;fib10_port&lt;/span&gt;
    &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="ss"&gt;fib10: &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;
  &lt;span class="c1"&gt;# ... handle other cases ...&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"This should not happen (BUG)."&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&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;In this example, if a message arrives at any of the four Ports stored in the &lt;code&gt;ports&lt;/code&gt; array, &lt;code&gt;Ractor.select&lt;/code&gt; returns that specific port along with the received result.&lt;/p&gt;

&lt;p&gt;As a side note, it's worth comparing this to other systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;IO.select&lt;/code&gt;&lt;/strong&gt;: Returns the IO object that is "ready" (e.g., for reading), but you still have to perform the read yourself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Ractor.select&lt;/code&gt;&lt;/strong&gt;: Actually completes the reception process and returns the value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Go's &lt;code&gt;select&lt;/code&gt; statement&lt;/strong&gt;: Requires you to write the processing logic for each branch within the statement itself.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each approach handles the "select" concept slightly differently.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comparison: Port vs. Channels/Queues
&lt;/h3&gt;

&lt;p&gt;First, let's summarize the key advantages of &lt;strong&gt;Port&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Guaranteed Delivery:&lt;/strong&gt; It ensures that messages are delivered &lt;em&gt;only&lt;/em&gt; to the intended Ractor, preventing tag collisions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decoupled Communication:&lt;/strong&gt; You can establish communication paths without relying on &lt;code&gt;Ractor.receive&lt;/code&gt;, which is prone to unintended message consumption ("message stealing").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simpler Primitives:&lt;/strong&gt; It replaces complex and hard-to-compose primitives like &lt;code&gt;.receive_if&lt;/code&gt;, &lt;code&gt;.yield&lt;/code&gt;, and &lt;code&gt;#take&lt;/code&gt; with a cleaner abstraction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Natural Actor Semantics:&lt;/strong&gt; It aligns perfectly with the Actor model semantics that Ruby aims to achieve with Ractor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's discuss about "why Port is Better than Channel (in Practice)".&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Safer than "Channel"

&lt;ul&gt;
&lt;li&gt;When a &lt;code&gt;Port#send&lt;/code&gt; succeeds, it guarantees that the destination Ractor is still alive and running.&lt;/li&gt;
&lt;li&gt;In contrast, with a &lt;strong&gt;Channel&lt;/strong&gt;, there is no guarantee that a Ractor capable of receiving from that channel is currently active.&lt;/li&gt;
&lt;li&gt;While a Port doesn't guarantee the receiver will &lt;em&gt;process&lt;/em&gt; the message (it might ignore it), it eliminates the failure case where a message vanishes into the void because the destination Ractor has already terminated. This removes a major source of non-deterministic bugs and makes the communication model much more predictable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Note:&lt;/strong&gt; This predictability is one of the primary reasons Ruby chose the &lt;strong&gt;Actor model&lt;/strong&gt; over &lt;strong&gt;CSP&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Superior Performance

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lighter Creation:&lt;/strong&gt; Creating a Channel requires allocating container structures to store messages. A Port only requires a lightweight "Ractor + unique ID" data structure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fewer Copies:&lt;/strong&gt; Sending a message via a Channel typically involves two copies: &lt;strong&gt;Sender → Channel&lt;/strong&gt; and &lt;strong&gt;Channel → Receiver&lt;/strong&gt;. With a Port, the message is copied only once: &lt;strong&gt;Sender → Receiver&lt;/strong&gt;. As we move toward Ractor-local GC (separating object spaces), this performance gap will become even more significant.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Simplified Implementation

&lt;ul&gt;
&lt;li&gt;Implementing &lt;code&gt;Port#receive&lt;/code&gt; is straightforward because it only requires locking the receiving Ractor.&lt;/li&gt;
&lt;li&gt;Conversely, &lt;code&gt;yield/take&lt;/code&gt; requires &lt;strong&gt;rendezvous synchronization&lt;/strong&gt;, necessitating locks on both the sender and receiver simultaneously—which is notoriously tricky.&lt;/li&gt;
&lt;li&gt;The current implementation of &lt;code&gt;.select&lt;/code&gt; is &lt;strong&gt;extremely&lt;/strong&gt; difficult and remains unstable in CI (despite two major rewrites). Simplifying the specification to Ports will lead to fewer bugs and faster development.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Personal Note:&lt;/strong&gt; For me, the "Simplicity of Implementation" is the biggest win. Implementing &lt;code&gt;yield/take&lt;/code&gt; and the supporting &lt;code&gt;select&lt;/code&gt; logic was an absolute nightmare. I never managed to completely eliminate the bugs while maintaining high performance (avoiding a VM-wide global lock). Moving to Ports allows us to ditch that complexity.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;They are downsides of Ports&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unfamiliarity:&lt;/strong&gt; The concept of a "Port" isn't widely known, especially for developers coming from Go who are used to Channels.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Abstraction Overhead:&lt;/strong&gt; For classic producer-consumer patterns, you may need an additional layer of abstraction.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, implementing &lt;strong&gt;Multiple Workers&lt;/strong&gt; (Ractors) requires a bit more coordination:&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;controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;

&lt;span class="n"&gt;workers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="no"&gt;WN&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="n"&gt;controller&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;controller&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
      &lt;span class="c1"&gt;# Notify controller that this worker is ready&lt;/span&gt;
      &lt;span class="n"&gt;controller&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;
      &lt;span class="c1"&gt;# Wait for a task from the producer&lt;/span&gt;
      &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;
      &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
      &lt;span class="c1"&gt;# Send the result back to the requested port&lt;/span&gt;
      &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&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="n"&gt;task_q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;result_port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;

&lt;span class="c1"&gt;# In this new API, Ports are Thread-safe!&lt;/span&gt;
&lt;span class="no"&gt;Thread&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;while&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;task_q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;
    &lt;span class="c1"&gt;# Get an available worker&lt;/span&gt;
    &lt;span class="n"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;
    &lt;span class="n"&gt;worker&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="n"&gt;result_port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;param&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="n"&gt;task_q&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;span class="n"&gt;task_q&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;43&lt;/span&gt;
&lt;span class="n"&gt;task_q&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;44&lt;/span&gt;

&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;result_port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this scenario, because the producer logic only supports a single Ractor, you would need a "mediator Ractor" if multiple Ractors were trying to feed tasks to these workers.&lt;/p&gt;

&lt;p&gt;One interesting side effect of this refactoring is that &lt;strong&gt;Ractor Ports now support Threads&lt;/strong&gt;. The previous Ractor API was essentially incompatible with Threads (it was "undefined behavior" territory), but the new, cleaner internal logic made it easy to add support.&lt;/p&gt;

&lt;p&gt;One remaining concern is that it's difficult to implement something like &lt;strong&gt;Go's `context&lt;/strong&gt;&lt;code&gt; using only Ports. I'm still evaluating how much of a limitation this is in practice (and specifically, why &lt;/code&gt;Channel#close` wasn't sufficient for those use cases).&lt;/p&gt;

&lt;h3&gt;
  
  
  Waiting for Ractor Termination
&lt;/h3&gt;

&lt;p&gt;If we remove &lt;code&gt;Ractor#take&lt;/code&gt;, we lose the way to wait for a Ractor to finish and retrieve its block's return value. To address this, I decided to adopt the naming convention from Ruby's &lt;code&gt;Thread&lt;/code&gt; class and introduced &lt;code&gt;Ractor#join&lt;/code&gt; (to wait for termination) and &lt;code&gt;Ractor#value&lt;/code&gt; (to wait and return the block's value).&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;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;   &lt;span class="c1"&gt;# wait for the termination of r&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;  &lt;span class="c1"&gt;#=&amp;gt; 42&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A unique specification here is that &lt;strong&gt;&lt;code&gt;Ractor#value&lt;/code&gt; can be called by at most one Ractor&lt;/strong&gt;. If one Ractor is already waiting for a value via &lt;code&gt;#value&lt;/code&gt;, and another Ractor attempts to call &lt;code&gt;#value&lt;/code&gt; on the same target, it will raise an error.&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;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 42 (Main Ractor executes value)&lt;/span&gt;

&lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;# This will raise an error&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reasoning behind this is safety: since the final result is not accessible by anyone else, it is safe for exactly one Ractor to access it. This allows us to pass the result &lt;strong&gt;without copying&lt;/strong&gt;, meaning an "unshareable object" can be received as-is.&lt;/p&gt;

&lt;p&gt;Actually, &lt;code&gt;Ractor#take&lt;/code&gt; had a similar special behavior for the final return value. However, I always felt it was "dirty" (or inconsistent) for the same method to behave differently depending on the timing. I'm glad we could clean this up with &lt;code&gt;Ractor#value&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Default Port and Compatibility
&lt;/h3&gt;

&lt;p&gt;Every Ractor is equipped with one &lt;strong&gt;default port&lt;/strong&gt; from the moment it is created. It is used, for example, to pass arguments to the Ractor, and it can be accessed via &lt;code&gt;Ractor#default_port&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Standard operations like &lt;code&gt;Ractor#send&lt;/code&gt; and &lt;code&gt;Ractor.receive&lt;/code&gt; are now essentially operations on this default port.&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;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&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="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 42&lt;/span&gt;
  &lt;span class="c1"&gt;# This is equivalent to Ractor.current.default_port.receive&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;span class="c1"&gt;# Semantically the same as r.default_port.send&lt;/span&gt;
&lt;span class="c1"&gt;# (We might restrict access to default_port from outside the Ractor in the future)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Monitor API
&lt;/h3&gt;

&lt;p&gt;To implement &lt;code&gt;join&lt;/code&gt; and &lt;code&gt;value&lt;/code&gt;, I introduced a low-level &lt;strong&gt;Monitor API&lt;/strong&gt; (inspired by Erlang).&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;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;monitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;monitor_port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;monitor_port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; :exited&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a Ractor terminates, it sends the symbol &lt;code&gt;:exited&lt;/code&gt; to the registered &lt;code&gt;monitor_port&lt;/code&gt;. If it crashes due to an exception, it sends &lt;code&gt;:aborted&lt;/code&gt;. I'm not sure yet if we'll add more event types (we'll see if use cases arise).&lt;/p&gt;

&lt;p&gt;This API makes implementing &lt;code&gt;#join&lt;/code&gt; very simple (in fact, it is already implemented this way: &lt;a href="https://github.com/ruby/ruby/blob/9e8fa9bcd7c01af9242f3b0d37c9ad521dc54404/ractor.rb#L547" rel="noopener noreferrer"&gt;see code&lt;/a&gt;). You can also use this to monitor the liveness of multiple Ractors:&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;monitor_port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;

&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;unlimited_task&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;monitor&lt;/span&gt; &lt;span class="n"&gt;monitor_port&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;monitor_port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;
  &lt;span class="n"&gt;do_something&lt;/span&gt; &lt;span class="c1"&gt;# e.g., restart a worker to replace the dead one&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Wait, looking at this now... if I only get &lt;code&gt;:exited&lt;/code&gt;, I won't know &lt;strong&gt;which&lt;/strong&gt; Ractor died. I'll probably need to include the source Ractor information. I'm debating between passing more info to the monitor port or creating a &lt;code&gt;receive&lt;/code&gt; method that identifies the sender. We'll see which is better.)&lt;/p&gt;

&lt;p&gt;I originally designed &lt;code&gt;Ractor#take&lt;/code&gt; five years ago specifically to handle this kind of logic, but it turns out things don't always go as planned!&lt;/p&gt;

&lt;h3&gt;
  
  
  Closing Ports
&lt;/h3&gt;

&lt;p&gt;Ports can be closed using the &lt;code&gt;#close&lt;/code&gt; method.&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;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="kp"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="c1"&gt;# Eventually raises ClosedError when trying to send to a closed port&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Currently, &lt;strong&gt;only the Ractor that created the port&lt;/strong&gt; can close it. Allowing anyone to close a port is a bit more complex (as it requires "waking up" all waiting threads), so I'll keep it this way until there is a clear, essential need for it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other Alternatives Considered
&lt;/h3&gt;

&lt;p&gt;As mentioned, I considered Channel-like mechanisms.&lt;/p&gt;

&lt;p&gt;Since a Port essentially enforces "tagged sends," another idea was to use &lt;code&gt;Ractor#send(tag, ...)&lt;/code&gt;. However, I was concerned about tag collisions. I decided that if we were going to use tags anyway, it would be more "Object-Oriented" to make the tag itself an object, hence, the &lt;code&gt;Port&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;I didn't spend too much time on the name "Port"; it just seemed to fit, and I couldn't think of many better alternatives. I looked into other languages for similar mechanisms but didn't find many equivalents, save for the concept of TCP port numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future Plans
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Ractor::Port&lt;/code&gt; has already been merged into the &lt;code&gt;master&lt;/code&gt; branch (and will be shipped with Ruby 4.0), so you can try it out today. I would love to hear your honest feedback.&lt;/p&gt;

&lt;p&gt;There are still a few "leftover" items I'm considering:&lt;/p&gt;

&lt;h3&gt;
  
  
  Is &lt;code&gt;Ractor::Port.new&lt;/code&gt; too long?
&lt;/h3&gt;

&lt;p&gt;I’ve been wondering if something like &lt;code&gt;Ractor.port&lt;/code&gt; would be better (similar to the feel of &lt;code&gt;IO.pipe&lt;/code&gt;). Since creating a port doesn't feel like a heavy operation, maybe a shorter factory method makes sense. What do you think?&lt;/p&gt;

&lt;h3&gt;
  
  
  Behavior on &lt;code&gt;close&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;As a prerequisite, remember that a &lt;code&gt;Port&lt;/code&gt; is an &lt;strong&gt;unshareable object&lt;/strong&gt;. This means a copy is created whenever it is passed to another Ractor.&lt;/p&gt;

&lt;p&gt;While implementing this, I realized something: even if other Ractors hold a copy of a Port to send messages to, if all local references to that Port within the &lt;em&gt;receiving&lt;/em&gt; Ractor are garbage collected (GC'd), there is no longer anyone left to read from it. In that case, the Port should probably be closed automatically.&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;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&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;port&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;# Sending 10 messages&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 0 (Returns the first message)&lt;/span&gt;
&lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;     &lt;span class="c1"&gt;# The handle to receive the remaining 9 messages is gone!&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="c1"&gt;# The program continues, but those 9 messages stay in memory indefinitely.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In cases like this, I believe the Port should close the moment it is GC'd in the receiving Ractor. This would allow us to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reclaim memory:&lt;/strong&gt; Free up the messages that were already sent (since no one can ever see them).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prevent waste:&lt;/strong&gt; Stop other Ractors from performing unnecessary work by sending messages to a dead-end.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I'm considering using &lt;strong&gt;reference counting&lt;/strong&gt; within the Port implementation to achieve this.&lt;/p&gt;

&lt;p&gt;Also, as I touched on earlier, there is the question of whether &lt;em&gt;other&lt;/em&gt; Ractors (senders) should be allowed to close a Port. It might be a good way to signal "I'm done sending," but it might also complicate the ownership model. The specification here is still likely to evolve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;With the introduction of &lt;code&gt;Ractor::Port&lt;/code&gt;, the Ractor API has undergone a significant overhaul. I believe this refactoring clarifies how Ractors communicate and makes the entire system much more approachable.&lt;/p&gt;

&lt;p&gt;Please take it for a spin and see how it feels!&lt;/p&gt;

&lt;p&gt;And hopefully, this time, we can finally see that "experimental" warning removed from Ractor soon.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>ractor</category>
      <category>parallel</category>
    </item>
    <item>
      <title>Reading the Ruby 3.4 NEWS with professionals (English translation)</title>
      <dc:creator>Koichi Sasada</dc:creator>
      <pubDate>Fri, 27 Dec 2024 02:45:49 +0000</pubDate>
      <link>https://forem.com/ko1/reading-the-ruby-34-news-with-professionals-english-translation-47a4</link>
      <guid>https://forem.com/ko1/reading-the-ruby-34-news-with-professionals-english-translation-47a4</guid>
      <description>&lt;p&gt;This article is Japanese-&amp;gt;English translation of &lt;a href="https://product.st.inc/entry/2024/12/25/154728" rel="noopener noreferrer"&gt;プロと読み解くRuby 3.4 NEWS - STORES Product Blog&lt;/a&gt;, mainly using DeepL.&lt;/p&gt;

&lt;p&gt;This is Koichi Sasada (ko1) and Yusuke Endoh (mame) from the Technology Infrastructure Group in the Technology Division at STORES, Inc. We are developing the Ruby interpreter (MRI: Matz Ruby Implementation, or the ruby command). We are professional Ruby committers because we are paid to develop Ruby.&lt;/p&gt;

&lt;p&gt;Ruby 3.4.0 was released today, December 25th, as the annual Christmas release (&lt;a href="https://www.ruby-lang.org/en/news/2024/12/25/ruby-3-4-0-released/" rel="noopener noreferrer"&gt;Ruby 3.4.0 Release&lt;/a&gt;). This year, we will also be explaining the &lt;a href="https://github.com/ruby/ruby/blob/v3_4_0/NEWS.md" rel="noopener noreferrer"&gt;NEWS.md file&lt;/a&gt; for Ruby 3.4 on the STORES Product Blog (incidentally, this will be an article for the &lt;a href="https://product.st.inc/entry/2024/11/18/162024" rel="noopener noreferrer"&gt;STORES Advent Calendar 2024&lt;/a&gt;, written in Japanese). Please see the previous article for an explanation of what a NEWS file is.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://techlife.cookpad.com/entry/2018/12/25/110240" rel="noopener noreferrer"&gt;Reading the Ruby 2.6 NEWS with professionals - Cookpad Developers Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://techlife.cookpad.com/entry/2019/12/25/121834" rel="noopener noreferrer"&gt;Reading the Ruby 2.7 NEWS with professionals - Cookpad Developers Blog&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://techlife.cookpad.com/entry/2020/12/25/155741" rel="noopener noreferrer"&gt;Reading the Ruby 3.0 NEWS with professionals - Cookpad Developers Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://techlife.cookpad.com/entry/2021/12/25/220002" rel="noopener noreferrer"&gt;Reading the Ruby 3.1 NEWS with professionals - Cookpad Developers Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://techlife.cookpad.com/entry/2022/12/26/121950" rel="noopener noreferrer"&gt;Reading the Ruby 3.2 NEWS with professionals - Cookpad Developers Blog&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://product.st.inc/entry/2023/12/25/160504" rel="noopener noreferrer"&gt;Reading the Ruby 3.3 NEWS with professionals - STORES Product Blog&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article not only explains the new features, but also describes the background to the changes and the difficulties involved, to the best of our recollection.&lt;/p&gt;

&lt;p&gt;Ruby 3.4 saw almost no changes to the language (grammar). This was because the decision was made to hold off on any grammar changes in order to focus on the changing the parser that interprets the grammar to Prism.&lt;/p&gt;

&lt;p&gt;The main changes in Ruby 3.4 are as follows (excerpt from the release notes)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;it&lt;/code&gt; is added&lt;/li&gt;
&lt;li&gt;The default parser is changed to Prism&lt;/li&gt;
&lt;li&gt;The Socket library is updated to support Happy Eyeballs Version 2 (RFC 8305)&lt;/li&gt;
&lt;li&gt;YJIT is updated&lt;/li&gt;
&lt;li&gt;Modular GC&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, we will introduce the items in the NEWS file, including these.&lt;/p&gt;

&lt;h2&gt;
  
  
  Language Changes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Block parameter &lt;code&gt;it&lt;/code&gt; is available
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;it&lt;/code&gt; is added to reference a block parameter. [&lt;a href="https://bugs.ruby-lang.org/issues/18980" rel="noopener noreferrer"&gt;Feature #18980&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;it&lt;/code&gt; reference to a block parameter without naming it has finally been introduced.&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;ary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"baz"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;ary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upcase&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;#=&amp;gt; ["FOO", "BAR", "BAZ"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;it&lt;/code&gt; makes it easier to write simple blocks that are easier to read.&lt;/p&gt;

&lt;h4&gt;
  
  
  Difference from numbered parameter
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;it&lt;/code&gt; is roughly the same as &lt;code&gt;_1&lt;/code&gt; in numbered parameter. However, the restrictions on where it can be written are a little more relaxed.&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="s2"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="s2"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upcase&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "BAR"&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="c1"&gt;# A syntax error if you write _1 here&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="s2"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="s2"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upcase&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "BAR"&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upcase&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "FOO" # this is allowed with `it`&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even though it is allowed, it is better to avoid using &lt;code&gt;it&lt;/code&gt; in anything other than one-line blocks, as it makes it difficult to understand what &lt;code&gt;it&lt;/code&gt; refers to.&lt;/p&gt;

&lt;p&gt;Also, while numbered parameters allow you to access the n-th argument with &lt;code&gt;_2&lt;/code&gt; or &lt;code&gt;_3&lt;/code&gt;, &lt;code&gt;it&lt;/code&gt; does not have such a function.&lt;/p&gt;

&lt;p&gt;By the way, it is not allowed to refer to &lt;code&gt;it&lt;/code&gt; and numbered parameters in the same block.&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="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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;# This is a syntax error&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Background to the introduction of &lt;code&gt;it&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;When numbered parameters were introduced, there were a number of proposals for how to write them, and &lt;code&gt;it&lt;/code&gt; was one of them &lt;a href="https://bugs.ruby-lang.org/issues/15897" rel="noopener noreferrer"&gt;[Feature #15897]&lt;/a&gt;. Or rather, I was the one who proposed it.&lt;/p&gt;

&lt;p&gt;There were quite a few people who liked the name &lt;code&gt;it&lt;/code&gt;, but there were compatibility issues. The method name &lt;code&gt;it&lt;/code&gt; is used a lot in existing code (mainly RSpec), so if it was simply made into a keyword, it would cause a fatal incompatibility. To avoid this, we invented a tricky trick of “making the keyword only for the call of &lt;code&gt;it&lt;/code&gt; with no arguments”.&lt;/p&gt;

&lt;p&gt;However, &lt;code&gt;it&lt;/code&gt; does not have a function to read the n-th argument, and because we were not sure whether that was okay, the notation &lt;code&gt;_1&lt;/code&gt; was eventually adopted.&lt;/p&gt;

&lt;p&gt;After that, as people started to use numbered parameters in practice, it became clear that the function to read the second and subsequent arguments was not used very much, and that &lt;code&gt;_1&lt;/code&gt; was overwhelmingly more commonly used.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;_1&lt;/code&gt;, especially, the number part "1", had a cognitively taxing issue. Therefore, it was decided to introduce &lt;code&gt;it&lt;/code&gt; as a more visually friendly alternative to &lt;code&gt;_1&lt;/code&gt;. In Ruby 3.3, the un-parameterized call to the method &lt;code&gt;it&lt;/code&gt; was warned to facilitate the transition, and it is now available in Ruby 3.4.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  frozen_string_literal will be default and a migration path is added
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;String literals in files without a &lt;code&gt;frozen_string_literal&lt;/code&gt; comment now emit a deprecation warning
when they are mutated.
These warnings can be enabled with &lt;code&gt;-W:deprecated&lt;/code&gt; or by setting &lt;code&gt;Warning[:deprecated] = true&lt;/code&gt;.
To disable this change, you can run Ruby with the &lt;code&gt;--disable-frozen-string-literal&lt;/code&gt;
command line argument. [&lt;a href="https://bugs.ruby-lang.org/issues/20205" rel="noopener noreferrer"&gt;Feature #20205&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There used to be a feature called &lt;code&gt;frozen_string_literal&lt;/code&gt; that would freeze string literals, and this will be the default. However, this will not be the case in Ruby 3.4, but rather in a later version (so it's “in the direction of”).&lt;/p&gt;

&lt;p&gt;As a concrete change in Ruby 3.4, when you run with the &lt;code&gt;-w&lt;/code&gt; or &lt;code&gt;-w:deprecated&lt;/code&gt; option (when you enable deprecated warnings), you will now get warnings about changes (destructive operations) to string literals.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ruby -W:deprecated -e '"" &amp;lt;&amp;lt; ?a'
-e:1: warning: literal string will be frozen in the future (run with --debug-frozen-string-literal for more information)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As the warning message says, if you add &lt;code&gt;--debug-frozen-string-literal&lt;/code&gt;, it will display where the string that was subject to the destructive operation was created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ruby -W:deprecated --debug-frozen-string-literal -e 's = ""
s &amp;lt;&amp;lt; ?a'
-e:2: warning: literal string will be frozen in the future
  # warning because the destructive operation is performed on the second line
-e:1: info: the string was created here
  # displays that it is for the string created on the first line
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As before, the &lt;code&gt;frozen_string_literal&lt;/code&gt; pragma is available.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ruby -W:deprecated -e '# frozen_string_literal: true
&amp;gt; "" &amp;lt;&amp;lt; ?a'
-e:2:in '&amp;lt;main&amp;gt;': can't modify frozen String: "", created at -e:2 (FrozenError)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if you specify &lt;code&gt;# frozen_string_literal: false&lt;/code&gt;, which was the default until now and probably no one applied, this warning will no longer be displayed. In other words, it is a declaration that string literals are not frozen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ruby -W:deprecated -e '# frozen_string_literal: false
"" &amp;lt;&amp;lt; ?a'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to specify this behavior for the entire application, specify &lt;code&gt;--enable-frozen-string-literal&lt;/code&gt; or &lt;code&gt;--disable-frozen-string-literal&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ruby -W:deprecated --enable-frozen-string-literal -e 'a = ""
a &amp;lt;&amp;lt; ?a'
-e:2:in '&amp;lt;main&amp;gt;': can't modify frozen String: "" (FrozenError)

$ ruby -W:deprecated --disable-frozen-string-literal -e 'a = ""
a &amp;lt;&amp;lt; ?a'
# No warnings
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In other words, if you specify &lt;code&gt;--disable-frozen-string-literal&lt;/code&gt;, the warning will not be displayed in the same way as before.&lt;/p&gt;

&lt;p&gt;As the warning “warning: literal string will be frozen in the future” indicates, the destructive operation on the frozen string literals will cause an error. Looking at the ticket, it says that it will be changed to an error from Ruby 4.0. Well, it's just a plan, so we don't know what will happen.&lt;/p&gt;

&lt;p&gt;Internally, strings that issue a warning when a destructive operation is performed (and will become frozen in the future) are called "Chilled Strings", but this is knowledge that is not necessary if you are using Ruby normally.&lt;/p&gt;

&lt;p&gt;Background to this story:&lt;/p&gt;

&lt;p&gt;The idea of freezing string literals was planed to introduce in Ruby 3.0, and there was a lot of discussion about it in Ruby 2.x era.&lt;/p&gt;

&lt;p&gt;The main reasons for this are thought to be (1) there are performance benefits because if string literals are frozen, the same string can be returned at that point, and (2) it is easier to program if they are frozen as much as possible, but I think the main reason you hear about it is (1).&lt;/p&gt;

&lt;p&gt;However, it has not been introduced so far because the disadvantages, such as incompatibility, are greater than the benefits. However, there are many people who like this feature, and there are many people who write &lt;code&gt;# frozen_string_literal: true&lt;/code&gt; at the beginning of their scripts. It is also often inserted automatically with editors support.&lt;/p&gt;

&lt;p&gt;Therefore, if everyone is writing it in the first place, why not make this behavior the default and not have to write this troublesome pragma? This is the reason why we are trying to make it the default again this time.&lt;/p&gt;

&lt;p&gt;By the way, according to the comments on &lt;a href="https://bugs.ruby-lang.org/issues/20205#note-65" rel="noopener noreferrer"&gt;Feature #20205: Enable &lt;code&gt;frozen_string_literal&lt;/code&gt; by default - Ruby master - Ruby Issue Tracking System&lt;/a&gt;, the following percentage of the files in the source code of published RubyGems specify &lt;code&gt;# frozen-string-literal: true&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Number of gems that have `# frozen-string-literal: true` in all .rb files

  among all public gems: 14254 / 175170 (8.14%)
  per-file basis: 445132 / 3051922 (14.59%)

  among public gems with the latest version released in 2015-present: 14208 / 101904 (13.94%)
  per-file basis: 443460 / 1952225 (22.72%)

  among public gems with the latest version released in 2020-present: 11974 / 41205 (29.06%)
  per-file basis: 389559 / 1136445 (34.28%)

  among public gems with the latest version released in 2022-present: 9121 / 26721 (34.13%)
  per-file basis: 329742 / 848061 (38.88%)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;About 15% overall. It seems that it is included in about 35% of the files that have been released recently (since 2022). How about it? Was it a lot? Was it a little?&lt;/p&gt;

&lt;p&gt;Up until now, it was easy to prepare a string (buffer) that could be changed casually like &lt;code&gt;s = ""; s &amp;lt;&amp;lt; s2&lt;/code&gt;, but now it requires a little more effort, such as &lt;code&gt;s = "".dup; s &amp;lt;&amp;lt; s2&lt;/code&gt; or &lt;code&gt;s = +""; s &amp;lt;&amp;lt; s2&lt;/code&gt;, so I think it will be a fairly big change in Ruby's mindset.&lt;/p&gt;

&lt;p&gt;But I guess it's not that big a deal for people who have written &lt;code&gt;# frozen_string_literal: true&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;By the way, &lt;code&gt;--enable-frozen-string-literal&lt;/code&gt; was introduced when we were discussing Ruby 2.x in order to test what would happen if string literals were frozen by default (I wrote a patch a long time ago). The &lt;code&gt;--disable-frozen-string-literal&lt;/code&gt; option, which was introduced at the same time, has finally seen the light of day.&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;

&lt;p&gt;Incidentally, I really hates this change. Ruby objects should be mutable in principle.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  The conditions for whether &lt;code&gt;String#+@&lt;/code&gt; dups have changed
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;String#+@&lt;/code&gt; now duplicates when mutating the string would emit a deprecation warning, offered as a replacement for the &lt;code&gt;str.dup if str.frozen?&lt;/code&gt; pattern.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is on the heels of defaulting to frozen_string_literal.&lt;/p&gt;

&lt;p&gt;Until now, &lt;code&gt;String#+@&lt;/code&gt;, i.e. &lt;code&gt;+"foo"&lt;/code&gt; would do nothing if the string was mutable, but it would now duplicate the string if it was a mutable string and would warn about destructive operations (== a chilled string).&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;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"str"&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; false, i.e. it is a different object&lt;/span&gt;

&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"str"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dup&lt;/span&gt; &lt;span class="c1"&gt;# No warning is given if you make a destructive change to c&lt;/span&gt;
&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; true, i.e. the same object without dup&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means that the only behaviour that used to be like for &lt;code&gt;c&lt;/code&gt; untile now, is now like for &lt;code&gt;a&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In other words, if you see the warning in the example, you can now decide to add &lt;code&gt;+&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;

&lt;h3&gt;
  
  
  Keyword arguments &lt;code&gt;**&lt;/code&gt; can now be passed &lt;code&gt;nil&lt;/code&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Keyword splatting &lt;code&gt;nil&lt;/code&gt; when calling methods is now supported.
&lt;code&gt;**nil&lt;/code&gt; is treated similarly to &lt;code&gt;**{}&lt;/code&gt;, passing no keywords, and not calling any conversion methods.  [&lt;a href="https://bugs.ruby-lang.org/issues/20064" rel="noopener noreferrer"&gt;Bug #20064&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is now possible to pass &lt;code&gt;nil&lt;/code&gt; to &lt;code&gt;**&lt;/code&gt; to pass a hash as a keyword argument.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&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;"default"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Hello, &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Pass a hash as a keyword argument&lt;/span&gt;
&lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&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;"mame"&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="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; Hello, mame&lt;/span&gt;

&lt;span class="c1"&gt;# If you pass nil instead of a hash, it is the same as specifying an empty hash&lt;/span&gt;
&lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; Hello, default&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was allowed because Matz was persuaded by the following code 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;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;key?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Personally, I wouldn't recommend using the &lt;code&gt;nil&lt;/code&gt; implicitly returned by &lt;code&gt;if&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  Block passing is no longer allowed in index assignment
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Block passing is no longer allowed in index assignment (e.g. &lt;code&gt;a[0, &amp;amp;b] = 1&lt;/code&gt;).  [&lt;a href="https://bugs.ruby-lang.org/issues/19918" rel="noopener noreferrer"&gt;Bug #19918&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don't think anyone would do this, but it was previously possible to pass a block as &lt;code&gt;a[0, &amp;amp;b] = 1&lt;/code&gt;.&lt;br&gt;
If you were to do it properly, there were a lot of things to consider, such as the order of evaluation.&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;
&lt;h3&gt;
  
  
  Keyword arguments are no longer allowed in index assignment (e.g. &lt;code&gt;a[0, kw: 1] = 2&lt;/code&gt;).
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Keyword arguments are no longer allowed in index assignment (e.g. &lt;code&gt;a[0, kw: 1] = 2&lt;/code&gt;).  [&lt;a href="https://bugs.ruby-lang.org/issues/20218" rel="noopener noreferrer"&gt;Bug #20218&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The same applies here, but the way of writing &lt;code&gt;a[0, kw: 1] = 2&lt;/code&gt; is now prohibited.&lt;br&gt;
It seems that there were people actually using it, but it seems that it has now been prohibited.&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;
&lt;h3&gt;
  
  
  The toplevel constant &lt;code&gt;Ruby&lt;/code&gt; is reserved
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The toplevel name &lt;code&gt;::Ruby&lt;/code&gt; is reserved now, and the definition will be warned when &lt;code&gt;Warning[:deprecated]&lt;/code&gt;.  [&lt;a href="https://bugs.ruby-lang.org/issues/20884" rel="noopener noreferrer"&gt;Feature #20884&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The toplevel constant &lt;code&gt;Ruby&lt;/code&gt; has been reserved. Defining it will result in a warning.&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;# Run with `ruby -w`&lt;/span&gt;

&lt;span class="c1"&gt;# A warning is given when you define a Ruby module yourself&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Ruby&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; warning: ::Ruby is reserved for Ruby 3.5&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# A warning is also given when you assign to the Ruby constant&lt;/span&gt;
&lt;span class="no"&gt;Ruby&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; warning: ::Ruby is reserved for Ruby 3.5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Ruby 3.5, the &lt;code&gt;Ruby&lt;/code&gt; module will be defined, and it is planned to contain meta-functions related to Ruby that should be provided commonly by Ruby implementations. What will be placed in the module has not yet been decided, but it is likely that &lt;code&gt;Ruby::VERSION&lt;/code&gt; will be defined as an alias for &lt;code&gt;RUBY_VERSION&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h2&gt;
  
  
  Update of built-in classes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Array#fetch_values&lt;/code&gt; was added
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Array#fetch_values&lt;/code&gt; was added. [&lt;a href="https://bugs.ruby-lang.org/issues/20702" rel="noopener noreferrer"&gt;Feature #20702&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since &lt;code&gt;Hash#fetch_values&lt;/code&gt; exists, &lt;code&gt;Array#fetch_values&lt;/code&gt; is also added.&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="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"baz"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;fetch_values&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;         &lt;span class="c1"&gt;#=&amp;gt; ["bar", "baz"]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"baz"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;fetch_values&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="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;         &lt;span class="c1"&gt;#=&amp;gt; index 4 outside of array bounds: -3...3 (IndexError)&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"baz"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;fetch_values&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="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;#=&amp;gt; ["bar", 42]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It seems that &lt;code&gt;Array#values_at&lt;/code&gt; returns nil for out-of-bounds access, whereas &lt;code&gt;Array#fetch_values&lt;/code&gt; raises an IndexError (or calls the block if given).&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Exception#set_backtrace&lt;/code&gt; now accepts arrays of &lt;code&gt;Thread::Backtrace::Location&lt;/code&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Exception#set_backtrace&lt;/code&gt; now accepts arrays of &lt;code&gt;Thread::Backtrace::Location&lt;/code&gt;. &lt;code&gt;Kernel#raise&lt;/code&gt;, &lt;code&gt;Thread#raise&lt;/code&gt; and &lt;code&gt;Fiber#raise&lt;/code&gt; also accept this new format. [&lt;a href="https://bugs.ruby-lang.org/issues/13557" rel="noopener noreferrer"&gt;Feature #13557&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The background explanation is very long. This is not a feature that we want to be used very much, so please only read it if you really want to.&lt;/p&gt;

&lt;p&gt;First of all, Ruby exception backtraces can be programmatically retrieved using the &lt;code&gt;Exception#backtrace&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;
  &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"exception"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="n"&gt;foo&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt;
  &lt;span class="n"&gt;pp&lt;/span&gt; &lt;span class="vg"&gt;$!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backtrace&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; ["test.rb:2:in 'Object#foo'", "test.rb:6:in '&amp;lt;main&amp;gt;'"]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, if you wanted to extract just the file name from each line of the backtrace, you had to use regular expressions to extract it. That's why the &lt;code&gt;Exception#backtrace_locations&lt;/code&gt; method was introduced. This returns an array of &lt;code&gt;Thread::Backtrace::Location&lt;/code&gt; instances, so you can easily extract the file name, line number, etc.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;
  &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"exception"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="n"&gt;foo&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt;
  &lt;span class="n"&gt;loc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vg"&gt;$!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backtrace_locations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;

  &lt;span class="c1"&gt;# Looks like a string, but is actually an instance of Thread::Backtrace::Location&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;loc&lt;/span&gt;        &lt;span class="c1"&gt;#=&amp;gt; "test.rb:2:in 'Object#foo'"&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;  &lt;span class="c1"&gt;#=&amp;gt; Thread::Backtrace::Location&lt;/span&gt;

  &lt;span class="c1"&gt;# You can easily get the file name and line number&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;   &lt;span class="c1"&gt;#=&amp;gt; "test.rb"&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lineno&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 2&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also get the backtrace at the time of the call using &lt;code&gt;Kernel#caller_locations&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;
  &lt;span class="n"&gt;loc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;caller_locations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;loc&lt;/span&gt;         &lt;span class="c1"&gt;#=&amp;gt; "test.rb:8:in '&amp;lt;main&amp;gt;'"&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;    &lt;span class="c1"&gt;#=&amp;gt; "test.rb"&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lineno&lt;/span&gt;  &lt;span class="c1"&gt;#=&amp;gt; 8&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;foo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By the way, you can replace the exception backtrace with the third argument of &lt;code&gt;raise&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;
  &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"exception"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"bar"&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;begin&lt;/span&gt;
  &lt;span class="n"&gt;foo&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt;
  &lt;span class="n"&gt;pp&lt;/span&gt; &lt;span class="vg"&gt;$!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backtrace&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; ["foo", "bar"]&lt;/span&gt;
  &lt;span class="n"&gt;pp&lt;/span&gt; &lt;span class="vg"&gt;$!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backtrace_locations&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; nil&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the backtrace is specified as an array of strings like this, the interpreter cannot know the actual file name or line number, so it cannot create &lt;code&gt;Thread::Backtrace::Location&lt;/code&gt;. Therefore, &lt;code&gt;Exception#backtrace_locations&lt;/code&gt; returns &lt;code&gt;nil&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, what happens if you specify &lt;code&gt;caller_locations&lt;/code&gt; instead of an array of strings as the third argument to &lt;code&gt;raise&lt;/code&gt;?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Ruby 3.3&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;
  &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"exception"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;caller_locations&lt;/span&gt;
    &lt;span class="c1"&gt;#=&amp;gt; in `set_backtrace': backtrace must be Array of String (TypeError)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;foo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This raises an error because there is a validation in &lt;code&gt;Exception#set_backtrace&lt;/code&gt; that &lt;code&gt;backtrace&lt;/code&gt; must be an array of strings.&lt;/p&gt;

&lt;p&gt;Here's the change we're making. &lt;code&gt;Exception#set_backtrace&lt;/code&gt; now accepts an array of &lt;code&gt;Thread::Backtrace::Location&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Ruby 3.4&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;
  &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"exception"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;caller_locations&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="n"&gt;foo&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt;
  &lt;span class="n"&gt;pp&lt;/span&gt; &lt;span class="vg"&gt;$!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backtrace_locations&lt;/span&gt;  &lt;span class="c1"&gt;#=&amp;gt; ["test.rb:6:in '&amp;lt;main&amp;gt;'"]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It seems that you can use this when you want to cut the backtrace or copy it from another exception. However, as I explained at length, if you try to fake the backtrace, it will make debugging more difficult, so I think it's better not to do it.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  A mechanism for offloading time-consuming operations using the Fiber Scheduler has been introduced
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;An optional &lt;code&gt;Fiber::Scheduler#blocking_operation_wait&lt;/code&gt; hook allows blocking operations to be moved out of the event loop in order to reduce latency and improve multi-core processor utilization. [&lt;a href="https://bugs.ruby-lang.org/issues/20876" rel="noopener noreferrer"&gt;Feature #20876&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Fiber scheduler can now accept a &lt;code&gt;blocking_operation_wait&lt;/code&gt; callback. This callback is provided to allow the Fiber scheduler to create a separate thread to offload time-consuming operations that attempt to release the GVL, such as zlib processing.&lt;/p&gt;

&lt;p&gt;In practice, I think it would be difficult to use, so I think it would be better to leave it to the framework.&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;IO::Buffer#copy&lt;/code&gt; now releases the GVL
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;IO::Buffer#copy can release the GVL, allowing other threads to run while copying data. [&lt;a href="https://bugs.ruby-lang.org/issues/20902" rel="noopener noreferrer"&gt;Feature #20902&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When executing &lt;code&gt;IO::Buffer#copy&lt;/code&gt;, the GVL is now released, allowing other threads to run. This can also be handled by &lt;code&gt;blocking_operation_wait&lt;/code&gt; we explained before.&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;GC.config&lt;/code&gt; added to allow accessing configurations on the Garbage Collector &amp;amp; new control over generational GC
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GC.config&lt;/code&gt; added to allow setting configuration variables * &lt;code&gt;GC.config&lt;/code&gt; added to allow setting configuration variables on the Garbage Collector. [&lt;a href="https://bugs.ruby-lang.org/issues/20443" rel="noopener noreferrer"&gt;Feature #20443&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;GC configuration parameter &lt;code&gt;rgengc_allow_full_mark&lt;/code&gt; introduced.  When &lt;code&gt;false&lt;/code&gt; GC will only mark young objects. Default is &lt;code&gt;true&lt;/code&gt;.  [&lt;a href="https://bugs.ruby-lang.org/issues/20443" rel="noopener noreferrer"&gt;Feature #20443&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GC has various settings, and the &lt;code&gt;GC.config&lt;/code&gt; method has been added to access those settings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ruby -e 'pp GC.config'
{rgengc_allow_full_mark: true, implementation: “default”}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can change the settings like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ruby -e 'p GC.config(rgengc_allow_full_mark: false)'
{rgengc_allow_full_mark: false}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The changed settings are returned. Hmm, I wonder if it's okay not to return the values before the change...&lt;/p&gt;

&lt;p&gt;Currently, there are two types of settings that can be taken: the &lt;code&gt;rgengc_allow_full_mark&lt;/code&gt; setting and the &lt;code&gt;implementation&lt;/code&gt; setting. I think that the number of these settings will continue to increase in the future.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;implementation&lt;/code&gt; setting, which we will introduce later in this article, is being changed so that the GC implementation can be replaced, and the name of the GC implementation is entered here (of course, it is read-only). The example value is “default”, so we can see that it is the same GC implementation as before.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;rgengc_allow_full_mark&lt;/code&gt; setting determines whether or not to allow time-consuming major GC to target objects in older generations in generational GC. The default is &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you set this to &lt;code&gt;false&lt;/code&gt;, major GC will not occur even if there are many objects in the old generation (more precisely, the old objects are not incremented while the configuration is &lt;code&gt;false&lt;/code&gt;). This was introduced to create a modern version of the old OoB GC. In other words, it is now possible to control the system so that major GC, which takes time, is prohibited while requests are processed, and perform major GC when there is time to spare. I don't know much about it, but I think it will be  included in Rails (this feature was included based on a discussion about making OOB GC the default in Ruby on Rails (&lt;a href="https://github.com/rails/rails/issues/50449" rel="noopener noreferrer"&gt;Run GC out-of-band by default · Issue #50449 · rails/rails&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Let's try it out a bit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ruby -e 'GC.config(rgengc_allow_full_mark: false)
           pp GC.stat;
           a = []
           10_000_000.times{ a &amp;lt;&amp;lt; [] }
           pp GC.stat'
{count: 4,
 minor_gc_count: 2,
 major_gc_count: 2,
 old_objects: 12017,
 (omitted)
}
{count: 17,
 minor_gc_count: 16,
 major_gc_count: 2,
 old_objects: 12017,
 (omitted)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;major_gc_count&lt;/code&gt;, that is, we can see that a major GC has not occurred. Also, since the number of &lt;code&gt;old_objects&lt;/code&gt; has not changed, we can see that the objects did not become old-generation objects in the first place.&lt;/p&gt;

&lt;p&gt;If you check &lt;code&gt;GC.latest_gc_info(:needs_major_by)&lt;/code&gt;, you can see whether a major GC is needed now (if it's truthy, it wants to do a major GC).&lt;/p&gt;

&lt;p&gt;It's a difficult feature to handle, so it's probably best to leave it to the framework.&lt;/p&gt;

&lt;p&gt;By the way, the &lt;code&gt;GC.config&lt;/code&gt; method itself was introduced as a general &lt;code&gt;GC.config&lt;/code&gt; method because it was thought that more settings would be added, based on discussions about how to handle the &lt;code&gt;rgengc_allow_full_mark&lt;/code&gt; setting.&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;

&lt;h3&gt;
  
  
  It is now possible to specify the capacity required when generating a hash
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Hash.new&lt;/code&gt; now accepts an optional &lt;code&gt;capacity:&lt;/code&gt; argument, to preallocate the hash with a given capacity.
This can improve performance when building large hashes incrementally by saving on reallocation and  rehashing of keys. [&lt;a href="https://bugs.ruby-lang.org/issues/19236" rel="noopener noreferrer"&gt;Feature #19236&lt;/a&gt;]
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="mi"&gt;10_000_000&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When creating a hash with 10 million elements, this program allocates memory during iteration, but if you know that you're going to create 10 million elements from the start, it seems more efficient to allocate the memory all at once at the start.&lt;/p&gt;

&lt;p&gt;So, we've changed it so that &lt;code&gt;Hash.new(capacity: n)&lt;/code&gt; allocates memory for n elements at the start. This is likely to be faster than allocating memory a little at a time. Let's try it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ time ruby -e 'n = 10_000_000; h = Hash.new; n.times{ h[it] = it }'

real    0m2.842s
user    0m2.485s
sys     0m0.294s

$ time ruby -e 'n = 10_000_000; h = Hash.new(capacity: n); n.times{ h[it] = it }'

real    0m2.122s
user    0m1.996s
sys     0m0.072s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;capacity:&lt;/code&gt; makes it a little faster.&lt;/p&gt;

&lt;p&gt;To be honest, I don't think there are many cases where this would be effective, but if you find one, please give it a try.&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;

&lt;h3&gt;
  
  
  Integer multiplication no longer returns &lt;code&gt;Float::INFINITY&lt;/code&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Integer#**&lt;/code&gt; used to return &lt;code&gt;Float::INFINITY&lt;/code&gt; when the return value is large, but now returns an Integer. If the return value is extremely large, it raises an exception.  [&lt;a href="https://bugs.ruby-lang.org/issues/20811" rel="noopener noreferrer"&gt;Feature #20811&lt;/a&gt;]&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Rational#**&lt;/code&gt; used to return &lt;code&gt;Float::INFINITY&lt;/code&gt; or &lt;code&gt;Float::NAN&lt;/code&gt; when the numerator of the return value is large, but now returns a Rational. If it is extremely large, it raises an exception. [&lt;a href="https://bugs.ruby-lang.org/issues/20811" rel="noopener noreferrer"&gt;Feature #20811&lt;/a&gt;]&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the result of an integer power of an integer is a very large number, Ruby 3.3 and earlier would return &lt;code&gt;Float::INFINITY&lt;/code&gt;, but from Ruby 3.4, it will now calculate the result directly, even if it takes a long time.&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;# Ruby 3.3: Returns Float::INFINITY while also issuing a warning "warning: in a**b, b may be too big"&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;10000000&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; Infinity&lt;/span&gt;

&lt;span class="c1"&gt;# Ruby 3.4: Calculates as is&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;10000000&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 10000000000000000000000000000.....&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trigger of this change was the news that a new largest prime number had been discovered in October this year. Endoh tried to calculate it in Ruby by executing &lt;code&gt;2**136279841-1&lt;/code&gt;, but it returned &lt;code&gt;Float::INFINITY&lt;/code&gt;. A disappointing experience. In fact, I had the same disappointing experience six years ago when the previous largest prime number was discovered, and I thought I would be disappointed again when the next largest prime number is discovered if nothing was done, so I proposed that we change it.&lt;/p&gt;

&lt;p&gt;The original behavior was probably to avoid time-consuming calculations, but it was difficult to imagine a situation where &lt;code&gt;INFINITY&lt;/code&gt; would be returned and be a happy situation, and also, with the use of GMP, even large prime numbers are not so slow anymore (it takes about 1 second to calculate &lt;code&gt;2**136279841-1&lt;/code&gt; on my laptop, and about 10 seconds for the radix conversion for display), so it was decided that calculations could be done as they were.&lt;/p&gt;

&lt;p&gt;Also, Rational integer powers have changed in the same way.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  A byte-based version of &lt;code&gt;MatchData#begin&lt;/code&gt; has been introduced
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MatchData#bytebegin&lt;/code&gt; and &lt;code&gt;MatchData#byteend&lt;/code&gt; have been added. [&lt;a href="https://bugs.ruby-lang.org/issues/20576" rel="noopener noreferrer"&gt;Feature #20576&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;MatchData#bytebegin&lt;/code&gt; has been introduced as a byte-based version of &lt;code&gt;MatchData#begin&lt;/code&gt;. The same applies to &lt;code&gt;#byteend&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="s2"&gt;"あいう"&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/い/&lt;/span&gt;

&lt;span class="c1"&gt;# The position of “い” is the first character...second character&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="vg"&gt;$~&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 1&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="vg"&gt;$~&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;#=&amp;gt; 2&lt;/span&gt;

&lt;span class="c1"&gt;# “い” is at bytes 3...6&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="vg"&gt;$~&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bytebegin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 3&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="vg"&gt;$~&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;byteend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;#=&amp;gt; 6&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Object#singleton_method&lt;/code&gt; now sees modules extended
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Object#singleton_method&lt;/code&gt; now returns methods in modules prepended to or included in the  receiver's singleton class. [&lt;a href="https://bugs.ruby-lang.org/issues/20620" rel="noopener noreferrer"&gt;Bug #20620&lt;/a&gt;]
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&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="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&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;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;singleton_method&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="nf"&gt;call&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 1&lt;/span&gt;
&lt;span class="c1"&gt;# Previously, this raised an error: singleton_method': undefined singleton method `a'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It seems that &lt;code&gt;Object#singleton_method&lt;/code&gt; did not return the method of the extended module before, but it now works properly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ractor can now require
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;require&lt;/code&gt; in Ractor is allowed. The requiring process will be run on  the main Ractor. &lt;code&gt;Ractor._require(feature)&lt;/code&gt; is added to run requiring process on the main Ractor. [&lt;a href="https://bugs.ruby-lang.org/issues/20627" rel="noopener noreferrer"&gt;Feature #20627&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It was not possible to &lt;code&gt;require&lt;/code&gt; on Ractor until now. This is because&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;it is unclear how to manage state such as &lt;code&gt;$LOADED_FEATURES&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;almost all libraries are expected to be executed on the main Ractor&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For toy-like applications, it's fine to &lt;code&gt;require&lt;/code&gt; all libraries before using Ractor, but as the scale increases, things like &lt;code&gt;require&lt;/code&gt;-ing in methods (e.g. the &lt;code&gt;pp&lt;/code&gt; library is &lt;code&gt;required&lt;/code&gt; when the &lt;code&gt;pp&lt;/code&gt; method is first executed) and the existence of &lt;code&gt;autoload&lt;/code&gt; become major problems.&lt;/p&gt;

&lt;p&gt;So, we changed the process so that &lt;code&gt;require&lt;/code&gt; is done in the main Ractor when &lt;code&gt;require&lt;/code&gt; is called in another Ractor.&lt;/p&gt;

&lt;p&gt;However, there are various libraries in the world that redefine &lt;code&gt;require&lt;/code&gt;. In such cases, you will need to determine whether you are in the main Ractor yourself, and if not, use the &lt;code&gt;Ractor._require(feature)&lt;/code&gt; method to execute &lt;code&gt;require&lt;/code&gt; in the main Ractor. Well, I don't think any of our readers will be redefining &lt;code&gt;require&lt;/code&gt; like that, so I don't think you need to remember this. Right?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ractor.main? is added. [&lt;a href="https://bugs.ruby-lang.org/issues/20627" rel="noopener noreferrer"&gt;Feature #20627&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;Ractor.main?&lt;/code&gt; has been introduced as a convenient method for determining whether the currently running Ractor is the main Ractor.&lt;/p&gt;

&lt;p&gt;When defining your own &lt;code&gt;require&lt;/code&gt;, please use a combination of these methods, as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;main?&lt;/span&gt;
      &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="c1"&gt;# do something with your original require process&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Incidentally, when redefining &lt;code&gt;Kernel#require&lt;/code&gt;, an anonymous module that behaves as described above is automatically prepended to &lt;code&gt;Kernel&lt;/code&gt; when &lt;code&gt;Ractor.new&lt;/code&gt; is first called.&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;Kernel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepend&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="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_temporary_name&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;RactorRequire&amp;gt;'&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt; &lt;span class="n"&gt;feature&lt;/span&gt;
          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;main?&lt;/span&gt;
            &lt;span class="k"&gt;super&lt;/span&gt;
          &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_require&lt;/span&gt; &lt;span class="n"&gt;feature&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, for example, I think that &lt;code&gt;Kernel#require&lt;/code&gt; as defined by rubygems can be used without any changes, but if you want to extend it with prepend or define &lt;code&gt;Object#require&lt;/code&gt; on your own, you will still need to use &lt;code&gt;Ractor._require&lt;/code&gt;. Well, you wouldn't do that, right?&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;

&lt;h3&gt;
  
  
  More ways to access the Ractor local storage
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Ractor.[]&lt;/code&gt; and &lt;code&gt;Ractor.[]=&lt;/code&gt; are added to access the ractor local storage
of the current Ractor. [&lt;a href="https://bugs.ruby-lang.org/issues/20715" rel="noopener noreferrer"&gt;Feature #20715&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In order to access the area prepared for each Ractor (Ractor local storage), &lt;code&gt;Ractor#[key]/[key]=&lt;/code&gt; was prepared, but in fact, even if the receiver was another Ractor, it was accessing the local storage of its own Ractor. Without warning.&lt;/p&gt;

&lt;p&gt;So, it was decided that a class method would be sufficient, and &lt;code&gt;Ractor.[]/[]=&lt;/code&gt; was introduced. It would be a problem if it were possible to access another Ractor. However, since it might be used for error handling, etc., it seems that the current &lt;code&gt;Ractor#[]&lt;/code&gt; will remain with an ambiguous specification.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Ractor.store_if_absent(key){ init }&lt;/code&gt; is added to initialize ractor local
variables in thread-safty. [&lt;a href="https://bugs.ruby-lang.org/issues/20875" rel="noopener noreferrer"&gt;Feature #20875&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ractor local storage is a kind of global variable space (a global variable space that is different for each Ractor).&lt;/p&gt;

&lt;p&gt;Now, when you want to set a value for each Ractor, it is not good if a multi-threaded program is running in the Ractor.&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;Ractor&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:cnt&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:m&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="no"&gt;Mutex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;synchronize&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:cnt&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance, this program looks fine, but if you run this part in a multi-threaded environment, there is a possibility that &lt;code&gt;m&lt;/code&gt; will take a different Mutex each time. There is also a possibility that &lt;code&gt;:cnt&lt;/code&gt; will be initialized in an unintended way.&lt;/p&gt;

&lt;p&gt;Therefore, a method called &lt;code&gt;Ractor.store_if_absent(key){ init }&lt;/code&gt; was introduced to perform the setting atomically. When the key corresponding to the key has not yet been initialized, the value obtained by executing the block is set as the value for that key. Since these initializations are performed atomically, they will not be disturbed by other threads.&lt;/p&gt;

&lt;p&gt;The program mentioned above can be made thread-safe by doing the following.&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;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;store_if_absent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:m&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt; &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:cnt&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="no"&gt;Mutex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;synchronize&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Ractor&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:cnt&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Specifically, I've added timeout.rb to make it Ractor-compatible for now.&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Range#size&lt;/code&gt; now raises &lt;code&gt;TypeError&lt;/code&gt; if the range is not iterable.
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Range#size&lt;/code&gt; now raises &lt;code&gt;TypeError&lt;/code&gt; if the range is not iterable. [&lt;a href="https://bugs.ruby-lang.org/issues/18984" rel="noopener noreferrer"&gt;Misc #18984&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;Range#size&lt;/code&gt; now raises &lt;code&gt;TypeError&lt;/code&gt; if the range is not iterable, such as &lt;code&gt;(0.1 ... 1)&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(0.1 ... 1).size  #=&amp;gt; 'Range#size': can't iterate from Float (TypeError)
# Until Ruby 3.3, it returned 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same applies to beginless ranges such as &lt;code&gt;(...0)&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Range#step&lt;/code&gt; semantics have changed slightly
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Range#step&lt;/code&gt; now consistently has a semantics of iterating by using &lt;code&gt;+&lt;/code&gt; operator for all types, not only numerics. [&lt;a href="https://bugs.ruby-lang.org/issues/18368" rel="noopener noreferrer"&gt;Feature #18368&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In simple terms, &lt;code&gt;Range#step&lt;/code&gt; can now be used with Time ranges.&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;pp&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2000&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; [2000-01-01 00:00:00 +0900,&lt;/span&gt;
&lt;span class="c1"&gt;# 2000-01-02 00:00:00 +0900,&lt;/span&gt;
&lt;span class="c1"&gt;# 2000-01-03 00:00:00 +0900]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Date and other ranges also work as expected.&lt;/p&gt;

&lt;p&gt;In actual behavior, &lt;code&gt;Range#step&lt;/code&gt; repeatedly adds the &lt;code&gt;step&lt;/code&gt; argument to the &lt;code&gt;Range#begin&lt;/code&gt; value using the &lt;code&gt;+&lt;/code&gt; method, and stops when it reaches &lt;code&gt;Range#end&lt;/code&gt;. Until now, it was built in accordance with the values in &lt;code&gt;Range#begin&lt;/code&gt; and &lt;code&gt;Range#end&lt;/code&gt;, so it could be said that the meaning has been clarified (however, there are still exceptions, such as the fact that the &lt;code&gt;+&lt;/code&gt; method is not actually called for Integer ranges, and the way String ranges are handled).&lt;/p&gt;

&lt;p&gt;Now it works without having to deal with &lt;code&gt;Time&lt;/code&gt;, &lt;code&gt;Date&lt;/code&gt;, etc. individually, but as a side effect, it behaves a little strangely with objects for which the “+” method doesn't seem to work like “addition”. I don't think you need to be careful about this, but just in case.&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;pp&lt;/span&gt; &lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;step&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="nf"&gt;take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&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="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="mi"&gt;1&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="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="mi"&gt;1&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="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="mi"&gt;1&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="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="mi"&gt;1&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="mi"&gt;1&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="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="mi"&gt;1&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="mi"&gt;1&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="mi"&gt;1&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="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="mi"&gt;1&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="mi"&gt;1&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="mi"&gt;1&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="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="mi"&gt;1&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="mi"&gt;1&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="mi"&gt;1&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="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="mi"&gt;1&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="mi"&gt;1&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="mi"&gt;1&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="mi"&gt;1&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;RubyVM::AbstractSyntaxTree::Node#locations&lt;/code&gt; was introduced
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Add &lt;code&gt;RubyVM::AbstractSyntaxTree::Node#locations&lt;/code&gt; method which returns location objects associated with the AST node. [&lt;a href="https://bugs.ruby-lang.org/issues/20624" rel="noopener noreferrer"&gt;Feature #20624&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;RubyVM::AbstractSyntaxTree::Location&lt;/code&gt; class which holds location information. [&lt;a href="https://bugs.ruby-lang.org/issues/20624" rel="noopener noreferrer"&gt;Feature #20624&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;#locations&lt;/code&gt; method, which returns location information in the code of &lt;code&gt;RubyVM::AbstractSyntaxTree::Node&lt;/code&gt;, has been introduced.&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;# Get location information from the node obtained by parsing “1 + 1”.&lt;/span&gt;
&lt;span class="n"&gt;loc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RubyVM&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;AbstractSyntaxTree&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"1 + 1"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;locations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#&amp;lt;RubyVM::AbstractSyntaxTree::Location:@1:0-1:5&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;# We can see that it extends from the 0th column of the 1st line to the 5th column of the 1st line&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first_lineno&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 1&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first_column&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 0&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_lineno&lt;/span&gt;  &lt;span class="c1"&gt;#=&amp;gt; 1&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_column&lt;/span&gt;  &lt;span class="c1"&gt;#=&amp;gt; 5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;#locations&lt;/code&gt; returns multiple location information depending on the node. For example, the node of the code &lt;code&gt;alias foo bar&lt;/code&gt; seems to return two location information: the overall location information and the location information of the “alias” keyword. Since the Prism node already has this kind of location information, it is a preparation for creating a conversion from &lt;code&gt;RubyVM::AbstractSyntaxTree::Node&lt;/code&gt; to the Prism node.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;String#append_as_bytes&lt;/code&gt; was introduced
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;String#append_as_bytes&lt;/code&gt; was added to more easily and efficiently work with binary buffers and protocols. It directly concatenate the arguments into the string without any encoding validation or conversion. [&lt;a href="https://bugs.ruby-lang.org/issues/20594" rel="noopener noreferrer"&gt;Feature #20594&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;String#append_as_bytes&lt;/code&gt; method, which destructively concatenates strings, was introduced.&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;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b&lt;/span&gt;
&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append_as_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"ä"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# This will result in a byte string equivalent to "\xff" + "ä".&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "\xFF\xC3\xA4"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What is the difference between this and &lt;code&gt;str &amp;lt;&amp;lt; 255 &amp;lt;&amp;lt; "ä"&lt;/code&gt;? In the following case, an exception is raised.&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;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b&lt;/span&gt;
&lt;span class="n"&gt;str&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"ä"&lt;/span&gt;
  &lt;span class="c1"&gt;#=&amp;gt; incompatible character encodings: BINARY (ASCII-8BIT) and UTF-8 (Encoding::CompatibilityError)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might think that &lt;code&gt;str&lt;/code&gt; is a binary encoding, so it should be silently concatenated. However, that means implicitly converting the argument string &lt;code&gt;"ä"&lt;/code&gt; into a broken string, which may not be a good idea. So it was decided to introduce a more explicit method with the name &lt;code&gt;as_bytes&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Incidentally, &lt;code&gt;String#append_as_bytes&lt;/code&gt; forcibly concatenates the receiver as a byte string, regardless of the encoding, not just binary encoding. I'm not sure about the use case for that, though.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Symbol#to_s&lt;/code&gt; now emits a deprecation warning when mutated
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The string returned by &lt;code&gt;Symbol#to_s&lt;/code&gt; now emits a deprecation warning when mutated, and will be frozen in a future version of Ruby. These warnings can be enabled with &lt;code&gt;-W:deprecated&lt;/code&gt; or by setting &lt;code&gt;Warning[:deprecated] = true&lt;/code&gt;. [&lt;a href="https://bugs.ruby-lang.org/issues/20350" rel="noopener noreferrer"&gt;Feature #20350&lt;/a&gt;]
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ruby -we ':sym.to_s &amp;lt;&amp;lt; ?a'
-e:1: warning: warning: string returned by :sym.to_s will be frozen in the future
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the beginning of this article, we introduced the mechanism for making frozen-string-literal the default (a string that will be frozen in the future if a destructive change is made to it), and if you try to make a destructive change to the string returned by &lt;code&gt;Symbol#to_s&lt;/code&gt; using this, you will now get a warning if the &lt;code&gt;deprecated&lt;/code&gt; warning is enabled (for example, if you start Ruby with &lt;code&gt;-w&lt;/code&gt; or &lt;code&gt;-W:deprecated&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;So I guess they want to freeze this return value in the future too. &lt;code&gt;Symbol#name&lt;/code&gt; went in for that, but should we have to do &lt;code&gt;#to_s&lt;/code&gt; too?&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;

&lt;h3&gt;
  
  
  On Windows, the string encoding of &lt;code&gt;Time#zone&lt;/code&gt; has changed slightly
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;On Windows, now &lt;code&gt;Time#zone&lt;/code&gt; encodes the system timezone name in UTF-8 instead of the active code page, if it contains non-ASCII characters. [&lt;a href="https://bugs.ruby-lang.org/issues/20929" rel="noopener noreferrer"&gt;Bug #20929&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don't really understand it, but it seems to have changed. The Windows native environment is difficult to me.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Time#xmlschema&lt;/code&gt; has been incorporated into the main body
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Time#xmlschema&lt;/code&gt;, and its &lt;code&gt;Time#iso8601&lt;/code&gt; alias have been moved into the core &lt;code&gt;Time&lt;/code&gt; class while previously it was an extension provided by the &lt;code&gt;time&lt;/code&gt; gem. [&lt;a href="https://bugs.ruby-lang.org/issues/20707" rel="noopener noreferrer"&gt;Feature #20707&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;Time#xmlschema&lt;/code&gt; and &lt;code&gt;Time#iso8601&lt;/code&gt; provided by the &lt;code&gt;time&lt;/code&gt; gem have been incorporated into the main Ruby interpreter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Time.new(2024, 12, 25).xmlschema #=&amp;gt; “2024-12-25T00:00:00+09:00”
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reason for this is that the C implementation is faster. That's right.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Warning.categories&lt;/code&gt; has been added
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Add &lt;code&gt;Warning.categories&lt;/code&gt; method which returns a list of possible warning categories. [&lt;a href="https://bugs.ruby-lang.org/issues/20293" rel="noopener noreferrer"&gt;Feature #20293&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recently introduced warnings have categories, and it is possible to turn the output on and off for each category, but since the number of categories is gradually increasing, a method to obtain a list of them has been introduced.&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;# Turn on warnings about deprecation&lt;/span&gt;
&lt;span class="no"&gt;Warning&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:deprecated&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;

&lt;span class="c1"&gt;# Returns a list of categories that can be specified.&lt;/span&gt;
&lt;span class="no"&gt;Warning&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;categories&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; [:deprecated, :experimental, :performance, :strict_unused_block]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main use is for testing Ruby itself. When running certain tests, we want to suppress warnings, so we save the status of all categories, suppress them, and then restore them later. I don't think there are any other uses.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h2&gt;
  
  
  stdlib updates
&lt;/h2&gt;

&lt;p&gt;There are various updates, but only the ones that are commented on in the NEWS article.&lt;/p&gt;

&lt;h3&gt;
  
  
  RubyGems now supports sigstore.dev
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Add &lt;code&gt;--attestation&lt;/code&gt; option to gem push. It enabled to store signature of build artifact to sigstore.dev.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;RubyGems now supports sigstore.dev, which aims to improve the security of the software supply chain.&lt;br&gt;
Sigstore is a series of mechanisms that provide automated signing for the software supply chain. If you pass the file path to a &lt;a href="https://docs.sigstore.dev/about/bundle/" rel="noopener noreferrer"&gt;Sigstore Bundle&lt;/a&gt; generated using &lt;a href="https://github.com/sigstore/cosign" rel="noopener noreferrer"&gt;cosign&lt;/a&gt; or &lt;a href="https://github.com/sigstore/sigstore-ruby" rel="noopener noreferrer"&gt;sigstore-ruby&lt;/a&gt; to &lt;code&gt;--attestation&lt;/code&gt;, you can upload the Gem signature to RubyGems.&lt;/p&gt;

&lt;p&gt;(atpons)&lt;/p&gt;
&lt;h3&gt;
  
  
  Support for checksums in Bundler
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Add a &lt;code&gt;lockfile_checksums&lt;/code&gt; configuration to include checksums in fresh lockfiles.&lt;/li&gt;
&lt;li&gt;Add bundle lock &lt;code&gt;--add-checksums&lt;/code&gt; to add checksums to an existing lockfile.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is now possible to add checksums to Gems using RubyGems. By adding checksums to Gems when the lockfile is generated, and verifying them when bundle install is run using that lockfile, the consistency of libraries in each execution environment can be guaranteed, and tampering by the library provider or man-in-the-middle attacks can be detected.&lt;/p&gt;

&lt;p&gt;(atpons)&lt;/p&gt;
&lt;h3&gt;
  
  
  JSON performance improvements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Performance improvements &lt;code&gt;JSON.parse&lt;/code&gt; about 1.5 times faster than json-2.7.x.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It seems that JSON processing has become much faster.&lt;br&gt;
For more details, see here.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://byroot.github.io/ruby/json/2024/12/15/optimizing-ruby-json-part-1.html" rel="noopener noreferrer"&gt;Optimizing Ruby's JSON, Part 1 | byroot's blog&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://byroot.github.io/ruby/json/2024/12/18/optimizing-ruby-json-part-2.html" rel="noopener noreferrer"&gt;Optimizing Ruby's JSON, Part 2 | byroot's blog&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(ko1)&lt;/p&gt;
&lt;h3&gt;
  
  
  Now possible to create a Tempfile with no actual file
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The keyword argument &lt;code&gt;anonymous: true&lt;/code&gt; is implemented for Tempfile.create.
&lt;code&gt;Tempfile.create(anonymous: true)&lt;/code&gt; removes the created temporary file immediately.
So applications don't need to remove the file.
[&lt;a href="https://bugs.ruby-lang.org/issues/20497" rel="noopener noreferrer"&gt;Feature #20497&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;Tempfile.create(anonymous: true)&lt;/code&gt; now creates a temporary file without creating a real file.&lt;br&gt;
To be precise, it seems that on Linux, where &lt;code&gt;O_TMPFILE&lt;/code&gt; is available, a temporary file is not created, but on other platforms, including Windows, a temporary file is created for a moment and then immediately deleted after the IO handle is obtained.&lt;/p&gt;

&lt;p&gt;I'm not sure what the user benefits are, but perhaps it avoids problems such as the temporary file not being deleted if the Ruby interpreter terminates abnormally due to a segfault, or the filename generated by the random number generator colliding with an existing file (a problem that seems unlikely enough not to be a concern).&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;
&lt;h3&gt;
  
  
  win32/sspi.rb has been removed from the Ruby repository
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;This library is now extracted from the Ruby repository to [ruby/net-http-sspi].
[&lt;a href="https://bugs.ruby-lang.org/issues/20775" rel="noopener noreferrer"&gt;Feature #20775&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don't know what this is, but it seems to have been removed. I think it wasn't working in the first place?&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;
&lt;h3&gt;
  
  
  The default gems have been updated in various ways
&lt;/h3&gt;

&lt;p&gt;win32-registry 0.1.0 has been added, and many other libraries have been updated.&lt;/p&gt;
&lt;h3&gt;
  
  
  The bundled gems have been updated in various ways
&lt;/h3&gt;

&lt;p&gt;They have been updated in various ways.&lt;/p&gt;
&lt;h4&gt;
  
  
  repl_type_completor has been added
&lt;/h4&gt;

&lt;p&gt;The following bundled gem has been added.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;repl_type_completor 0.1.9&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The name of katakata_irb, which was announced at RubyKaigi 2023, has changed and it is now a standard attachment (bundled gem). This is a library that supports completion in IRB, and it uses Prism and RBS to provide smarter completion candidates than before. By default, IRB analyzes completion targets using regular expressions , so there were problems such as it sometimes suggesting methods that could not be called, but repl_type_completor now makes it possible to suggest more accurate candidates. Even if you haven't written RBS, it will automatically become smarter because RBS is included as standard for built-in classes and the analysis function itself has become more powerful.&lt;br&gt;
In IRB, repl_type_completor is used preferentially if it is present in the execution environment. In the Bundler environment, &lt;code&gt;bundle add repl_type_completor&lt;/code&gt; is required.&lt;/p&gt;

&lt;p&gt;(ima1zumi)&lt;/p&gt;
&lt;h3&gt;
  
  
  Transfer from default gems to bundled gems
&lt;/h3&gt;

&lt;p&gt;The following bundled gems are promoted from default gems.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/ruby/mutex_m" rel="noopener noreferrer"&gt;mutex_m&lt;/a&gt; 0.3.0&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ruby/getoptlong" rel="noopener noreferrer"&gt;getoptlong&lt;/a&gt; 0.2.1&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ruby/base64" rel="noopener noreferrer"&gt;base64&lt;/a&gt; 0.2.0&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ruby/bigdecimal" rel="noopener noreferrer"&gt;bigdecimal&lt;/a&gt; 3.1.8&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ruby/observer" rel="noopener noreferrer"&gt;observer&lt;/a&gt; 0.1.2&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ruby/abbrev" rel="noopener noreferrer"&gt;abbrev&lt;/a&gt; 0.1.2&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ruby/resolv-replace" rel="noopener noreferrer"&gt;resolv-replace&lt;/a&gt; 0.1.1&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ruby/rinda" rel="noopener noreferrer"&gt;rinda&lt;/a&gt; 0.2.0&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ruby/drb" rel="noopener noreferrer"&gt;drb&lt;/a&gt; 2.2.1&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ruby/nkf" rel="noopener noreferrer"&gt;nkf&lt;/a&gt; 0.2.0&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ruby/syslog" rel="noopener noreferrer"&gt;syslog&lt;/a&gt; 0.2.0&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ruby/csv" rel="noopener noreferrer"&gt;csv&lt;/a&gt; 3.3.2&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These libraries have been changed from default gems to bundled gems.&lt;br&gt;
If you are developing an app using Gemfile, please add these libraries to your Gemfile.&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;
&lt;h2&gt;
  
  
  Compatibility issues
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Error messages and backtrace have been changed
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Error messages and backtrace displays have been changed.

&lt;ul&gt;
&lt;li&gt;Use a single quote instead of a backtick as an opening quote. [&lt;a href="https://bugs.ruby-lang.org/issues/16495" rel="noopener noreferrer"&gt;Feature #16495&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;Display a class name before a method name (only when the class has a permanent name). [&lt;a href="https://bugs.ruby-lang.org/issues/19117" rel="noopener noreferrer"&gt;Feature #19117&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;Extra &lt;code&gt;rescue&lt;/code&gt;/&lt;code&gt;ensure&lt;/code&gt; frames are no longer available on the backtrace. [&lt;a href="https://bugs.ruby-lang.org/issues/20275" rel="noopener noreferrer"&gt;Feature #20275&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;Kernel#caller, Thread::Backtrace::Location’s methods, etc. are also changed accordingly.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's not very flashy, but there have been a lot of changes. Spot the differences.&lt;/p&gt;

&lt;p&gt;Old example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test.rb:1:in `foo': undefined method `time' for an instance of Integer
from test.rb:2:in `&amp;lt;main&amp;gt;'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;New example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test.rb:1:in 'Object#foo': undefined method 'time' for an instance of Integer
from test.rb:2:in '&amp;lt;main&amp;gt;'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For one thing, the method name display has changed from &lt;code&gt;foo&lt;/code&gt; to &lt;code&gt;Object#foo&lt;/code&gt;. It's now easier to see which method it is (on the other hand, it's also possible that it's become more verbose, so if you find yourself complaining a lot, it might be a good idea to speak up now).&lt;br&gt;
The other thing is that backticks have been changed to single quotes. This improves the experience of copying and pasting error messages and having them turn out weird in markdown.&lt;/p&gt;

&lt;p&gt;I was the one who made this change, but even this small change had an unexpected impact on compatibility, and it was a lot of work. There were some naughty libraries that parsed error messages and backtraces. For example, irb and test-unit. I don't think it will affect the majority of libraries, but if there is any problem, please feel free to report it so we can investigate.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;
&lt;h3&gt;
  
  
  Hash#inspect results have changed
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Hash#inspect&lt;/code&gt; rendering have been changed. [&lt;a href="https://bugs.ruby-lang.org/issues/20433" rel="noopener noreferrer"&gt;Bug #20433&lt;/a&gt;]&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Symbol keys are displayed using the modern symbol key syntax: &lt;code&gt;"{user: 1}"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Other keys now have spaces around &lt;code&gt;=&amp;gt;&lt;/code&gt;: &lt;code&gt;'{"user" =&amp;gt; 1}'&lt;/code&gt;, while previously they didn't: &lt;code&gt;'{"user"=&amp;gt;1}'&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The appearance of a &lt;code&gt;p&lt;/code&gt; (&lt;code&gt;inspect&lt;/code&gt;) of a Hash has changed.&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;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; {:user=&amp;gt;1} until Ruby 3.3&lt;/span&gt;
    &lt;span class="c1"&gt;#=&amp;gt; {user: 1} from Ruby 3.4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I think that very few people write hashes like &lt;code&gt;{ :user =&amp;gt; 1 }&lt;/code&gt; these days, so this change is to match that.&lt;/p&gt;

&lt;p&gt;This is probably the biggest incompatibility in Ruby 3.4, apart from the warning for the default &lt;code&gt;frozen_string_literal&lt;/code&gt;. If you have the following code in your tests, it will fail.&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;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"result: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&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;str&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;"result: {:user=&amp;gt;1}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you only want to use it with Ruby 3.4 or later, you can just change the expected value, but if you want to use it with both Ruby 3.3 and 3.4, for example in testing a library, you might want to change it 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;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str&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;"result: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="mi"&gt;1&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, since the &lt;code&gt;inspect&lt;/code&gt; string may change in this way, it is not recommended to use it as the expected value in a test.&lt;/p&gt;

&lt;p&gt;Incidentally, for a Hash that contains a mixture of Symbol and non-Symbol keys, only the Symbol keys are displayed with a colon.&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;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;:sym&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"str"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; {sym: 1, "str" =&amp;gt; 2}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As an aside, the circumstances leading up to this change are a little interesting. It all started with a bug report saying that &lt;code&gt;{:&amp;lt;=&amp;gt;1}&lt;/code&gt; was being output as &lt;code&gt;{:&amp;lt;=&amp;gt;1}&lt;/code&gt; when &lt;code&gt;inspect&lt;/code&gt; was used, and that it didn't revert back to the original value when &lt;code&gt;eval&lt;/code&gt; was used. There is no guarantee that the result of &lt;code&gt;inspect&lt;/code&gt; will revert back to the original value when &lt;code&gt;eval&lt;/code&gt; is used, but &lt;code&gt;{:&amp;lt;=&amp;gt;1}&lt;/code&gt; is not very clear to the eye, so we wanted to fix it. So, it was considered that spaces should be inserted, like &lt;code&gt;{:&amp;lt; =&amp;gt; 1}&lt;/code&gt;. Then, &lt;code&gt;{:key=&amp;gt;1}&lt;/code&gt; should also be changed to &lt;code&gt;{:key =&amp;gt; 1}&lt;/code&gt;? I think that &lt;code&gt;{:&amp;lt; =&amp;gt; 1}&lt;/code&gt; will not affect most people, but changing to &lt;code&gt;{:key =&amp;gt; 1}&lt;/code&gt; is a certain degree of incompatibility. There was also the option of giving the change up, but there had been talk of “making &lt;code&gt;Hash#inspect&lt;/code&gt; into the &lt;code&gt;{key: 1}&lt;/code&gt; format someday” that had been smoldering and then fading away, and the momentum grew that “if it's going to be incompatible anyway, let's do it all at once here”, and that's how this change came about.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  Converting a decimal number to a string now interprets a string without a decimal part
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Kernel#Float()&lt;/code&gt; now accepts a decimal string with decimal part omitted. [&lt;a href="https://bugs.ruby-lang.org/issues/20705" rel="noopener noreferrer"&gt;Feature #20705&lt;/a&gt;]&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;String#to_f&lt;/code&gt; now accepts a decimal string with decimal part omitted. [&lt;a href="https://bugs.ruby-lang.org/issues/20705" rel="noopener noreferrer"&gt;Feature #20705&lt;/a&gt;]&lt;br&gt;
Note that the result changes when an exponent is specified.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short, it now interprets half-decimal strings like &lt;code&gt;"1."&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"1."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Previously, &lt;code&gt;Kernel#Float&lt;/code&gt; would throw an exception for such strings. &lt;code&gt;String#to_f&lt;/code&gt; evaluated the string within the range of interpretation (ignoring the part after the period in the example above), so the behavior is the same.&lt;/p&gt;

&lt;p&gt;The fractional part between the period and the exponent notation can also be omitted.&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;Float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"1.E-1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 0.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was also an exception for &lt;code&gt;Kernel#Float&lt;/code&gt; in the past. &lt;code&gt;String#to_f&lt;/code&gt; ignored everything after the period, so it returned 1, but in Ruby 3.4 and later, it returns 0.1, so it is slightly incompatible. I hope no one will use it.&lt;/p&gt;

&lt;p&gt;Incidentally, there are quite a few languages that interpret &lt;code&gt;1.&lt;/code&gt; as a decimal number, including C and Python. Of course, in the Ruby language, the period is a method call operator, so &lt;code&gt;1.&lt;/code&gt; cannot be interpreted as a decimal number.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Refinement#refined_class&lt;/code&gt; has been removed
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Removed deprecated method &lt;code&gt;Refinement#refined_class&lt;/code&gt;. [&lt;a href="https://bugs.ruby-lang.org/issues/19714" rel="noopener noreferrer"&gt;Feature #19714&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It has been removed. Please use &lt;code&gt;Refinement#target&lt;/code&gt; from now on.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h2&gt;
  
  
  Stdlib incompatibilities
&lt;/h2&gt;

&lt;p&gt;There also appear to be some changes in the standard library.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;DidYouMean&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DidYouMean::SPELL_CHECKERS[]=&lt;/code&gt; and &lt;code&gt;DidYouMean::SPELL_CHECKERS.merge!&lt;/code&gt; are removed.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Net::HTTP&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Removed the following deprecated constants:
&lt;code&gt;Net::HTTP::ProxyMod&lt;/code&gt;
&lt;code&gt;Net::NetPrivate::HTTPRequest&lt;/code&gt;
&lt;code&gt;Net::HTTPInformationCode&lt;/code&gt;
&lt;code&gt;Net::HTTPSuccessCode&lt;/code&gt;
&lt;code&gt;Net::HTTPRedirectionCode&lt;/code&gt;
&lt;code&gt;Net::HTTPRetriableCode&lt;/code&gt;
&lt;code&gt;Net::HTTPClientErrorCode&lt;/code&gt;
&lt;code&gt;Net::HTTPFatalErrorCode&lt;/code&gt;
&lt;code&gt;Net::HTTPServerErrorCode&lt;/code&gt;
&lt;code&gt;Net::HTTPResponseReceiver&lt;/code&gt;
&lt;code&gt;Net::HTTPResponceReceiver&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These constants were deprecated from 2012.&lt;/p&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Timeout&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reject negative values for Timeout.timeout. [&lt;a href="https://bugs.ruby-lang.org/issues/20795" rel="noopener noreferrer"&gt;Bug #20795&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;URI&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Switched default parser to RFC 3986 compliant from RFC 2396 compliant.
[&lt;a href="https://bugs.ruby-lang.org/issues/19266" rel="noopener noreferrer"&gt;Bug #19266&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  C API updates
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;rb_newobj&lt;/code&gt; and &lt;code&gt;rb_newobj_of&lt;/code&gt; (and corresponding macros &lt;code&gt;RB_NEWOBJ&lt;/code&gt;, &lt;code&gt;RB_NEWOBJ_OF&lt;/code&gt;, &lt;code&gt;NEWOBJ&lt;/code&gt;, &lt;code&gt;NEWOBJ_OF&lt;/code&gt;) have been removed. [&lt;a href="https://bugs.ruby-lang.org/issues/20265" rel="noopener noreferrer"&gt;Feature #20265&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These C APIs have been removed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Removed deprecated function &lt;code&gt;rb_gc_force_recycle&lt;/code&gt;. [&lt;a href="https://bugs.ruby-lang.org/issues/18290" rel="noopener noreferrer"&gt;Feature #18290&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This C API has been removed. It was previously a no-op, so if you're using it, just remove it.&lt;/p&gt;

&lt;p&gt;To begin with, what this did was to manually free objects that were no longer needed before the GC ran. However, it was very difficult to use, and if it was used on a live object by mistake, it would cause strange things to happen, and it would require special processing for the GC, so recently it was more of a nuisance than anything else. So, it's a good thing it's been removed.&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation improvements
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prism is now the default parser
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The default parser is now Prism.
To use the conventional parser, use the command-line argument &lt;code&gt;--parser=parse.y&lt;/code&gt;.
[&lt;a href="https://bugs.ruby-lang.org/issues/20564" rel="noopener noreferrer"&gt;Feature #20564&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Prism is now the default parser for Ruby.&lt;/p&gt;

&lt;p&gt;For the past few years, the Ruby parser has been a very hot topic at RubyKaigi and other events, but even so, it's just an internal improvement, so I don't think there's anything for ordinary users to be aware of. If anything, the error messages for syntax errors have changed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ruby -e 'foo('
-e: -e:1: syntax error found (SyntaxError)
&amp;gt; 1 | foo(
    |     ^ unexpected end-of-input; expected a `)` to close the arguments
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Is it convenient to have snippets? It used to be like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ruby --parser=parse.y -e 'foo('
-e:1: syntax error, unexpected end-of-input, expecting ')'
ruby: compile error (SyntaxError)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, there are still some incompatibilities in Prism in corner cases (and I'm sure there will be more found in the future), so there may be some changes here and there.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  Happy Eyeballs v2 has been implemented
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Happy Eyeballs version 2 (RFC8305), an algorithm that ensures faster and more reliable connections
by attempting IPv6 and IPv4 concurrently, is used in Socket.tcp and TCPSocket.new.
To disable it globally, set the environment variable &lt;code&gt;RUBY_TCP_NO_FAST_FALLBACK=1&lt;/code&gt; or
call &lt;code&gt;Socket.tcp_fast_fallback=false&lt;/code&gt;.
Or to disable it on a per-method basis, use the keyword argument &lt;code&gt;fast_fallback: false&lt;/code&gt;.
[&lt;a href="https://bugs.ruby-lang.org/issues/20108" rel="noopener noreferrer"&gt;Feature #20108&lt;/a&gt;] [&lt;a href="https://bugs.ruby-lang.org/issues/20782" rel="noopener noreferrer"&gt;Feature #20782&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;TCPSocket.new&lt;/code&gt; and &lt;code&gt;Socket.tcp&lt;/code&gt; now connect by default using Happy Eyeballs v2. Happy Eyeballs v2 is a mechanism that tries to connect to a specified host using both IPv4 and IPv6 simultaneously, and establishes a connection with the first host that responds.&lt;/p&gt;

&lt;p&gt;Previously, it tried to connect to IPv6 and IPv4 sequentially. With this method, if you tried to connect to IPv6 first in an environment where there were problems with IPv6 communication for some reason, it would take a long time because it wouldn't switch to trying to connect to IPv4 until the IPv6 connection timed out. Ruby 3.4 connects to IPv6 and IPv4 simultaneously, so even if IPv6 doesn't work, IPv4 will respond immediately and the connection should be established without waiting for a timeout.&lt;/p&gt;

&lt;p&gt;Well, actually, I personally feel that it is more of a transitional measure needed for society to gradually shift from IPv4 to IPv6, than a direct user benefit. Happy Eyeballs is a social responsibility. Ruby is doing a great job of fulfilling its social responsibilities.&lt;/p&gt;

&lt;p&gt;Happy Eyeballs has a slight overhead because it connects in parallel. If you really want to stop Happy Eyeballs, set &lt;code&gt;Socket.tcp_fast_fallback = false&lt;/code&gt; or set the environment variable &lt;code&gt;RUBY_TCP_NO_FAST_FALLBACK=1&lt;/code&gt;. In addition to the overhead, it may also be useful for isolating problems when connection problems occur.&lt;/p&gt;

&lt;p&gt;(mame)&lt;/p&gt;

&lt;h3&gt;
  
  
  A mechanism for switching GC implementations has been introduced
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Alternative garbage collector (GC) implementations can be loaded dynamically
through the modular garbage collector feature. To enable this feature,
configure Ruby with &lt;code&gt;--with-modular-gc&lt;/code&gt; at build time. GC libraries can be
loaded at runtime using the environment variable &lt;code&gt;RUBY_GC_LIBRARY&lt;/code&gt;.
[&lt;a href="https://bugs.ruby-lang.org/issues/20351" rel="noopener noreferrer"&gt;Feature #20351&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is now possible to build Ruby so that the GC implementation can be switched at Ruby process startup by specifying &lt;code&gt;--with-modular-gc&lt;/code&gt; in &lt;code&gt;configure&lt;/code&gt; when building Ruby. This can be changed using the &lt;code&gt;RUBY_GC_LIBRARY&lt;/code&gt; environment variable.&lt;/p&gt;

&lt;p&gt;Since this option is not specified by default when building, it can be said that this was introduced for the purpose of experimenting with replacing the GC implementation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ruby's built-in garbage collector has been split into a separate file at
&lt;code&gt;gc/default/default.c&lt;/code&gt; and interacts with Ruby using an API defined in
&lt;code&gt;gc/gc_impl.h&lt;/code&gt;. The built-in garbage collector can now also be built as a
library using &lt;code&gt;make modular-gc MODULAR_GC=default&lt;/code&gt; and enabled using the
environment variable &lt;code&gt;RUBY_GC_LIBRARY=default&lt;/code&gt;. [&lt;a href="https://bugs.ruby-lang.org/issues/20470" rel="noopener noreferrer"&gt;Feature #20470&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The directory structure related to GC has been changed so that the GC implementation can be changed in various ways.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An experimental GC library is provided based on &lt;a href="https://www.mmtk.io/" rel="noopener noreferrer"&gt;MMTk&lt;/a&gt;.
This GC library can be built using &lt;code&gt;make modular-gc MODULAR_GC=mmtk&lt;/code&gt; and
enabled using the environment variable &lt;code&gt;RUBY_GC_LIBRARY=mmtk&lt;/code&gt;. This requires
the Rust toolchain on the build machine. [&lt;a href="https://bugs.ruby-lang.org/issues/20860" rel="noopener noreferrer"&gt;Feature #20860&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using the feature to change the GC implementation, an experimental GC implementation called MMTk, which is a collection of many GC implementations, was provided. MMTk contains GC implementations that are the culmination of research, but due to limitations in CRuby, only a very simple one is currently supported, and it is said that it will be developed in the future. It's written in Rust, isn't it?&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;

&lt;h3&gt;
  
  
  YJIT
&lt;/h3&gt;

&lt;h4&gt;
  
  
  New features
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Add unified memory limit via &lt;code&gt;--yjit-mem-size&lt;/code&gt; command-line option (default 128MiB)  which tracks total YJIT memory usage and is more intuitive than the
old &lt;code&gt;--yjit-exec-mem-size&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;More statistics now always available via &lt;code&gt;RubyVM::YJIT.runtime_stats&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add compilation log to track what gets compiled via &lt;code&gt;--yjit-log&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Tail of the log also available at run-time via &lt;code&gt;RubyVM::YJIT.log&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Add support for shareable consts in multi-ractor mode&lt;/li&gt;

&lt;li&gt;Can now trace counted exits with &lt;code&gt;--yjit-trace-exits=COUNTER&lt;/code&gt;
&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  New optimizations
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Compressed context reduces memory needed to store YJIT metadata&lt;/li&gt;
&lt;li&gt;Improved allocator with ability to allocate registers for local variables&lt;/li&gt;
&lt;li&gt;When YJIT is enabled, use more Core primitives written in Ruby:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Array#each&lt;/code&gt;, &lt;code&gt;Array#select&lt;/code&gt;, &lt;code&gt;Array#map&lt;/code&gt; rewritten in Ruby for better performance [&lt;a href="https://bugs.ruby-lang.org/issues/20182" rel="noopener noreferrer"&gt;Feature #20182&lt;/a&gt;].&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Ability to inline small/trivial methods such as:

&lt;ul&gt;
&lt;li&gt;Empty methods&lt;/li&gt;
&lt;li&gt;Methods returning a constant&lt;/li&gt;
&lt;li&gt;Methods returning &lt;code&gt;self&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Methods directly returning an argument&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Specialized codegen for many more runtime methods&lt;/li&gt;

&lt;li&gt;Optimize &lt;code&gt;String#getbyte&lt;/code&gt;, &lt;code&gt;String#setbyte&lt;/code&gt; and other string methods&lt;/li&gt;

&lt;li&gt;Optimize bitwise operations to speed up low-level bit/byte manipulation&lt;/li&gt;

&lt;li&gt;Various other incremental optimizations&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;It seems that YJIT has also improved in various ways. I'm sure there will be a detailed explanation, so I'll skip it here.&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;

&lt;h2&gt;
  
  
  Miscellaneous changes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Added a warning when passing a block to a method that doesn't use the passed block
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Passing a block to a method which doesn't use the passed block will show
a warning on verbose mode (&lt;code&gt;-w&lt;/code&gt;).
[&lt;a href="https://bugs.ruby-lang.org/issues/15554" rel="noopener noreferrer"&gt;Feature #15554&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When passing a block to a method that doesn't use the passed block, a warning will now be issued if &lt;code&gt;-w&lt;/code&gt; is specified.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; warning: the block passed to 'Object#f' defined at t.rb:1 may be ignored&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, if you run this as it is, you will get a warning even if you are passing a block intentionally and unnecessarily, as in the following duck typing, and we found that this is quite common.&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;C&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="c1"&gt;# This uses a block&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;D&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt; &lt;span class="c1"&gt;# This one doesn't use a block&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;C&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;D&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; No warning is given because there is a method named “f” that uses a block&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, in this way, if there is a method with the same name that uses a block, no warning is given (even with &lt;code&gt;-w&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;If you don't like this loosening of the conditions, try running with &lt;code&gt;-W:strict_unused_block&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./miniruby -W:strict_unused_block -e '
class C
  def f = yield # This uses a block
end

class D
  def f = nil # This does not use a block
end

[C.new, D.new].each{ it.f{} }
'
-e:10: warning: the block passed to 'D#f' defined at -e:7 may be ignored
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will now get a warning.&lt;/p&gt;

&lt;p&gt;In fact, when I tried it out with our app, I found one example in the rspec tests where a block was being passed where it shouldn't have been. I only found it because I was using the strict version, so you might want to be prepared for a lot of false positives.&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;

&lt;h3&gt;
  
  
  Warnings are now given for redefining methods that we wouldn't expect to be redefined
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Redefining some core methods that are specially optimized by the interpreter
and JIT like String#freeze or Integer#+ now emits a performance class
warning (&lt;code&gt;-W:performance&lt;/code&gt; or &lt;code&gt;Warning[:performance] = true&lt;/code&gt;).
[&lt;a href="https://bugs.ruby-lang.org/issues/20429" rel="noopener noreferrer"&gt;Feature #20429&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If methods that are not supposed to be redefined, such as &lt;code&gt;String#freeze&lt;/code&gt; and &lt;code&gt;Integer#+&lt;/code&gt;, are redefined, it has a negative impact on the interpreter's performance (because the interpreter is optimized on the assumption that they will not be redefined). Therefore, if such a method is redefined, a warning will now be issued if &lt;code&gt;-W:performance&lt;/code&gt; is specified.&lt;/p&gt;

&lt;p&gt;(ko1)&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We have introduced the new features and improvements of Ruby 3.4. In addition to the ones introduced here, there are also bug fixes and minor improvements. We hope you will check them out in your Ruby applications.&lt;/p&gt;

&lt;p&gt;Although Ruby 3.4 doesn't have any particularly eye-catching new features, there have been a lot of implementation improvements, such as the replacement of the parser with Prism and the introduction of a new grammar called &lt;code&gt;it&lt;/code&gt;. There have also been some decisions that will have a major impact on the future development of Ruby, such as the defaultization of frozen-string-literal. Please set it up on your computer and enjoy the new Ruby.&lt;/p&gt;

&lt;p&gt;Enjoy Ruby programming!&lt;/p&gt;

&lt;p&gt;(ko1/mame, guest: ima1zumi, atpons)&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Am I the only one who doesn't put parentheses around the parameters in Ruby method definitions?</title>
      <dc:creator>Koichi Sasada</dc:creator>
      <pubDate>Fri, 12 Apr 2024 02:04:04 +0000</pubDate>
      <link>https://forem.com/ko1/am-i-the-only-one-who-doesnt-put-parentheses-around-the-parameters-in-ruby-method-definitions-136c</link>
      <guid>https://forem.com/ko1/am-i-the-only-one-who-doesnt-put-parentheses-around-the-parameters-in-ruby-method-definitions-136c</guid>
      <description>&lt;p&gt;I'm Koichi, a Ruby interpreter developer in the Technology Division at STORES, Inc. And I'm looking forward to RubyKaigi 2024!&lt;/p&gt;

&lt;p&gt;Now, when you define a method in Ruby, you can omit parentheses even if there are method parameters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&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;bar&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's how you define &lt;code&gt;bar&lt;/code&gt;. I used to prefer to write it this way, but I was shocked when my colleague Mr. Endoh told me that only Koichi (I) uses that way nowadays, so I did some research.&lt;/p&gt;

&lt;p&gt;By the way, there is a method definition method that cannot be used without parentheses, so I will tearfully add parentheses at that time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kw&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt; &lt;span class="c1"&gt;# required keyword argument&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;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# unnamed block argument&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Supplement 1: There is a lot of controversy in Ruby about whether or not to put parentheses when calling a method, but I will only mention the parentheses when defining a method. I haven't heard much about it.&lt;/p&gt;

&lt;p&gt;Supplement 2: I think most people don't put parentheses when there are no parameters at definition, so I will describe "only when there are parameters".&lt;/p&gt;

&lt;h2&gt;
  
  
  Conventions
&lt;/h2&gt;

&lt;p&gt;Rubocop has a default rule that says to put parentheses when there are parameters; even Standardrb has a default ([&lt;a href="https://github.com/standardrb/standard/blob/8307fa8f449f896075ccad"&gt;https://github.com/standardrb/standard/blob/8307fa8f449f896075ccad&lt;/a&gt; 74bf6a128ed2c26189/config/base.yml#L1098:title])&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.rubocop.org/rubocop/cops_style.html#stylemethoddefparentheses"&gt;https://docs.rubocop.org/rubocop/cops_style.html#stylemethoddefparentheses&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The rule at Cookpad, which I feel is often referenced when coding rules, says that brackets MUST be added.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/cookpad/styleguide/blob/master/ruby.en.md#method-definitions"&gt;https://github.com/cookpad/styleguide/blob/master/ruby.en.md#method-definitions&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[MUST] On method definition, do not omit parentheses of parameter list, except for methods without parameters.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A coding rule that was often referred to in the past also says that the parentheses MUST be added.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://shugo.net/ruby-codeconv/codeconv.html"&gt;https://shugo.net/ruby-codeconv/codeconv.html&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Parentheses should be added to the list of temporary arguments in a method definition. However, if there are no arguments, the parentheses should be omitted. (Ja -&amp;gt; En translation)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm going to have a hard time getting a job writing programs in Ruby...&lt;/p&gt;

&lt;h2&gt;
  
  
  Actual survey
&lt;/h2&gt;

&lt;p&gt;I checked how many ways of writing with/without parentheses on Ruby's method definition with parameters in actual Ruby code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fszfcp5eoatr9a792d7f8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fszfcp5eoatr9a792d7f8.png" alt="Comparison of method definitions with and without parentheses with parameters" width="754" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The survey covers the latest versions of all gems pushed to rubygems.org. I am grateful to Endoh-san et, al. using [&lt;a href="https://rubygems.org/gems/rubygems-mirror"&gt;https://rubygems.org/gems/rubygems-mirror&lt;/a&gt;] to manage rubygems mirrors for Ruby development.&lt;/p&gt;

&lt;p&gt;It seems that about 95% of the definitions has parentheses (w/paren) and 5% does not (wo/paren).&lt;/p&gt;

&lt;p&gt;I thought "don't older gems often have no parentheses?" (Possibility that the rule of putting parentheses has been more thoroughly enforced now than in the past.) So, I tabulated the results by year.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F24uye8hffuat9wcdooqf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F24uye8hffuat9wcdooqf.png" alt="Ratio with and without parenthes by year" width="800" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Surprisingly, there was no "more or less because they are older" trend. The percentage is high around the middle.&lt;/p&gt;

&lt;p&gt;A bit of caution is that the year counted here is the date of the latest release, not the date of first writing (if the first release was a long time ago and are still releasing in 2024, it is counted in 2024).&lt;/p&gt;

&lt;p&gt;Here are the actual values.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgftixwxqpgjw5mpolw0x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgftixwxqpgjw5mpolw0x.png" alt="Specific values and the number of files (right axis)" width="800" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In addition, I also checked what happens when there are no parameters at method definitions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr62t6a4v9egs4ros59v5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr62t6a4v9egs4ros59v5.png" alt="Method definition without parameters" width="752" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I thought no one put them on, but there are about 3%. Note that there was an anomaly in 2023, so I excluded that (I didn't check what the anomaly was).&lt;/p&gt;

&lt;p&gt;Looking at the Rails code, I found that it uses parentheses without parameters when it wants to be written on a single line, like &lt;code&gt;def foo() end&lt;/code&gt;. That kind of thing seems to go away with a one-line method definition (&lt;code&gt;def foo = nil&lt;/code&gt;). No, I'm not so sure. (Write &lt;code&gt;def foo() = nil&lt;/code&gt;?)&lt;/p&gt;

&lt;h2&gt;
  
  
  Investigation code
&lt;/h2&gt;

&lt;p&gt;I used Prism to investigate.&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="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'prism'&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ParenFinder&lt;/span&gt;
  &lt;span class="no"&gt;TYPES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:paren_w_params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:paren_w_params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:paren_w_params&lt;/span&gt;
      &lt;span class="kp"&gt;false&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:paren_wo_params&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="p"&gt;}&lt;/span&gt;
    &lt;span class="kp"&gt;false&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:noparen_w_params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:noparen_w_params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="kp"&gt;false&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:noparen_wo_params&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="p"&gt;}&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;reset&lt;/span&gt;
    &lt;span class="vg"&gt;$defs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TYPES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;{[&lt;/span&gt;&lt;span class="n"&gt;_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]}.&lt;/span&gt;&lt;span class="nf"&gt;to_h&lt;/span&gt;
    &lt;span class="vg"&gt;$defs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:files&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="vg"&gt;$defs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:time&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;reset&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;
    &lt;span class="vi"&gt;@file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;file&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;defs_rec&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:def_node&lt;/span&gt;
      &lt;span class="n"&gt;paren&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lparen_loc&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="kp"&gt;false&lt;/span&gt;
      &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parameters&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="kp"&gt;false&lt;/span&gt;
      &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TYPES&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;paren&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="vg"&gt;$defs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;# [@file, node.slice.lines.first.chomp]&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;child_nodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compact&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;defs_rec&lt;/span&gt; &lt;span class="n"&gt;n&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;def&lt;/span&gt; &lt;span class="nf"&gt;defs&lt;/span&gt;
    &lt;span class="n"&gt;ast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Prism&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;defs_rec&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;
  &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="c1"&gt;# ignore any errors&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;If&lt;/span&gt; &lt;span class="no"&gt;ARGV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;ARGV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;
  &lt;span class="no"&gt;ParenFinder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;defs&lt;/span&gt;
  &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;__FILE__&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="n"&gt;process_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt;
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;FileTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;symlink?&lt;/span&gt;
       &lt;span class="c1"&gt;# ignore (depends on data)&lt;/span&gt;
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;FileTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;directory?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="no"&gt;Dir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'*'&lt;/span&gt;&lt;span class="p"&gt;)){&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;process_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="sr"&gt;/. *\.rb$/&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;
      &lt;span class="vg"&gt;$defs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:files&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="no"&gt;ParenFinder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;defs&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="c1"&gt;# ignore other files&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="c1"&gt;# ignore&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dup&lt;/span&gt;

  &lt;span class="no"&gt;ARGV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;
    &lt;span class="n"&gt;process_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="vg"&gt;$defs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:time&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="vg"&gt;$defs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:time&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="vg"&gt;$defs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;
  &lt;span class="nf"&gt;else&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;
  &lt;span class="nf"&gt;end&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
`&lt;/p&gt;

&lt;p&gt;I am glad that I was able to create an easy survey script, although I was able to ask Mr. Endoh, who tinkers with Prism, how to use it. I mean, the &lt;code&gt;lparen_loc&lt;/code&gt; method (the position of the parentheses that wrap the parameters) is amazing.&lt;/p&gt;

&lt;p&gt;Symlink handling was going to have to be tweaked for each survey target.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why method definitions without parentheses are allowed
&lt;/h2&gt;

&lt;p&gt;I had a chat with Yukihiro Matz Matsumoto and asked him, "Why were you allowed to define methods without parentheses?" He said it was for the following reasons.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In Ruby, parentheses can be omitted in "command-like method" calls. For example, &lt;code&gt;attr :ivar&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Matz introduced it because I thought it would be better to be able to define such command-like methods without parentheses as well.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the discussion, there were opinions such as "The writer cannot judge whether the method is a command-like method or not" and "There seem to be not so many opportunities to write command-like methods". I found out that the function of omitting parentheses when defining a method is likely to be forgotten, except when there are no parameters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;I started this survey with the question, "Am I the only one who doesn't put parentheses around the parameters in Ruby method definitions?" Is it an interesting result that 5% of them violate well-known coding conventions?&lt;/p&gt;

</description>
      <category>ruby</category>
    </item>
    <item>
      <title>Personal efforts to improve the quality of Ruby interpreter</title>
      <dc:creator>Koichi Sasada</dc:creator>
      <pubDate>Tue, 27 Dec 2022 15:57:12 +0000</pubDate>
      <link>https://forem.com/ko1/personal-efforts-to-improve-the-quality-of-ruby-interpreter-2lcl</link>
      <guid>https://forem.com/ko1/personal-efforts-to-improve-the-quality-of-ruby-interpreter-2lcl</guid>
      <description>&lt;p&gt;This article is Japanese -&amp;gt; English translation (thank you DeepL!) of the following post (and some additional messages):&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://techlife.cookpad.com/entry/2022/12/26/175343" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--VwBhSk4y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.user.blog.st-hatena.com/default_entry_og_image/6628210/1516165995781773" height="420" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://techlife.cookpad.com/entry/2022/12/26/175343" rel="noopener noreferrer" class="c-link"&gt;
          Rubyインタプリタの品質向上のために個人的にやっていること - クックパッド開発者ブログ
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          技術部の笹田です。Ruby 3.2 無事にリリースされて良かったよかった。 Rubyインタプリタは複雑なプログラムなので、当然のごとくバグが入ってきます。Rubyインタプリタ開発者は、これに対していろんな対策をしています。たとえば、テストを書いて、CI環境でチェックするとか、今となっては当然のことを、当然のごとくやっています（RubyCIやchkbuild、ruby/spec: The Ruby Spec Suite aka ruby/specなどの整備や、実行環境の日々のメンテナンスの成果です）。 これに追加して、個人的にテストをとにかくたくさん繰り返し行うマシン群を用意しています。テストの…
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--srJkG2rq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techlife.cookpad.com/icon/favicon" width="48" height="48"&gt;
        techlife.cookpad.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;I'm a programmer and one of the full-time commiter of the Ruby interpreter (&lt;a href="https://atdot.net/~ko1/"&gt;Homepage of Koichi Sasada&lt;/a&gt;) at Cookpad Inc. We hope you enjoy the recently released Ruby 3.2.&lt;/p&gt;

&lt;p&gt;Ruby interpreter is a complex program, so it naturally has bugs, and Ruby interpreter developers are taking various countermeasures against them. For example, we write tests and check them in CI environment (This is the result of daily maintenance of the test environment, such as &lt;a href="https://rubyci.org/"&gt;RubyCI&lt;/a&gt;, &lt;a href="https://github.com/ruby/chkbuild"&gt;chkbuild&lt;/a&gt;, &lt;a href="https://github.com/ruby/spec"&gt;ruby/spec: The Ruby Spec Suite aka ruby/spec&lt;/a&gt; and machines).&lt;/p&gt;

&lt;p&gt;In addition to this, I have a group of machines that I personally run a lot of test iterations on. The purpose is to improve the quality of Ruby interpreter by running tests as often as possible to find bugs that occur only occasionally. In this article, I'd like to introduce about such an uncommon test environment.&lt;/p&gt;

&lt;p&gt;To prepare this test environment, I have received support from various people. In this article I would like to express my gratitude for their support.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Need to "Reveal" Bugs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Environment to run tests on a regular basis
&lt;/h3&gt;

&lt;p&gt;In a typical CI/CD context, you run tests on each commit (PR), because if there is a problem, you know there is a problem in the commit. GitHub Actions and the like often target such tests.&lt;/p&gt;

&lt;p&gt;To deal with it, Ruby interpreter core team prepares and uses the following test environments.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Per-PR and per-push testing environment with GitHub Actions&lt;/li&gt;
&lt;li&gt;Periodical test environment with chkbuild (The results are available on &lt;a href="https://rubyci.org/"&gt;rubyci&lt;/a&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both 1 and 2 are basically mechanisms to check for problems with the last modification.&lt;/p&gt;

&lt;p&gt;To get accurate test results, chkbuild makes an Ruby interpreter from scrach and run tests every time under various operating systems and CPU architectures. However, since it takes time, it is executed about once every two hours on a machine.&lt;/p&gt;

&lt;p&gt;Currently, most of the compute resources are built on AWS with support from &lt;a href="https://www.ruby.or.jp/ja/"&gt;Ruby Association&lt;/a&gt; and other organizations. GitHub actions are provided by GitHub.&lt;/p&gt;

&lt;p&gt;Speaking of which, Shopify has been kind enough to test their (presumably huge) application with a development version of Ruby. That's very helpful.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1605706285994741761-277" src="https://platform.twitter.com/embed/Tweet.html?id=1605706285994741761"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1605706285994741761-277');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1605706285994741761&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h3&gt;
  
  
  Digression: Try &lt;code&gt;head&lt;/code&gt;/&lt;code&gt;debug&lt;/code&gt; versions of &lt;a href="https://github.com/ruby/setup-ruby"&gt;setup-ruby&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;If you have projects which have tests running on GitHub actions with setup-ruby, please consider to add &lt;code&gt;head&lt;/code&gt;/&lt;code&gt;debug&lt;/code&gt; versions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;head&lt;/code&gt;: nightly build&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;debug&lt;/code&gt;: nightly build with assertions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you find out the strange behavior, please feel free to report it to the &lt;a href="https://bugs.ruby-lang.org/issues/"&gt;https://bugs.ruby-lang.org/issues/&lt;/a&gt;. This kind of contributions are very helpful for us.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test environment to discover tests that sometimes fail
&lt;/h3&gt;

&lt;p&gt;When you have a software as big as Ruby interpreter, you may encounter a phenomenon that it sometimes fails even though nothing has changed. It is also possible that there may be last minute changes, but the test fails for reasons not contemplated by the modification. This is sometimes called a flaky test. There are several possible reasons for this.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Bad test.&lt;/li&gt;
&lt;li&gt;Tests caused by external factors such as "time" and "system status&lt;/li&gt;
&lt;li&gt;Bugs are already mixed in, but only if you're unlucky (or lucky) enough to find them.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The most common experience is that "1. bad test". For example, if it depends on the order of the tests, it can cause problems at any moment. If you're writing tests that are timing-sensitive, they can be a bit off and sometimes fail (or fail because of a change in machine spec).&lt;/p&gt;

&lt;p&gt;Tests caused by external factors in #2 are also sometimes bad, though it's not impossible to say that the tests are bad. For example, the example shown in &lt;a href="https://blog.n-z.jp/blog/2021-02-03-ruby-zlib-test-binary-mode-bug.html"&gt;ruby/zlib tests started to fail even though I didn't do anything - @znz blog&lt;/a&gt; (written in Japanese), where the timestamps generated specific data at a specific time, and the tests failed (fixed the tests and solved the problem).&lt;/p&gt;

&lt;p&gt;Well, the above are in the category of "bad testing", so they are not directly related to the quality of the interpreter itself. However, if we leave these things unmodified, you'll have a hard time checking the test results, so you need to fix them as soon as possible. It's the Broken Windows Theory.&lt;/p&gt;

&lt;p&gt;Even a bug that appears once in 10,000 times by bad luck may be stepped on once a day in a software with 10,000 users a day. Or rather, you step on it. If it is worse, it may become a source of vulnerability.&lt;/p&gt;

&lt;p&gt;This kind of bug is likely to appear in the following situations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatic memory management (GC)&lt;/li&gt;
&lt;li&gt;An algorithm using cache&lt;/li&gt;
&lt;li&gt;Parallel and concurrent executions&lt;/li&gt;
&lt;li&gt;Networks and other external systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these are areas (and areas I work with a lot) where it's easy to bring in non-deterministic behavior, i.e., behavior that doesn't produce the same result even if you run it twice. There are many other things that can cause a "Hey, that's not the same result as before?" situation, such as memory address randomization by the system.&lt;/p&gt;

&lt;p&gt;So, there are many ways to tackle on this, we use the method of "just try many times". It's simple: if it appears once in every 10,000 times, then if you run it 10,000 times, it will be reproducible.&lt;/p&gt;

&lt;p&gt;In other words: "Bugs that don't appear very often are already in the code → the more test trials we run, the higher the probability of stepping on such a bug (we can reveal it out)."&lt;/p&gt;

&lt;p&gt;The chkbuild I mentioned earlier runs about 12 times a day (and multiplied by the number of environments), and the GitHub actions runs every event, so it's not enough to say "run a lot". So I made my own test environment and have been running it for about 5 years.&lt;/p&gt;

&lt;p&gt;It originally started when I got fed up with "occasional" bugs in debugging on the newly designed GC development, and ran tests all the time on a single machine.&lt;/p&gt;

&lt;p&gt;When I first started, I used the command &lt;code&gt;while make up all test-all; do date; done&lt;/code&gt; to run the test indefinitely (stopping if it failed). However, with this, I have to look at the terminal to check the results, and I could not know if the test stops unintentionally. Also, it's hard to scale, so I had to build my own test environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Techniques for running lots of tests
&lt;/h2&gt;

&lt;p&gt;In order to run a lot of tests, we devised the following&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use multiple machines (scale-out)&lt;/li&gt;
&lt;li&gt;Use machines with better performance (scale-up)&lt;/li&gt;
&lt;li&gt;Run multiple tests simultaneously on a single machine to use up hardware resources&lt;/li&gt;
&lt;li&gt;Shorten the time of a single build and test run&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is an introduction to each of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preparing the machine to be used
&lt;/h3&gt;

&lt;p&gt;If we have enough money, the most reliable (and easy for the savvy) way to scale out is to prepare lots of machines on the cloud, but since this is a private activity, there is a limit to how much money I can provide. Also, some of these types of uses that use up computing resources are not suited to cheap cloud services.&lt;/p&gt;

&lt;p&gt;Since I have some space at home, I'm currently hosting the actual machines in a temporary location (this activity will likely end there as the children grow older and they needs their own rooms. Please remember that Japanese home doesn't have enough space to maintain multiple computers).&lt;/p&gt;

&lt;p&gt;I've been staring at the price list of AWS and other services, but using real machines is the cheapest... (I wonder if it will be cheaper if I find a discount plan). I'm grateful that I can buy a nice little machine with 8 cores 16 threads for just under 100,000 JPY (about 750 USD). Currently, I'm running 4 machines.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5jkg1zdup7lmj4f9a9l3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5jkg1zdup7lmj4f9a9l3.jpg" alt="Image description" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 machine: bought 6 years ago&lt;/li&gt;
&lt;li&gt;1 machine: ThinkCentre M75q Gen 2 (donated by Garnet Tech 373 Inc.: &lt;a href="http://garnettech373.com/news/20210409ruby_core_machine.html"&gt;Garnet Tech 373 Inc. provides development machines to support Ruby interpreter development - Garnet Tech 373 Inc.&lt;/a&gt; written in Japanese) about 60,000 JPY&lt;/li&gt;
&lt;li&gt;2 machine: MINISFORUM EliteMini HX90 (purchased with proceeds from GitHub sponsors (&lt;a href="https://github.com/sponsors/ko1"&gt;Sponsor @ko1 on GitHub Sponsors)&lt;/a&gt;) 2 x about 80,000 JPY&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All the new machines are small. I used to have a mid-tower machine in my lineup, but it was getting in the way... I bought the HX90 at the last Black Friday because it was a bit low price.&lt;/p&gt;

&lt;p&gt;Test run times correlated nicely with CPU frequency. The faster the better.&lt;/p&gt;

&lt;p&gt;If you run one test suite, 2GB of memory seems to be enough even if we build and run each test in parallel (it was my surprise).&lt;/p&gt;

&lt;p&gt;I have an electricity meter on, and when I look at it, it seems to go up and down around 400Wh in total. When I look at the Tokyo Electric Power Company's electricity rates &lt;a href="https://www.tepco.co.jp/ep/private/plan/standard/kanto/index-j.html"&gt;Standard Plan&lt;/a&gt;. That's just under 10,000 JPY (per month). I'm partially compensated by the earnings from GitHub sponsors.&lt;/p&gt;

&lt;p&gt;(By the way, this electricity bill includes three Mac minis used for rubyci/chkbuild, which I introduced earlier; the Mac minis were purchased with the support of &lt;a href="https://ruby-no-kai.org/"&gt;Nihhon Ruby-no-Kai&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;It's fine now because it is Winter, but during the hot months (since I didn't turn on the A/C) the fan was making a lot of noise. Worried about a fire. So far, it's been running for more than a year, even in continuous operation. But after 5 years, 2 of the middle tower machines broke. The smaller machines seem to have a shorter life span.&lt;/p&gt;

&lt;p&gt;The machine cost (leaving aside the old one) is 220,000 JPY, and if it depreciates in 3 years, it is about 70,000 JPY/year. Electricity cost is roughly 120,000 JPY/year. In other words, about 200,000 JPY/year (about 1,500 USD/year). It is cheap because it doesn't need the cost of the place and the maintenance personnel cost. And I don't need SLA because I don't have to worry about the system if the system is down. Well, it is a good hobby to arrange machines at home.&lt;/p&gt;

&lt;p&gt;As a side note, one of the reasons I'm running physical machines on hand is to do benchmarking. If you use a machine in the cloud, you may have to mess up your instance, so I prefer to use a physical machine as much as possible. For example, the machine on &lt;a href="https://rubybench.github.io/"&gt;https://rubybench.github.io/&lt;/a&gt; is the machine hosted at my home (this machine is also provided by Ruby-no-kai Japan, thank you very much). When I need to do serious benchmarking of new features, I stop running tests and and use these machines for benchmarking (because sometimes I need more than one machine for benchmarking).&lt;/p&gt;

&lt;h3&gt;
  
  
  Parallel execution of build and test processes
&lt;/h3&gt;

&lt;p&gt;One way to increase the number of test trials is to launch multiple processes running the test suite on a single machine.&lt;/p&gt;

&lt;p&gt;When a test suite is executed, there are times when it consumes resources and times when it is free, so the idea is to improve the overall performance by running another test execution process when one test execution process is not busy. However, if the number of concurrent test processes is too large, the overall performance may deteriorate because resources are consumed in resource conflicts.&lt;/p&gt;

&lt;p&gt;Simply running multiple test processes sometimes interfered with each other (e.g. filesystem or network ports), so through some trial and error I figured out that it was ok to tweak some settings in the Docker container. I now have 22 Docker containers running the test suite simultaneously on a single machine (&lt;a href="https://github.com/ko1/build-ruby/blob/master/docker/run_sp2.rb"&gt;build-ruby/run_sp2.rb at master ・ ko1/build-ruby&lt;/a&gt;). The memory is 32GB, which is enough (but I gave up the RAM disk, which will be described later).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6alu5rmeoiehl8dwmo6c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6alu5rmeoiehl8dwmo6c.png" alt="Running various tests concurrently in a Docker container (memory consumption)" width="497" height="544"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Reduce build and test time
&lt;/h3&gt;

&lt;p&gt;In order to reduce the time it takes to build the latest version of Ruby and run through the entire test suite, we've devised the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reuse compile results etc.&lt;/li&gt;
&lt;li&gt;Using a RAM disk&lt;/li&gt;
&lt;li&gt;Concurrent build and test processing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The test runs on rubyci.org do not reuse any compile results and so on to ensure the test results. However, in this case, the goal is to get more number of tests, so I try to reuse the compiled results aggressively. However, there are sometimes problems caused by reuse, so if it fails twice, it erases all the compiled results and build from scratch.&lt;/p&gt;

&lt;p&gt;In an environment with relatively spare memory, I try to use RAM disks (tmpfs) for all build results to speed up the build a bit. However, it's not clear how well this works. The reason is that the OS caches performance-related data in memory on its own. It's just a matter of feeling that it's faster.&lt;/p&gt;

&lt;p&gt;The parallel execution of the build is by &lt;code&gt;make -jN&lt;/code&gt;. 10 years ago, there were quite a few bugs caused by this, but now we can build in parallel with almost no problem.&lt;/p&gt;

&lt;p&gt;Running tests in parallel, which means splitting up the test suite and running the them in parallel. There are roughly three groups of Ruby tests to run in this environment, one of which has long supported parallel processing (&lt;code&gt;make test-all&lt;/code&gt;). I rewrote one more group (&lt;code&gt;make btest&lt;/code&gt;) to make it parallelizable, with the goal of making it count.&lt;/p&gt;

&lt;p&gt;With these efforts, in an environment where you occupy a fast machine and repeatedly run "build the latest version -&amp;gt; run tests" can be done in less than 2 minutes. In other words, because we always get the latest version of Ruby from the repository for testing, if you make a problematic commit that causes the test to fail, you'll get a test failure notification in as little as two minutes (the result is notified to the Slack channel).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj4uafyb0sna8b8s5snuw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj4uafyb0sna8b8s5snuw.png" alt="Results of repeating tests" width="800" height="626"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Technique to reveal bugs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  More tests
&lt;/h3&gt;

&lt;p&gt;Even if a bug is introduced, it cannot be detected if there no code that will step on that bug. This is why extensive testing is needed. Ruby already has a large set of tests.&lt;/p&gt;

&lt;p&gt;Also, the Ruby interpreter source code contains a lot of assertions (statements of what should happen at this point in the program). You can think of this as a kind of test. In the parts I code, I try to increase the number of such assertions so that we can detect strange states.&lt;/p&gt;

&lt;p&gt;Many of these assertions are only enabled for checking in debug builds. For this reason, we use debug builds to run them in some of the environments we run in.&lt;/p&gt;

&lt;p&gt;For testing, ideally, it would be nice to bring a prominent app or library and run its tests on the latest development version of Ruby, but I haven't gotten around to that.&lt;/p&gt;

&lt;h3&gt;
  
  
  More test patterns
&lt;/h3&gt;

&lt;p&gt;The tests we run are not all the same, but we try to find bugs by running the tests in different patterns.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run the test with a Ruby interpreter built with various parameters.&lt;/li&gt;
&lt;li&gt;Run tests with different versions of the build environment (compiler).&lt;/li&gt;
&lt;li&gt;Run the tests in a random order. For example, the method cache status changes depending on the order in which the tests are executed, so there may be bugs to be found there.&lt;/li&gt;
&lt;li&gt;Repeatedly run the test. Similarly, repeating the same test may change the status of the method cache.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wrote a software to build Ruby and run tests according to the configuration (&lt;a href="https://github.com/ko1/build-ruby"&gt;ko1/build-ruby: Build Ruby from source code&lt;/a&gt;). For example, here is a list of settings: &lt;a href="https://github.com/ko1/build-ruby/blob/master/docker/ruby/targets.yaml"&gt;https://github.com/ko1/build-ruby/blob/master/docker/ruby/targets.yaml&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Dealing with Errors
&lt;/h3&gt;

&lt;p&gt;We have devised several ways to deal with unforeseen problems.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recording of all execution logs&lt;/li&gt;
&lt;li&gt;Allow configurable timeouts to prevent infinite stoppages

&lt;ul&gt;
&lt;li&gt;When a timeout occurs, gdb dumps the backtrace of the related process&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;In case of an abnormal exit that spits out core, you can download the core&lt;/li&gt;
&lt;li&gt;If the failures continue, delete all the data, increase the execution interval, and so on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, when a test fails, we often don't know the cause after all. We would like to devise a little more.&lt;/p&gt;

&lt;h3&gt;
  
  
  Developing a system to check the results
&lt;/h3&gt;

&lt;p&gt;I made a site ci.rvm.jp to aggregate the test results. The DB is SQLite3 because the number of viewers is limited (so it is slow). It's a really slow server, so I don't even link to it.&lt;/p&gt;

&lt;p&gt;I'm trying to make the output to stderr visible in the summary page of the execution result so that it's easy to understand what's wrong when you look at the failure page (but otehr CI sites seem to be doing this endlessly).&lt;/p&gt;

&lt;p&gt;When a test fails, there is a Slack notification (for the Ruby committers to see) and an email notification (just to me). In the rare case of a commit that fails, the notifications are terrible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aside: Other possible innovations
&lt;/h3&gt;

&lt;p&gt;There are many possible methods for testing non-deterministic behavior, especially in academic researches.&lt;/p&gt;

&lt;p&gt;For example, making any external events demterministics (external events such as input/output and thread scheduling). In other words, you can make sure that the same program (and external input) will always return the same result by using a variety of different techniques. Once a problem is found, if the problem can always be reproduced, it seems to make things easier. However, I've heard a lot about this at the research level, but I wonder how practical it will be.&lt;/p&gt;

&lt;p&gt;We can also think of methods such as using formal methods to automatically generate exhaustive tests and data that makes it easier to cover. It would be cool to be able to do this kind of thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Outcome
&lt;/h2&gt;

&lt;p&gt;I did a lot of hard work at first to fix the problem, because with thousands of attempts every day, it's quite a flurry of failures. I worked much harder to fix it, mainly because there are a lot of test flops.&lt;/p&gt;

&lt;p&gt;I was also able to fix some bugs caused by timing. Here's the patch I have in my notes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/ruby/ruby/commit/4c9f3ce7b1a35ac891550ccd7b3de1be7475f2d4"&gt;fix marking T_NONE object bug. · ruby/ruby@4c9f3ce&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ruby/ruby/commit/3cb6952f12335ce0a84d612cdbebbc358c430e81"&gt;fix passing wrong &lt;code&gt;passed_bmethod_me&lt;/code&gt;. · ruby/ruby@3cb6952&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, I introduced my personal activity to find rare bugs by increasing the number of test runs in order to improve quality of Ruby interpreter.&lt;/p&gt;

&lt;p&gt;Bugs are always present in programs, and it is hard to find bugs in large, complex programs. In this article, I introduced some of the trial-and-error process. I wish I could take a more scientific approach. If you know a good method, please let me know.&lt;/p&gt;

&lt;p&gt;As I mentioned in the article, this system is made possible by a lot of support. Especially &lt;a href="https://github.com/sponsors/ko1"&gt;GitHub Sponsors&lt;/a&gt; was important to continue this activity. Thank you again.&lt;/p&gt;

&lt;p&gt;As for the machines, a few years ago, a certain company gave me three big rack-mount machines with 3-digit GB memory that became unnecessary, and I installed them in another certain company N. I have been operating the machines including these (Mr. S of company N has been taking care of the machine operation for a long time). The other day, these machines were removed because they were old indeed, so I wrote this article with the memorial service and gratitude. Thank you very much.&lt;/p&gt;

&lt;p&gt;Well, I wish you have a happy New Year and enjoy newly released Ruby 3.2!&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>testing</category>
    </item>
    <item>
      <title>rstfilter VSCode extension for your new Ruby development experience</title>
      <dc:creator>Koichi Sasada</dc:creator>
      <pubDate>Sun, 12 Jun 2022 19:53:21 +0000</pubDate>
      <link>https://forem.com/ko1/rstfilter-vscode-extension-for-your-new-ruby-development-experience-1h97</link>
      <guid>https://forem.com/ko1/rstfilter-vscode-extension-for-your-new-ruby-development-experience-1h97</guid>
      <description>&lt;p&gt;Today I released new VSCode extension &lt;a href="https://marketplace.visualstudio.com/items?itemName=KoichiSasada.ruby-rstfilter"&gt;Ruby's rstfilter extension&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This extension provides new Ruby development experience:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can see the execution results line by line on the editor without launching a ruby command manually. You only need to &lt;em&gt;start&lt;/em&gt; rstfilter command by &lt;code&gt;Ctrl+Alt+S&lt;/code&gt;, and see the results of each lines and outputs on the bottom.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;You can see the expression results by hovering the mouse pointer on the expression.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwmx9fcntql9bjxbgb2j2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwmx9fcntql9bjxbgb2j2.png" alt="hover demo" width="332" height="148"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You don't need to write &lt;code&gt;p&lt;/code&gt;/&lt;code&gt;pp&lt;/code&gt; methods.&lt;br&gt;
You don't need to use irb/pry REPL tools.&lt;br&gt;
You only need to save the file and see the results immediately.&lt;/p&gt;

&lt;p&gt;I hope it is useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ruby newbie who want to check the results frequently.&lt;/li&gt;
&lt;li&gt;Test developers&lt;/li&gt;
&lt;li&gt;Prototyping developers for apps/libs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the libraries, the following traditional idiom can be used.&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;XXX&lt;/span&gt;
   &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vg"&gt;$0&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kp"&gt;__FILE__&lt;/span&gt;
  &lt;span class="c1"&gt;# Try with XXX.&lt;/span&gt;
  &lt;span class="c1"&gt;# This test code will be ignored when the file is required.&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enjoy your Ruby programming!&lt;/p&gt;




&lt;p&gt;Similar projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;xmpfilter in &lt;a href="https://rubygems.org/gems/rcodetools/"&gt;rcodetools&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/JoshCheek/seeing_is_believing"&gt;JoshCheek/seeing_is_believing: Displays the results of every line of code in your file&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://quokkajs.com/"&gt;Quokka - JavaScript and TypeScript playground in your editor&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Duroktar/Wolf"&gt;Duroktar/Wolf: Wolf is a VsCode extension that enables live inspection of Python code in the editor.&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Update: v0.0.2 changes the interface. &lt;em&gt;Save&lt;/em&gt; doesn't kick the rstfilter but you need to run rstfilter explicitly by &lt;code&gt;Ctrl+Alt+S&lt;/code&gt;. Please add &lt;code&gt;Alt&lt;/code&gt; to your &lt;code&gt;Ctrl-S&lt;/code&gt; saving :)&lt;/p&gt;

</description>
    </item>
    <item>
      <title>debug.gem blog: initial commit</title>
      <dc:creator>Koichi Sasada</dc:creator>
      <pubDate>Sat, 31 Jul 2021 16:34:03 +0000</pubDate>
      <link>https://forem.com/ko1/debug-gem-blog-initial-commit-3ip</link>
      <guid>https://forem.com/ko1/debug-gem-blog-initial-commit-3ip</guid>
      <description>&lt;h2&gt;
  
  
  About this series of articles
&lt;/h2&gt;

&lt;p&gt;This series introduce features of &lt;a href="https://github.com/ruby/debug"&gt;ruby/debug: Debugging functionality for Ruby&lt;/a&gt; now I'm working on. debug.gem will be introduced into Ruby 3.1 which will be released Dec/2021.&lt;/p&gt;

&lt;p&gt;This article introduce about debug.gem, the background and the motivation.&lt;/p&gt;

&lt;p&gt;BTW see the recent article &lt;a href="https://dev.to/st0012/a-sneak-peek-of-ruby-s-new-debugger-5caa"&gt;A Sneak Peek of Ruby's New Debugger!&lt;/a&gt; by &lt;a class="mentioned-user" href="https://dev.to/st0012"&gt;@st0012&lt;/a&gt;. His great article introduces debug.gem from Ruby on Rails developer perspective.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background and motivation
&lt;/h2&gt;

&lt;p&gt;debug.gem is a debugger for Ruby. It is new replacement of &lt;a href="https://docs.ruby-lang.org/en/3.0.0/DEBUGGER__.html"&gt;lib/debug.rb&lt;/a&gt; which is installed from ancient Ruby 1.3.1 (!!). However, lib/debug.rb is not maintained well long time and nobody uses it. Surprisingly, most of features works on recent Ruby versions. However, it introduce performance penalties to the application and it lacks important features like flexible breakpoint setting or remote debugging or IDE integration (it can integrate with emacs, though) and so on. Also it doesn't have cosmetic features like coloring.&lt;/p&gt;

&lt;p&gt;Maybe byebug (&lt;a href="https://github.com/deivid-rodriguez/byebug"&gt;deivid-rodriguez/byebug: Debugging in Ruby 2&lt;/a&gt;) is most famous Ruby's debugger in recent years. byebug has enough features, however it doesn't utilize recent added &lt;code&gt;TracePoint&lt;/code&gt; features for debuggers, so it is slow when some breakpoint is set.&lt;/p&gt;

&lt;p&gt;byebug supports threading. However Ruby 3.0 introduced Ractor (&lt;a href="https://github.com/ruby/ruby/blob/master/doc/ractor.md"&gt;ruby/ractor.md at master · ruby/ruby&lt;/a&gt;). Ractor enables to make parallel program in Ruby, but the parallel programming is difficult and it needs support for debugging. However, it is hard to support ractors by existing debuggers.&lt;/p&gt;

&lt;p&gt;Hidden but the most important motivation is I (Koichi) likes to make a developer support tools. I feel that using debugger is difficult for the programmers. It is challenging topic to provide useful features for many Rubyists.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making a new gem
&lt;/h2&gt;

&lt;p&gt;We talked about replacement plan of lib/debug.rb. Fortunately we can reserve "debug" gem name in rubygem.org so we decided to make a new gem and replace with lib/debug.rb&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This article introduces the background and the motivation of debug.gem.&lt;/p&gt;

&lt;p&gt;There is no debugger implementation to utilize MRI's new features for debuggers and no debugger supports Ractors. This is why we decided to make a new debugger.&lt;/p&gt;

&lt;p&gt;I really appreciate any feedback about debug.gem. Next I will introduce basic features of debug.gem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Acknowledgements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;This work is supported by &lt;a href="https://www.cookpadteam.com/careers"&gt;Cookpad&lt;/a&gt;, an employer of me (Koichi). In recent months Cookpad allowed me to focus on the debugger development.&lt;/li&gt;
&lt;li&gt;Naoto Ono helps us to prepare testing framework on Google summer of code project (&lt;a href="https://summerofcode.withgoogle.com/organizations/4697800446574592/#5080690120458240"&gt;Ruby | Google Summer of Code&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/st0012"&gt;Stan Lo&lt;/a&gt; contributes appealing features like text coloring and tells us the debugger experience on his projects.&lt;/li&gt;
&lt;li&gt;And all contributors!&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ruby</category>
      <category>debugger</category>
    </item>
  </channel>
</rss>
