<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://nithinbekal.com/feed/ruby.xml" rel="self" type="application/atom+xml" /><link href="https://nithinbekal.com/" rel="alternate" type="text/html" /><updated>2026-05-19T12:26:59+00:00</updated><id>https://nithinbekal.com/feed/ruby.xml</id><title type="html">Nithin Bekal | Ruby</title><subtitle>Nithin Bekal&apos;s blog about programming - Ruby, Rails, Vim, Elixir.</subtitle><entry><title type="html">Migrate from Devise to Rails authentication generator</title><link href="https://nithinbekal.com/posts/devise-to-rails-auth/" rel="alternate" type="text/html" title="Migrate from Devise to Rails authentication generator" /><published>2026-03-24T00:00:00+00:00</published><updated>2026-03-24T00:00:00+00:00</updated><id>https://nithinbekal.com/posts/devise-to-rails-auth</id><content type="html" xml:base="https://nithinbekal.com/posts/devise-to-rails-auth/"><![CDATA[<p>Although Devise is a fantastic authentication library, I’ve been reaching for <a href="https://nithinbekal.com/posts/rails-8-auth/">Rails’s built in authentication generator</a> since it was introduced in Rails 8. Almost all of my hobby apps are for a single user (me!) so I don’t really need all the features of devise.</p>

<p>Recently, I finally removed devise from an old project and replaced it with the generated authentication code. This turned out to be far easier than expected. Here are my notes:</p>

<h3 id="run-the-generator">Run the generator</h3>

<p>The first thing to do is to run the generator:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rails generate authentication
</code></pre></div></div>

<p>This creates a bunch of files and overwrites some of our existing code, especially the <code class="language-plaintext highlighter-rouge">User</code> model. (<a href="https://www.bigbinary.com/blog/rails-8-introduces-a-basic-authentication-generator">Here’s an excellent walkthrough of the generated code</a>.)</p>

<h3 id="create-a-new-migration-for-users-table">Create a new migration for users table</h3>

<p>The generator creates a <code class="language-plaintext highlighter-rouge">CreateUsers</code> migration, but we already have a users table. I deleted the generated migration, and created a new one that updates the table to match what the generated code expects.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">MigrateUsersFromDevise</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Migration</span><span class="p">[</span><span class="mf">8.1</span><span class="p">]</span>
  <span class="k">def</span> <span class="nf">change</span>
    <span class="n">rename_column</span> <span class="ss">:users</span><span class="p">,</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">:email_address</span>
    <span class="n">rename_column</span> <span class="ss">:users</span><span class="p">,</span> <span class="ss">:encrypted_password</span><span class="p">,</span> <span class="ss">:password_digest</span>

    <span class="n">change_column_default</span> <span class="ss">:users</span><span class="p">,</span> <span class="ss">:email_address</span><span class="p">,</span> <span class="kp">nil</span>
    <span class="n">change_column_default</span> <span class="ss">:users</span><span class="p">,</span> <span class="ss">:password_digest</span><span class="p">,</span> <span class="kp">nil</span>

    <span class="n">remove_index</span> <span class="ss">:users</span><span class="p">,</span> <span class="ss">name: </span><span class="s2">"index_users_on_reset_password_token"</span>

    <span class="n">remove_column</span> <span class="ss">:users</span><span class="p">,</span> <span class="ss">:reset_password_token</span><span class="p">,</span> <span class="ss">:string</span>
    <span class="n">remove_column</span> <span class="ss">:users</span><span class="p">,</span> <span class="ss">:reset_password_sent_at</span><span class="p">,</span> <span class="ss">:datetime</span>
    <span class="n">remove_column</span> <span class="ss">:users</span><span class="p">,</span> <span class="ss">:remember_created_at</span><span class="p">,</span> <span class="ss">:datetime</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>The generator uses <code class="language-plaintext highlighter-rouge">email_address</code> instead of Devise’s <code class="language-plaintext highlighter-rouge">email</code> column. Keeping the name could have worked, but I decided to switch to the new name for the sake of consistency across projects.</p>

<h3 id="restore-the-user-model">Restore the <code class="language-plaintext highlighter-rouge">User</code> model</h3>

<p>The generator overwrote <code class="language-plaintext highlighter-rouge">app/models/user.rb</code>. Keep the generated <code class="language-plaintext highlighter-rouge">has_secure_password</code>, <code class="language-plaintext highlighter-rouge">has_many :sessions</code>, and <code class="language-plaintext highlighter-rouge">normalizes :email_address</code> lines, then restore all the other validations, associations, or any methods that you might have.</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">class User &lt; ApplicationRecord
</span><span class="gd">-  devise :database_authenticatable, :registerable,
-    :recoverable, :rememberable, :validatable
</span><span class="gi">+  has_secure_password
+  has_many :sessions, dependent: :destroy
+
+  normalizes :email_address, with: -&gt;(e) { e.strip.downcase }
+
+  validates :email_address, presence: true, uniqueness: true
+  validates :password, length: { minimum: 6 }, allow_nil: true
</span>  
  # other code
<span class="p">end
</span></code></pre></div></div>

<h3 id="update-the-routes">Update the routes</h3>

<p>The generator would already have added the new routes for sessions and passwords, but we still have a <code class="language-plaintext highlighter-rouge">devise_for :users</code> line, which should be removed.</p>

<h3 id="fix-up-applicationcontroller">Fix up <code class="language-plaintext highlighter-rouge">ApplicationController</code></h3>

<p>Devise exposes a <code class="language-plaintext highlighter-rouge">current_user</code> helper that I use everywhere in the views, but the generated code exposes the current user using <code class="language-plaintext highlighter-rouge">CurrentAttributes</code>. I decided to add the helpers I was already using to <code class="language-plaintext highlighter-rouge">ApplicationController</code>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">ApplicationController</span> <span class="o">&lt;</span> <span class="no">ActionController</span><span class="o">::</span><span class="no">Base</span>
  <span class="kp">include</span> <span class="no">Authentication</span>

  <span class="k">def</span> <span class="nf">current_user</span> <span class="o">=</span> <span class="no">Current</span><span class="p">.</span><span class="nf">user</span>
  <span class="k">def</span> <span class="nf">user_signed_in?</span> <span class="o">=</span> <span class="no">Current</span><span class="p">.</span><span class="nf">user</span><span class="p">.</span><span class="nf">present?</span>
<span class="k">end</span>
</code></pre></div></div>

<h3 id="fix-test-helpers">Fix test helpers</h3>

<p><code class="language-plaintext highlighter-rouge">Devise::Test::IntegrationHelpers</code> was included in <code class="language-plaintext highlighter-rouge">test_helper.rb</code>, so I removed it. The generator added a <code class="language-plaintext highlighter-rouge">test/test_helpers/session_test_helper.rb</code> file with a <code class="language-plaintext highlighter-rouge">sign_in_as</code> method. I added a <code class="language-plaintext highlighter-rouge">sign_in</code> alias for that method, so I don’t have to change all the existing tests in the same PR.</p>

<p>The fixtures file got replaced by the generator, which needed to be cleaned up. I made sure that the fixtures I needed were still there:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">&lt;% password_digest = BCrypt::Password.create("password", cost</span><span class="err">:</span> <span class="s">1) %&gt;</span>

<span class="na">admin</span><span class="pi">:</span>
  <span class="na">email_address</span><span class="pi">:</span> <span class="s">admin@example.com</span>
  <span class="na">password_digest</span><span class="pi">:</span> <span class="s">&lt;%= password_digest %&gt;</span>
</code></pre></div></div>

<h3 id="add-registrationscontroller">Add <code class="language-plaintext highlighter-rouge">RegistrationsController</code></h3>

<p>The generator doesn’t add a sign up route, so I had to manually add it. This is documented in my previous <a href="/posts/rails-8-auth/#adding-registrations-to-rails-8">post about the authentication generator</a>.</p>

<h3 id="replace-devise-path-helpers">Replace devise path helpers</h3>

<p>The devise path helper names are different, so we need to update those in the views:</p>

<ul>
  <li>Login: <code class="language-plaintext highlighter-rouge">new_session_path</code> instead of <code class="language-plaintext highlighter-rouge">new_user_session_path</code></li>
  <li>Logout: <code class="language-plaintext highlighter-rouge">session_path</code> instead of <code class="language-plaintext highlighter-rouge">destroy_user_session_path</code></li>
</ul>

<h3 id="fix-system-tests">Fix system tests</h3>

<p>Devise includes <code class="language-plaintext highlighter-rouge">Warden::Test::Helpers</code> in <code class="language-plaintext highlighter-rouge">ApplicationSystemTestCase</code>, which provides a <code class="language-plaintext highlighter-rouge">login_as</code> helper. Warden is no longer a dependency, so we need to remove that and the <code class="language-plaintext highlighter-rouge">teardown</code> block that references it:</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">class ApplicationSystemTestCase &lt; ActionDispatch::SystemTestCase
</span><span class="gd">-  include Warden::Test::Helpers
-
-  teardown do
-    Warden.test_reset! # Reset Warden after each test
-  end
</span><span class="p">end
</span></code></pre></div></div>

<p>We can now replace the helper with a new method:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">ApplicationSystemTestCase</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">SystemTestCase</span>
  <span class="c1"># ...</span>

  <span class="k">def</span> <span class="nf">sign_in</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
    <span class="n">session</span> <span class="o">=</span> <span class="n">user</span><span class="p">.</span><span class="nf">sessions</span><span class="p">.</span><span class="nf">create!</span>
    <span class="n">visit</span> <span class="n">root_url</span>
    <span class="n">signed_value</span> <span class="o">=</span> <span class="n">signed_cookie_value</span><span class="p">(</span><span class="n">session</span><span class="p">.</span><span class="nf">id</span><span class="p">)</span>

    <span class="n">page</span><span class="p">.</span><span class="nf">driver</span><span class="p">.</span><span class="nf">browser</span><span class="p">.</span><span class="nf">manage</span><span class="p">.</span><span class="nf">add_cookie</span><span class="p">(</span>
      <span class="ss">name: </span><span class="s2">"session_id"</span><span class="p">,</span>
      <span class="ss">value: </span><span class="n">signed_value</span><span class="p">,</span>
      <span class="ss">path: </span><span class="s2">"/"</span><span class="p">,</span>
    <span class="p">)</span>
    <span class="n">visit</span> <span class="n">current_url</span>
  <span class="k">end</span>

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

  <span class="k">def</span> <span class="nf">signed_cookie_value</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
    <span class="n">key_generator</span> <span class="o">=</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">KeyGenerator</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span>
      <span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">secret_key_base</span><span class="p">,</span> <span class="ss">iterations: </span><span class="mi">1000</span>
    <span class="p">)</span>
    <span class="n">secret</span> <span class="o">=</span> <span class="n">key_generator</span><span class="p">.</span><span class="nf">generate_key</span><span class="p">(</span><span class="s2">"signed cookie"</span><span class="p">)</span>
    <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">MessageVerifier</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">secret</span><span class="p">).</span><span class="nf">generate</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h3 id="remove-devise-completely">Remove devise completely</h3>

<p>Now that everything else is fixed, we can remove all references to <code class="language-plaintext highlighter-rouge">devise</code> in the code:</p>

<ul>
  <li>Remove <code class="language-plaintext highlighter-rouge">gem "devise"</code> from <code class="language-plaintext highlighter-rouge">Gemfile</code> and run <code class="language-plaintext highlighter-rouge">bundle install</code></li>
  <li>Delete <code class="language-plaintext highlighter-rouge">config/initializers/devise.rb</code></li>
  <li>Delete <code class="language-plaintext highlighter-rouge">config/locales/devise.en.yml</code></li>
</ul>

<h3 id="wrapping-up">Wrapping up</h3>

<p>With that, I was able to remove another dependency from the project. If I had more complex authentication requirements, I’d have kept devise on. However, it is overkill for an app only for myself. The migration was quite easy, and now I have one less dependency to worry about.</p>]]></content><author><name></name></author><category term="ruby" /><category term="rails" /><summary type="html"><![CDATA[Although Devise is a fantastic authentication library, I’ve been reaching for Rails’s built in authentication generator since it was introduced in Rails 8. Almost all of my hobby apps are for a single user (me!) so I don’t really need all the features of devise.]]></summary></entry><entry><title type="html">Minimal Sorbet setup with inline RBS comments</title><link href="https://nithinbekal.com/posts/minimal-sorbet-rbs-setup/" rel="alternate" type="text/html" title="Minimal Sorbet setup with inline RBS comments" /><published>2026-02-01T00:00:00+00:00</published><updated>2026-02-01T00:00:00+00:00</updated><id>https://nithinbekal.com/posts/minimal-sorbet-rbs-setup</id><content type="html" xml:base="https://nithinbekal.com/posts/minimal-sorbet-rbs-setup/"><![CDATA[<p>I’ve been working through the fantastic <a href="https://craftinginterpreters.com/contents.html">Crafting Interpreters</a> book, and implementing the <a href="https://github.com/nithinbekal/rlox">Lox interpreter in Ruby</a>. I wanted a minimal type checking setup for the code, so I decided to configure sorbet with RBS comment syntax.</p>

<p>First, we add the <code class="language-plaintext highlighter-rouge">sorbet</code> and <code class="language-plaintext highlighter-rouge">tapioca</code> gems to the <code class="language-plaintext highlighter-rouge">Gemfile</code>.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">gem</span> <span class="s2">"sorbet"</span>
<span class="n">gem</span> <span class="s2">"tapioca"</span><span class="p">,</span> <span class="ss">require: </span><span class="kp">false</span>
</code></pre></div></div>

<p>Next, we add a <code class="language-plaintext highlighter-rouge">sorbet/config</code> file that includes all the arguments that we would pass when running the <code class="language-plaintext highlighter-rouge">srb typecheck</code> command:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>--dir
lib
--enable-experimental-rbs-comments
</code></pre></div></div>

<p>And that’s basically it! You can now type check your Ruby codebase with <code class="language-plaintext highlighter-rouge">bundle exec srb typecheck</code>.</p>

<p>Based on my experience <a href="https://nithinbekal.com/posts/sorbet-rails/">adding Sorbet to a Rails app</a> in the past, I expected this to be more cumbersome. Having seen how straightforward it is to get started, I think sorbet is going to be part of even my smaller Ruby projects.</p>]]></content><author><name></name></author><category term="ruby" /><category term="sorbet" /><summary type="html"><![CDATA[I’ve been working through the fantastic Crafting Interpreters book, and implementing the Lox interpreter in Ruby. I wanted a minimal type checking setup for the code, so I decided to configure sorbet with RBS comment syntax.]]></summary></entry><entry><title type="html">What’s new in Ruby 4.0</title><link href="https://nithinbekal.com/posts/ruby-4-0/" rel="alternate" type="text/html" title="What’s new in Ruby 4.0" /><published>2025-12-17T00:00:00+00:00</published><updated>2025-12-17T00:00:00+00:00</updated><id>https://nithinbekal.com/posts/ruby-4-0</id><content type="html" xml:base="https://nithinbekal.com/posts/ruby-4-0/"><![CDATA[<p>Ruby 4.0 will be released next week on Christmas day. This release brings a new JIT compiler, improvements to Ractors, a new mechanism to define namespaces called <code class="language-plaintext highlighter-rouge">Ruby::Box</code>, and a whole lot of other changes.</p>

<p>Although it’s a major version bump, there shouldn’t be any serious breaking changes. This version bump is to celebrate 30 years since the first public release of Ruby.</p>

<h2 id="rubybox"><code class="language-plaintext highlighter-rouge">Ruby::Box</code></h2>

<p><code class="language-plaintext highlighter-rouge">Ruby::Box</code> is an experimental feature that brings isolated namespaces to Ruby. This can be enabled by setting the <code class="language-plaintext highlighter-rouge">RUBY_BOX=1</code> environment variable. This can allow you to do things like loading two versions of a library at the same time like this:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># foo_v1.rb</span>
<span class="k">class</span> <span class="nc">Foo</span>
  <span class="k">def</span> <span class="nf">hello</span>
    <span class="s2">"Foo version 1"</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="c1"># foo_v2.rb</span>
<span class="k">class</span> <span class="nc">Foo</span>
  <span class="k">def</span> <span class="nf">hello</span>
    <span class="s2">"Foo version 2"</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="c1"># main.rb</span>

<span class="n">v1</span> <span class="o">=</span> <span class="no">Ruby</span><span class="o">::</span><span class="no">Box</span><span class="p">.</span><span class="nf">new</span>
<span class="n">v1</span><span class="p">.</span><span class="nf">require</span><span class="p">(</span><span class="s2">"./foo_v1"</span><span class="p">)</span>

<span class="n">v2</span> <span class="o">=</span> <span class="no">Ruby</span><span class="o">::</span><span class="no">Box</span><span class="p">.</span><span class="nf">new</span>
<span class="n">v2</span><span class="p">.</span><span class="nf">require</span><span class="p">(</span><span class="s2">"./foo_v2"</span><span class="p">)</span>

<span class="n">v1</span><span class="o">::</span><span class="no">Foo</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">hello</span> <span class="c1">#=&gt; "Foo version 1"</span>
<span class="n">v2</span><span class="o">::</span><span class="no">Foo</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">hello</span> <span class="c1">#=&gt; "Foo version 2"</span>
</code></pre></div></div>

<p>I find the syntax rather confusing, with the need for instantiating a <code class="language-plaintext highlighter-rouge">Box</code> object. But this is still an experimental feature, so we’ll hopefully have better ergonomics with the final version.</p>
<h2 id="ractor">Ractor</h2>

<p>Ractor’s API has been redesigned to use <code class="language-plaintext highlighter-rouge">Ractor::Port</code> as the means for communicating between ractors. As a result <code class="language-plaintext highlighter-rouge">Ractor.yield</code> and <code class="language-plaintext highlighter-rouge">Ractor#take</code> have been removed. Now, you would use a ractor port like this:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">port</span> <span class="o">=</span> <span class="no">Ractor</span><span class="o">::</span><span class="no">Port</span><span class="p">.</span><span class="nf">new</span>

<span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">port</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="nb">p</span><span class="o">|</span>
  <span class="nb">p</span> <span class="o">&lt;&lt;</span> <span class="s2">"first value"</span>
  <span class="nb">p</span> <span class="o">&lt;&lt;</span> <span class="s2">"second value"</span>
<span class="k">end</span>

<span class="nb">puts</span> <span class="n">port</span><span class="p">.</span><span class="nf">receive</span> <span class="c1">#=&gt; "first value"</span>
<span class="nb">puts</span> <span class="n">port</span><span class="p">.</span><span class="nf">receive</span> <span class="c1">#=&gt; "second value"</span>
</code></pre></div></div>

<p>In Ruby 3.4, this would have looked like this:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ractor</span> <span class="o">=</span> <span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span> <span class="k">do</span>
  <span class="no">Ractor</span><span class="p">.</span><span class="nf">yield</span> <span class="s2">"first value"</span>
  <span class="no">Ractor</span><span class="p">.</span><span class="nf">yield</span> <span class="s2">"second value"</span>
<span class="k">end</span>

<span class="nb">puts</span> <span class="n">ractor</span><span class="p">.</span><span class="nf">take</span>  <span class="c1"># =&gt; "first value"</span>
<span class="nb">puts</span> <span class="n">ractor</span><span class="p">.</span><span class="nf">take</span>  <span class="c1"># =&gt; "second value"</span>
</code></pre></div></div>

<h2 id="zjit">ZJIT</h2>

<p>A new JIT compiler called <a href="https://railsatscale.com/2025-05-14-merge-zjit/">ZJIT has been merged into Ruby</a>. This implements a method based JIT compiler, compared to the lazy basic block versioning compiler that YJIT uses. Using a more traditional type of JIT will hopefully make the codebase more accessible to new contributors.</p>

<p>Although ZJIT is faster than the interpreted code, it hasn’t yet caught up with YJIT. The latter is still the recommended JIT for production. However, this sets the stage for some more speedups in the next year.</p>

<h2 id="logical-operators-on-the-next-line">Logical operators on the next line</h2>

<p>The following syntax is now allowed for logical operators <code class="language-plaintext highlighter-rouge">and</code>, <code class="language-plaintext highlighter-rouge">or</code>, <code class="language-plaintext highlighter-rouge">&amp;&amp;</code> and <code class="language-plaintext highlighter-rouge">||</code>.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="n">condition1?</span>
  <span class="o">&amp;&amp;</span> <span class="n">condition2?</span>
  <span class="o">&amp;&amp;</span> <span class="n">condition3?</span>
  <span class="c1"># do something</span>
<span class="k">end</span>

<span class="c1"># The above is the same as what we can currently do with:</span>

<span class="k">if</span> <span class="n">condition1?</span> <span class="o">&amp;&amp;</span>
  <span class="n">condition2?</span> <span class="o">&amp;&amp;</span>
  <span class="n">condition3?</span>
  <span class="c1"># do something</span>
<span class="k">end</span>
</code></pre></div></div>

<h2 id="ruby-top-level-module"><code class="language-plaintext highlighter-rouge">Ruby</code> top level module</h2>

<p>The <code class="language-plaintext highlighter-rouge">Ruby</code> top level module was reserved in Ruby 3.4, but now it actually has some constants defined in it:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Ruby</span><span class="o">::</span><span class="no">VERSION</span>
<span class="c1">#=&gt; "4.0.0"</span>

<span class="no">Ruby</span><span class="o">::</span><span class="no">DESCRIPTION</span>
<span class="c1">#=&gt; "ruby 4.0.0preview2 (2025-11-17 master 4fa6e9938c) +PRISM [x86_64-darwin24]"</span>

<span class="c1"># Other constants in the module:</span>
<span class="no">Ruby</span><span class="p">.</span><span class="nf">constants</span>
<span class="c1">#=&gt; [:REVISION, :COPYRIGHT, :ENGINE, :ENGINE_VERSION, :DESCRIPTION,</span>
<span class="c1">#    :VERSION, :RELEASE_DATE, :Box, :PLATFORM, :PATCHLEVEL]</span>
</code></pre></div></div>

<h2 id="instance_variables_to_inspect"><code class="language-plaintext highlighter-rouge">instance_variables_to_inspect</code></h2>

<p>When <code class="language-plaintext highlighter-rouge">inspect</code> is called on an object, it includes all instance variables, including memoization variables, which can get noisy in larger classes. For instance, the <code class="language-plaintext highlighter-rouge">@area</code> variable shows up below after the <code class="language-plaintext highlighter-rouge">area</code> method is called.</p>

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

<span class="n">square</span> <span class="o">=</span> <span class="no">Square</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
<span class="nb">puts</span> <span class="n">square</span> <span class="c1">#=&gt; #&lt;Square:0x000000011f280ff8 @width=5&gt;</span>

<span class="n">square</span><span class="p">.</span><span class="nf">area</span> <span class="c1">#=&gt; 25</span>
<span class="nb">puts</span> <span class="n">square</span> <span class="c1">#=&gt; =&gt; #&lt;Square:0x000000011f280ff8 @area=25 @width=5&gt;</span>
</code></pre></div></div>

<p>However, by defining which variables should be shown like this, we can make the inspect output less noisy.</p>

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

  <span class="kp">private</span>
  
  <span class="k">def</span> <span class="nf">instance_variables_to_inspect</span> <span class="o">=</span> <span class="p">[</span><span class="ss">:@width</span><span class="p">]</span>
<span class="k">end</span>

<span class="n">square</span><span class="p">.</span><span class="nf">area</span> <span class="c1"># 25</span>
<span class="nb">puts</span> <span class="n">square</span> <span class="c1">#=&gt; =&gt; #&lt;Square:0x000000011f280ff8 @width=5&gt;</span>
</code></pre></div></div>

<h2 id="arrayrfind"><code class="language-plaintext highlighter-rouge">Array#rfind</code></h2>

<p><code class="language-plaintext highlighter-rouge">Array#rfind</code> has been implemented to find the last element matching a condition. This is a more efficient alternative to <code class="language-plaintext highlighter-rouge">reverse_each.find</code>. It avoids an array allocation that happens when calling <code class="language-plaintext highlighter-rouge">Enumerable#reverse_each</code>.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># new</span>
<span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">].</span><span class="nf">rfind</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:odd?</span><span class="p">)</span> <span class="c1">#=&gt; 7</span>

<span class="c1"># old</span>
<span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">].</span><span class="nf">reverse_each</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:odd?</span><span class="p">)</span> <span class="c1">#=&gt; 7</span>
</code></pre></div></div>

<p>At the same time, <code class="language-plaintext highlighter-rouge">Array#find</code> has also been added, which is a more efficient implementation than <code class="language-plaintext highlighter-rouge">Enumerable#find</code> that was being used before.</p>

<h2 id="other-changes">Other changes</h2>

<ul>
  <li><a href="https://railsatscale.com/2025-05-21-fast-allocations-in-ruby-3-5/">Object allocations are significantly faster</a> - over 2x without JIT and almost 4x with JIT enabled.</li>
  <li><a href="https://github.com/ruby/rjit">RJIT</a> has been extracted into a separate gem.</li>
  <li><code class="language-plaintext highlighter-rouge">Set</code> and <code class="language-plaintext highlighter-rouge">Pathname</code> are now core classes. Previously, <code class="language-plaintext highlighter-rouge">Set</code> was an autoloaded stdlib class, while  <code class="language-plaintext highlighter-rouge">Pathname</code> was an autoloaded default gem.</li>
  <li>CGI library has been removed from default gems, but a few commonly used features such as <code class="language-plaintext highlighter-rouge">CGI.escape</code> and related methods are retained and can be used by requiring <code class="language-plaintext highlighter-rouge">cgi/escape</code>.</li>
</ul>

<h2 id="further-reading">Further reading</h2>

<p>This post highlights changes that I personally found most interesting, and skip over features I might not use. If you’re looking for a more comprehensive look at the release, I highly recommend looking at the <a href="https://www.ruby-lang.org/en/news/2025/11/17/ruby-4-0-0-preview2-released/">release announcement</a>, and <a href="https://docs.ruby-lang.org/en/master/NEWS_md.html">changelog</a>. The <a href="https://rubyreferences.github.io/rubychanges/4.0.html">Ruby References</a> website is another fantastic resource if you want a deeper dive into all of the changes in this version.</p>

<div class="note">
  <p>
    This article is part of the <em>What's New in Ruby</em> series.
    To read about a different version of Ruby,
    pick the version here:
  </p>

  <p>
    <a href="/posts/ruby-2-3-features/">2.3</a>
    &nbsp;&nbsp;
    <a href="/posts/ruby-2-4-features/">2.4</a>
    &nbsp;&nbsp;
    <a href="/posts/ruby-2-5-features/">2.5</a>
    &nbsp;&nbsp;
    <a href="/posts/ruby-2-6/">2.6</a>
    &nbsp;&nbsp;
    <a href="/posts/ruby-2-7/">2.7</a>
    &nbsp;&nbsp;
    <a href="/posts/ruby-3-0/">3.0</a>
    &nbsp;&nbsp;
    <a href="/posts/ruby-3-1/">3.1</a>
    &nbsp;&nbsp;
    <a href="/posts/ruby-3-2/">3.2</a>
    &nbsp;&nbsp;
    <a href="/posts/ruby-3-3/">3.3</a>
    &nbsp;&nbsp;
    <a href="/posts/ruby-3-4/">3.4</a>
    &nbsp;&nbsp;
    <a href="/posts/ruby-4-0/">4.0</a>
  </p>
</div>]]></content><author><name></name></author><category term="ruby" /><summary type="html"><![CDATA[Ruby 4.0 will be released next week on Christmas day. This release brings a new JIT compiler, improvements to Ractors, a new mechanism to define namespaces called Ruby::Box, and a whole lot of other changes.]]></summary></entry><entry><title type="html">Stop memoizing Hash lookups in Ruby</title><link href="https://nithinbekal.com/posts/ruby-hash-memoization/" rel="alternate" type="text/html" title="Stop memoizing Hash lookups in Ruby" /><published>2025-07-11T00:00:00+00:00</published><updated>2025-07-11T00:00:00+00:00</updated><id>https://nithinbekal.com/posts/ruby-hash-memoization</id><content type="html" xml:base="https://nithinbekal.com/posts/ruby-hash-memoization/"><![CDATA[<p>When a method performs a slow operation,
memoizing the result using instance variables is a useful optimization.
However, I’ve often seen people (including myself, sometimes!)
reaching for memoization for things that don’t need to be optimized.</p>

<p>One common example is when there’s a class that wraps a Hash object.
Hashes in Ruby are quite well optimized,
so do you really need to memoize the result of the hash lookup?
Let’s benchmark and find out.</p>

<h2 id="benchmarks">Benchmarks</h2>

<p>Let’s start with a simple <code class="language-plaintext highlighter-rouge">Setting</code> class that takes a data hash with <code class="language-plaintext highlighter-rouge">type</code> and <code class="language-plaintext highlighter-rouge">value</code> keys.</p>

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

  <span class="k">def</span> <span class="nf">type</span>
    <span class="vi">@data</span><span class="p">[</span><span class="s2">"type"</span><span class="p">]</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">type_memoized</span>
    <span class="vi">@type</span> <span class="o">||=</span> <span class="vi">@data</span><span class="p">[</span><span class="s2">"type"</span><span class="p">]</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>There’s a <code class="language-plaintext highlighter-rouge">type</code> method here, and I’ve added a <code class="language-plaintext highlighter-rouge">type_memoized</code> method for comparison.
Now let’s benchmark the two methods:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">setting</span> <span class="o">=</span> <span class="no">Setting</span><span class="p">.</span><span class="nf">new</span><span class="p">({</span> <span class="s2">"type"</span> <span class="o">=&gt;</span> <span class="s2">"string"</span><span class="p">,</span> <span class="s2">"value"</span> <span class="o">=&gt;</span> <span class="s2">"hello"</span> <span class="p">})</span>

<span class="no">Benchmark</span><span class="p">.</span><span class="nf">ips</span> <span class="k">do</span> <span class="o">|</span><span class="n">x</span><span class="o">|</span>
  <span class="n">x</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s2">"type"</span><span class="p">)</span> <span class="p">{</span> <span class="n">setting</span><span class="p">.</span><span class="nf">type</span> <span class="p">}</span>
  <span class="n">x</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s2">"memoized type"</span><span class="p">)</span> <span class="p">{</span> <span class="n">setting</span><span class="p">.</span><span class="nf">type_memoized</span> <span class="p">}</span>
  <span class="n">x</span><span class="p">.</span><span class="nf">compare!</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Running this showed that memoization does indeed make things faster.
On my 2019 Intel Macbook, this showed a 1.31x improvement:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ruby 3.4.4 (2025-05-14 revision a38531fd3f) +PRISM [x86_64-darwin24]
Warming up --------------------------------------
         type     1.339M i/100ms
memoized type     1.586M i/100ms
Calculating -------------------------------------
         type     12.411M (± 1.0%) i/s   (80.57 ns/i) -     62.954M in   5.072739s
memoized type     16.214M (± 1.0%) i/s   (61.68 ns/i) -     82.453M in   5.085992s

Comparison:
memoized type: 16213611.7 i/s
         type: 12411448.7 i/s - 1.31x  slower
</code></pre></div></div>

<p>However, you will notice that in actual time per method call,
the difference is tiny - less than 20 nanoseconds on my 6 year old Macbook!
Unless you’re calling this thousands of times in a loop, this will likely never be a bottleneck in your code.</p>

<h2 id="looking-up-nonexistent-keys">Looking up nonexistent keys</h2>

<p>Let’s look at one other case.
What happens when we memoize a lookup for a key that doesn’t exist?</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">setting_without_type</span> <span class="o">=</span> <span class="no">Setting</span><span class="p">.</span><span class="nf">new</span><span class="p">({</span> <span class="s2">"value"</span> <span class="o">=&gt;</span> <span class="s2">"hello"</span> <span class="p">})</span>

<span class="no">Benchmark</span><span class="p">.</span><span class="nf">ips</span> <span class="k">do</span> <span class="o">|</span><span class="n">x</span><span class="o">|</span>
  <span class="n">x</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s2">"missing key"</span><span class="p">)</span> <span class="p">{</span> <span class="n">setting_without_type</span><span class="p">.</span><span class="nf">type</span> <span class="p">}</span>
  <span class="n">x</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s2">"memoized missing key"</span><span class="p">)</span> <span class="p">{</span> <span class="n">setting_without_type</span><span class="p">.</span><span class="nf">type_memoized</span> <span class="p">}</span>
  <span class="n">x</span><span class="p">.</span><span class="nf">compare!</span>
<span class="k">end</span>
</code></pre></div></div>

<p>This time, the memoized version is actually 1.2x slower!
This is because <code class="language-plaintext highlighter-rouge">@data["type"]</code> returns nil every time,
so we don’t ever return the memoized value.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>         missing key     13.163M (± 1.4%) i/s   (75.97 ns/i) -     66.077M in   5.021125s
memoized missing key     10.853M (± 4.3%) i/s   (92.14 ns/i) -     55.153M in   5.091847s

Comparison:
         missing key: 13162501.1 i/s
memoized missing key: 10852595.0 i/s - 1.21x  slower
</code></pre></div></div>

<p><strong>Update</strong>: As <a href="https://github.com/phallstrom">@phallstorm</a> mentions in the comments below,
we could prevent the cache misses by using the <code class="language-plaintext highlighter-rouge">if defined?(@type)</code> memoization pattern.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">type_memoized_correctly</span>
  <span class="k">return</span> <span class="vi">@type</span> <span class="k">if</span> <span class="k">defined?</span><span class="p">(</span><span class="vi">@type</span><span class="p">)</span>
  <span class="vi">@type</span> <span class="o">=</span> <span class="vi">@data</span><span class="p">[</span><span class="s2">"type"</span><span class="p">]</span>
<span class="k">end</span>
</code></pre></div></div>

<p>At this point, we’ve achieved similar performance as the case where the key is present.
However, the downside is that more than half the method is now memoization logic,
without too much performance benefit.</p>

<h2 id="nested-keys">Nested keys</h2>

<p>Finally, let’s see what happens when we’re dealing with nested hashes:</p>

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

  <span class="k">def</span> <span class="nf">nested_value</span>
    <span class="vi">@data</span><span class="p">[</span><span class="s2">"nested"</span><span class="p">][</span><span class="s2">"value"</span><span class="p">]</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">nested_value_memoized</span>
    <span class="vi">@nested_value</span> <span class="o">||=</span> <span class="vi">@data</span><span class="p">[</span><span class="s2">"nested"</span><span class="p">][</span><span class="s2">"value"</span><span class="p">]</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="n">setting</span> <span class="o">=</span> <span class="no">Setting</span><span class="p">.</span><span class="nf">new</span><span class="p">({</span> <span class="s2">"nested"</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="s2">"value"</span> <span class="o">=&gt;</span> <span class="s2">"hello"</span> <span class="p">}</span> <span class="p">})</span>

<span class="no">Benchmark</span><span class="p">.</span><span class="nf">ips</span> <span class="k">do</span> <span class="o">|</span><span class="n">x</span><span class="o">|</span>
  <span class="n">x</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s2">"nested key"</span><span class="p">)</span> <span class="p">{</span> <span class="n">setting</span><span class="p">.</span><span class="nf">nested_value</span> <span class="p">}</span>
  <span class="n">x</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s2">"memoized nested key"</span><span class="p">)</span> <span class="p">{</span> <span class="n">setting</span><span class="p">.</span><span class="nf">nested_value_memoized</span> <span class="p">}</span>
  <span class="n">x</span><span class="p">.</span><span class="nf">compare!</span>
<span class="k">end</span>
</code></pre></div></div>

<p>This time the memoized version is about 1.6x faster.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>         nested key     10.654M (± 5.6%) i/s   (93.86 ns/i) -     54.107M in   5.097061s
memoized nested key     16.867M (± 1.3%) i/s   (59.29 ns/i) -     85.219M in   5.053237s

Comparison:
memoized nested key: 16866963.5 i/s
         nested key: 10654347.8 i/s - 1.58x  slower
</code></pre></div></div>

<h2 id="conclusion">Conclusion</h2>

<p>The highlights from the benchmarks are:</p>

<ul>
  <li>Hash lookups are already extremely fast, in the order of nanoseconds.</li>
  <li>🔼 1.3x speedup on memoizing a hash lookup</li>
  <li>🔼 1.6x speedup if memoizing a two-level nested hash</li>
  <li>🔻 1.2x slowdown on memoizing a hash lookup where the key doesn’t exist.</li>
</ul>

<p>So, do you really need to memoize a hash lookup?
Most likely not.
Hash lookups are already optimized at the language level,
so the difference between that and instance variable lookups is already tiny.</p>

<p>Reserve memoization for cases where it really matters,
such as database calls or really expensive computations.</p>

<p>There’s no need to optimize at this level unless a profiler has showed you that it will speed things up.
There’s probably something in there that is multiple orders of magnitude slower that you can optimize.</p>]]></content><author><name></name></author><category term="ruby" /><summary type="html"><![CDATA[When a method performs a slow operation, memoizing the result using instance variables is a useful optimization. However, I’ve often seen people (including myself, sometimes!) reaching for memoization for things that don’t need to be optimized.]]></summary></entry><entry><title type="html">Migrating Postgres to SQLite using the Sequel gem</title><link href="https://nithinbekal.com/posts/psql-sqlite3-sequel/" rel="alternate" type="text/html" title="Migrating Postgres to SQLite using the Sequel gem" /><published>2025-06-26T00:00:00+00:00</published><updated>2025-06-26T00:00:00+00:00</updated><id>https://nithinbekal.com/posts/psql-sqlite3-sequel</id><content type="html" xml:base="https://nithinbekal.com/posts/psql-sqlite3-sequel/"><![CDATA[<p>In my previous post, I wrote about exporting the postgres database for
<a href="https://devlibrary.org/">devlibrary</a> from fly.io to a local file.
Now, I want to convert that into a sqlite database,
so I can get rid of the dependency on a separate database server.</p>

<p>The <a href="https://github.com/jeremyevans/sequel">sequel gem</a> allows connecting to one database,
and dumping the contents into another.</p>

<p>First, let’s import that dump into local postgres database.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>psql devlibrary_development &lt; devlibrary-dump.sql
</code></pre></div></div>

<p>Next, we’ll install <code class="language-plaintext highlighter-rouge">sequel</code> and <code class="language-plaintext highlighter-rouge">sqlite3</code> gems.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem install sequel sqlite3
</code></pre></div></div>

<p>Finally, we can dump the postgres database straight into an sqlite3 database using:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sequel -C postgres://localhost/devlibrary_development \
    sqlite://storage/development.sqlite3
</code></pre></div></div>

<p>And that’s it - we now have a sqlite3 file that can be used by a Rails app.
In fact, <a href="https://devlibrary.org/">devlibrary</a> is now actually backed by a sqlite database!</p>]]></content><author><name></name></author><category term="postgres" /><category term="ruby" /><category term="sqlite" /><summary type="html"><![CDATA[In my previous post, I wrote about exporting the postgres database for devlibrary from fly.io to a local file. Now, I want to convert that into a sqlite database, so I can get rid of the dependency on a separate database server.]]></summary></entry><entry><title type="html">What’s new in Ruby 3.4</title><link href="https://nithinbekal.com/posts/ruby-3-4/" rel="alternate" type="text/html" title="What’s new in Ruby 3.4" /><published>2024-12-17T00:00:00+00:00</published><updated>2024-12-17T00:00:00+00:00</updated><id>https://nithinbekal.com/posts/ruby-3-4</id><content type="html" xml:base="https://nithinbekal.com/posts/ruby-3-4/"><![CDATA[<p>A new version of Ruby is released every year on Christmas day.
The 3.4 release will be out next week,
so I’ve been playing around with the release candidate.
Here are some of the features I found interesting.</p>

<p><em>(As an aside,
I just realized that
this is the 10th year in a row
that I’ve written one of these
“what’s new in Ruby” recaps!)</em> 🎉</p>

<h2 id="-it---the-default-block-parameter">🧱 <code class="language-plaintext highlighter-rouge">it</code> - the default block parameter</h2>

<p>Ruby introduced numbered block parameters
5 years ago in version 2.7.
This allowed us to use <code class="language-plaintext highlighter-rouge">_1</code>, <code class="language-plaintext highlighter-rouge">_2</code> etc
instead of explicit block parameters.
Now, a new variable called <code class="language-plaintext highlighter-rouge">it</code> has been added
to refer to the default block parameter.
This can be handy when chaining multiple operations:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">isbn</span><span class="p">.</span><span class="nf">gsub</span><span class="p">(</span><span class="s2">"-"</span><span class="p">,</span> <span class="s2">""</span><span class="p">)</span>
  <span class="p">.</span><span class="nf">then</span> <span class="p">{</span> <span class="no">URI</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s2">"</span><span class="si">#{</span><span class="n">endpoint</span><span class="si">}</span><span class="s2">?q=isbn:</span><span class="si">#{</span><span class="n">it</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="p">}</span>
  <span class="p">.</span><span class="nf">then</span> <span class="p">{</span> <span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">}</span>
  <span class="p">.</span><span class="nf">then</span> <span class="p">{</span> <span class="no">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">}</span>
  <span class="p">.</span><span class="nf">then</span> <span class="p">{</span> <span class="n">extract_volume_info</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">}</span>

<span class="k">def</span> <span class="nf">extract_volume_info</span><span class="p">(</span><span class="n">response</span><span class="p">)</span>
  <span class="k">return</span> <span class="k">if</span> <span class="n">response</span><span class="p">[</span><span class="s2">"totalItems"</span><span class="p">]</span> <span class="o">==</span> <span class="mi">0</span>
  <span class="n">response</span><span class="p">[</span><span class="s2">"items"</span><span class="p">][</span><span class="s2">"volumeInfo"</span><span class="p">]</span>
<span class="k">end</span>
</code></pre></div></div>

<p>I like this as a shorthand for local method calls.
Previously, if you wanted shorthand syntax
for a method in local scope,
you’d write something like <code class="language-plaintext highlighter-rouge">&amp;method(:foo)</code>.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># old</span>
<span class="n">list</span><span class="p">.</span><span class="nf">map</span><span class="p">(</span><span class="o">&amp;</span><span class="nb">method</span><span class="p">(</span><span class="ss">:transform</span><span class="p">))</span>

<span class="c1"># new</span>
<span class="n">list</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="n">transform</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">}</span>
</code></pre></div></div>

<p>With the <code class="language-plaintext highlighter-rouge">&amp;method</code> syntax,
the method name is a symbol,
making it harder for editors and IDEs
to identify it as a method reference
when using the “find references” feature.
With the new syntax,
it’s easier for tools to identify it as a method call.
The above code also allocates an unnecessary block object,
which is avoided in the <code class="language-plaintext highlighter-rouge">it</code> version.</p>

<ul>
  <li><a href="https://bugs.ruby-lang.org/issues/18980">Feature #18980: <code class="language-plaintext highlighter-rouge">it</code> as a default block parameter</a></li>
</ul>

<h2 id="-chilled-strings">🥶 Chilled strings</h2>

<p>Ruby 2.3 introduced the concept of frozen string literals,
with the <code class="language-plaintext highlighter-rouge">frozen_string_literal: true</code> comment,
which would make any string literal immutable by default.
However, it hasn’t been possible
to make this the default behavior
due to compatibility concerns with many libraries.</p>

<p>Ruby 3.4 takes another step towards immutable strings
by introducing the concept of chilled strings.
Now files without the <code class="language-plaintext highlighter-rouge">frozen_string_literal</code> comment
will treat string literals as “chilled”,
which means they can still be mutated,
but it will emit a warning on mutation.</p>

<p>You can use the <code class="language-plaintext highlighter-rouge">-W:deprecated</code> flag
to see which strings are being mutated.
If you see a string being mutated,
make sure to explicitly mark it as mutable
by either calling <code class="language-plaintext highlighter-rouge">dup</code>
or using the unary <code class="language-plaintext highlighter-rouge">+</code> operator.</p>

<p>Let’s take an example:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">s</span> <span class="o">=</span> <span class="s2">"foo"</span>
<span class="n">s</span> <span class="o">&lt;&lt;</span> <span class="s2">"bar"</span>
<span class="c1"># foo.rb:2: warning: literal string will be frozen in the future </span>
<span class="c1"># (run with --debug-frozen-string-literal for more information)</span>
</code></pre></div></div>

<p>Now if we run with <code class="language-plaintext highlighter-rouge">--debug-frozen-string-literal</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ruby <span class="nt">-W</span>:deprecated <span class="nt">--debug-frozen-string-literal</span> foo.rb

<span class="c"># foo.rb:2: warning: literal string will be frozen in the future</span>
<span class="c"># foo.rb:1: info: the string was created here</span>
</code></pre></div></div>

<p>To fix this, you can use
either <code class="language-plaintext highlighter-rouge">s = "foo".dup</code>
or use the unary <code class="language-plaintext highlighter-rouge">+</code> operator
(<code class="language-plaintext highlighter-rouge">s = +"foo"</code>).</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Works fine</span>
<span class="n">s</span> <span class="o">=</span> <span class="s2">"foo"</span><span class="p">.</span><span class="nf">dup</span>
<span class="n">s</span> <span class="o">&lt;&lt;</span> <span class="s2">"bar"</span> <span class="c1"># no warning</span>

<span class="c1"># Also works fine</span>
<span class="n">s</span> <span class="o">=</span> <span class="o">+</span><span class="s2">"foo"</span>
<span class="n">s</span> <span class="o">&lt;&lt;</span> <span class="s2">"bar"</span> <span class="c1"># no warning</span>
</code></pre></div></div>

<p><em>(Note:
<code class="language-plaintext highlighter-rouge">String.new</code> also creates mutable strings,
but as <a href="https://www.reddit.com/r/ruby/comments/1hgtwhy/comment/m2nxkbo/">Ufuk points out</a>,
it has some rough edges compared to the above options.)</em></p>

<ul>
  <li><a href="https://bugs.ruby-lang.org/issues/19334">Feature #19334: Warn on modified future frozen string literal</a></li>
</ul>

<h2 id="--prism-is-the-new-default-parser">🌈  Prism is the new default parser</h2>

<p>Ruby 3.3 introduced a new parser for Ruby called <a href="https://github.com/ruby/prism">Prism</a>.
It is designed to be
more error tolerant, and easier to maintain.
With 3.4, it will ship as the default parser for Ruby,
replacing the <code class="language-plaintext highlighter-rouge">lrama</code> parser generator,
which generates the parser
from the 16kLOC long
<a href="https://github.com/ruby/ruby/blob/9715131c32fa9753da6a616c9ad3891e27bcff5b/parse.y"><code class="language-plaintext highlighter-rouge">parse.y</code></a> file,
that is hard to maintain.</p>

<p>Prism is already the default
for many other Ruby implementations
(JRuby, TruffleRuby, Natalie, Opal)
and community tools (Rubocop, RBI, Ruby LSP etc).
Having it become the single source of language grammar
makes it easier for tooling to keep up
with changes in Ruby.</p>

<ul>
  <li><a href="https://bugs.ruby-lang.org/issues/20564">Feature #20564: Switch default parser to Prism</a></li>
</ul>

<h2 id="️-modular-gc">🗑️ Modular GC</h2>

<p>Ruby’s garbage collection
has been made more modular,
so alternative garbage collectors
can be dynamically loaded at runtime.
As part of this,
an experimental GC based on <a href="https://www.mmtk.io/">MMTk</a>
has been merged into Ruby.</p>

<p>This will allow more experimentation
with modern high performance GCs,
and make it easier
to compare different GC implementations in production.</p>

<ul>
  <li><a href="https://bugs.ruby-lang.org/issues/20470">Feature #20470: Extract Ruby’s Garbage Collector</a></li>
</ul>

<h2 id="-more-yjit-improvements">🚀 More YJIT improvements</h2>

<p>Ever since YJIT became Ruby’s default JIT compiler in Ruby 3.1,
we have seen incredible performance jumps every year.
This year is no different.
On <a href="https://speed.yjit.org/">YJIT benchmarks</a>,
YJIT is a whopping 92% faster
compared to runnning Ruby without JIT.</p>

<p>Railsbench shows 95% speedup,
but real world apps
are usually more constrained
by IO than benchmarks.
However, many real world Rails apps
have shown 15-20% speedups with YJIT in Ruby 3.3,
so expect speedups in a similar ballpark.
(Rails also enables YJIT by default now.)</p>

<p>An interesting development with YJIT
is that more of the core library methods
(such as <a href="https://bugs.ruby-lang.org/issues/20182"><code class="language-plaintext highlighter-rouge">Array#each</code></a>)
can now be rewritten in Ruby instead of C
and can take advantage of YJIT’s optimizations.
Aaron Patterson’s excellent article,
<a href="https://railsatscale.com/2023-08-29-ruby-outperforms-c/">Ruby Outperforms C</a>,
goes in depth into why this makes things faster.</p>

<h2 id="-monkeypatch-warnings">🙈 Monkeypatch warnings</h2>

<p>Ruby’s flexibility means that
you can even monkeypatch core classes
if you wanted to.
However, many of these methods
have special handling in the interpreter and JIT,
and redefining them can negatively affect performance.
With 3.4, redefining such methods
will cause a performance warning.
The feature request has a list
of the methods that shouldn’t be redefined:</p>

<ul>
  <li><a href="https://bugs.ruby-lang.org/issues/20429">Feature #20429: Emit a performance warning when specially optimized core methods are redefined</a></li>
</ul>

<h2 id="-ruby-toplevel-module-is-reserved">💎 Ruby toplevel module is reserved</h2>

<p>The <code class="language-plaintext highlighter-rouge">Ruby</code> namespace is now reserved,
which means that if you define a class or module called <code class="language-plaintext highlighter-rouge">Ruby</code>,
you will see a warning.
With 3.5.0, a new <code class="language-plaintext highlighter-rouge">Ruby</code> module will be introduced,
but for now there’s nothing in that namespace.
I expect we’ll eventually see things like <code class="language-plaintext highlighter-rouge">Ruby::VERSION</code>
exposed through the module
instead of top level constants like <code class="language-plaintext highlighter-rouge">RUBY_VERSION</code>.</p>

<ul>
  <li><a href="https://bugs.ruby-lang.org/issues/20884">Feature #20884: reserve “Ruby” toplevel module for Ruby language</a></li>
</ul>

<h2 id="-further-reading">📚 Further reading</h2>

<p>These “what’s new in Ruby” posts
highlight changes that I personally found most interesting,
and skip over features I might not use.
If you’re looking for a more comprehensive look at the release,
I highly recommend looking at the
<a href="https://www.ruby-lang.org/en/news/2024/12/12/ruby-3-4-0-rc1-released/">release announcement</a>,
and <a href="https://github.com/ruby/ruby/blob/v3_4_0_rc1/NEWS.md">changelog</a>.
The <a href="https://rubyreferences.github.io/rubychanges/3.4.html">Ruby References</a> website
is another fantastic resource
if you want a deeper dive
into all of the changes in this version.</p>

<div class="note">
  <p>
    This article is part of the <em>What's New in Ruby</em> series.
    To read about a different version of Ruby,
    pick the version here:
  </p>

  <p>
    <a href="/posts/ruby-2-3-features/">2.3</a>
    &nbsp;&nbsp;
    <a href="/posts/ruby-2-4-features/">2.4</a>
    &nbsp;&nbsp;
    <a href="/posts/ruby-2-5-features/">2.5</a>
    &nbsp;&nbsp;
    <a href="/posts/ruby-2-6/">2.6</a>
    &nbsp;&nbsp;
    <a href="/posts/ruby-2-7/">2.7</a>
    &nbsp;&nbsp;
    <a href="/posts/ruby-3-0/">3.0</a>
    &nbsp;&nbsp;
    <a href="/posts/ruby-3-1/">3.1</a>
    &nbsp;&nbsp;
    <a href="/posts/ruby-3-2/">3.2</a>
    &nbsp;&nbsp;
    <a href="/posts/ruby-3-3/">3.3</a>
    &nbsp;&nbsp;
    <a href="/posts/ruby-3-4/">3.4</a>
    &nbsp;&nbsp;
    <a href="/posts/ruby-4-0/">4.0</a>
  </p>
</div>]]></content><author><name></name></author><category term="ruby" /><summary type="html"><![CDATA[A new version of Ruby is released every year on Christmas day. The 3.4 release will be out next week, so I’ve been playing around with the release candidate. Here are some of the features I found interesting.]]></summary></entry><entry><title type="html">Contributing to Ruby docs</title><link href="https://nithinbekal.com/posts/contributing-ruby-docs/" rel="alternate" type="text/html" title="Contributing to Ruby docs" /><published>2024-10-08T00:00:00+00:00</published><updated>2024-10-08T00:00:00+00:00</updated><id>https://nithinbekal.com/posts/contributing-ruby-docs</id><content type="html" xml:base="https://nithinbekal.com/posts/contributing-ruby-docs/"><![CDATA[<p>Last week,
I came across a few small improvements
that I could make to the Ruby docs.
In the past,
I’ve found the idea of contributing to the Ruby repo quite daunting,
but I found that it’s actually pretty straightforward.</p>

<p>I made some notes about the steps
to get things set up locally,
and I’m sharing these here
in the hope that
I can convince someone else
how easy it is to contribute!</p>

<h2 id="getting-set-up-to-make-changes-to-docs">Getting set up to make changes to docs</h2>

<p>First, I forked the ruby repo,
and cloned my fork:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone git@github.com:nithinbekal/ruby.git
</code></pre></div></div>

<p>Before I could run the configure scripts,
I had to install <code class="language-plaintext highlighter-rouge">autoconf</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew <span class="nb">install </span>autoconf
</code></pre></div></div>

<p>Next, you generate the configure script:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./autogen.sh
</code></pre></div></div>

<p>And then run the configure script:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./configure
</code></pre></div></div>

<p>We need a build directory
where we can generate the documentation site:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">mkdir </span>build <span class="o">&amp;&amp;</span> <span class="nb">cd </span>build
</code></pre></div></div>

<p>And finally,
we can generate the HTML docs.
The first run of this command will take some time,
but subsequent runs should be faster.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make html
</code></pre></div></div>

<p>This will display a link to the generated documentation.
We can now make changes to the inline RDoc docs
for methods and classes,
and run the <code class="language-plaintext highlighter-rouge">make html</code> command
to update the generated docs
and verify that everything renders correctly.</p>

<h2 id="tips">Tips</h2>

<ul>
  <li>To make changes to the style of the docs,
you’ll need to update the theme in <a href="https://github.com/ruby/rdoc">ruby/rdoc</a>
which gets synced with the main repo.</li>
  <li>To update the <a href="https://www.ruby-lang.org/en/">Ruby language website</a>,
you’ll need to fork <a href="https://github.com/ruby/www.ruby-lang.org">ruby/www.ruby-lang.org</a>.</li>
  <li>Now that Ruby has extracted some gems
out of the stdlib,
some of the docs need to be updated
in the gems’ own repo,
even though ruby/ruby also contains that code.
    <ul>
      <li>This happened when I tried
<a href="https://github.com/ruby/benchmark/pull/25">updating the docs for <code class="language-plaintext highlighter-rouge">Benchmark.realtime</code></a>
in the <a href="https://github.com/ruby/ruby">ruby/ruby</a> repo.
Turns out I needed to update
the docs in the <a href="https://github.com/ruby/benchmark">ruby/benchmark</a> repo,
which periodically gets synced into the main repo.</li>
    </ul>
  </li>
</ul>

<h2 id="wrapping-up">Wrapping up</h2>

<p>I recently came across the article,
<a href="https://walnut356.github.io/posts/language-documentation/">Why is language documentation still so terrible?</a>,
which is what prompted me to look for ways to improve Ruby docs.
I was blown away by how quickly
the core team reviewed and merges my PRs.
I hope this little how-to
helps you contribute to the docs too!</p>]]></content><author><name></name></author><category term="ruby" /><summary type="html"><![CDATA[Last week, I came across a few small improvements that I could make to the Ruby docs. In the past, I’ve found the idea of contributing to the Ruby repo quite daunting, but I found that it’s actually pretty straightforward.]]></summary></entry><entry><title type="html">Rails: Benchmark.ms deprecated</title><link href="https://nithinbekal.com/posts/rails-benchmark-ms-deprecated/" rel="alternate" type="text/html" title="Rails: Benchmark.ms deprecated" /><published>2024-09-26T00:00:00+00:00</published><updated>2024-09-26T00:00:00+00:00</updated><id>https://nithinbekal.com/posts/rails-benchmark-ms-deprecated</id><content type="html" xml:base="https://nithinbekal.com/posts/rails-benchmark-ms-deprecated/"><![CDATA[<p>Today I stumbled upon
<a href="https://github.com/rails/rails/pull/52746">this PR</a>
which deprecates the <code class="language-plaintext highlighter-rouge">Benchmark.ms</code> monkeypatch in Rails,
without a replacement.
It’s a handy method for instrumenting
different parts of the code.
Here’s how you might use it:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">time_in_ms</span> <span class="o">=</span> <span class="no">Benchmark</span><span class="p">.</span><span class="nf">ms</span> <span class="p">{</span> <span class="nb">sleep</span> <span class="mf">0.5</span> <span class="p">}</span>
<span class="c1">#=&gt; 501.8</span>
</code></pre></div></div>

<p>This will no longer work in Rails 8.1
(8.0 beta was just released today,
so it will still work for some time).
However, if you use it,
you will see this deprecation warning in the logs:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>`Benchmark.ms` is deprecated and will be removed in Rails 8.1 without replacement.
</code></pre></div></div>

<p>But if you look at the original implementation,
all it does is call <code class="language-plaintext highlighter-rouge">Benchmark.realtime</code>,
and multiply the time in seconds by <code class="language-plaintext highlighter-rouge">1000</code>.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">ms</span><span class="p">(</span><span class="o">&amp;</span><span class="n">blk</span><span class="p">)</span>
  <span class="mi">1000</span> <span class="o">*</span> <span class="n">realtime</span><span class="p">(</span><span class="o">&amp;</span><span class="n">blk</span><span class="p">)</span>
<span class="k">end</span>
</code></pre></div></div>

<p>I think the easiest way to replace it
is to manually multiply by <code class="language-plaintext highlighter-rouge">1000</code>
when we need the time in ms.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">t</span> <span class="o">=</span> <span class="no">Benchmark</span><span class="p">.</span><span class="nf">realtime</span> <span class="p">{</span> <span class="nb">sleep</span> <span class="mf">0.5</span> <span class="p">}</span> <span class="c1">#=&gt; 0.5018...</span>
<span class="n">time_in_ms</span> <span class="o">=</span> <span class="n">t</span> <span class="o">*</span> <span class="mi">1000</span>                <span class="c1">#=&gt; 501.8...</span>
</code></pre></div></div>

<p>Another alternative is to use <code class="language-plaintext highlighter-rouge">ActiveSupport::Benchmark</code>
which provides a more flexible version of <code class="language-plaintext highlighter-rouge">realtime</code>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">time_in_ms</span> <span class="o">=</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">Benchmark</span><span class="p">.</span><span class="nf">realtime</span><span class="p">(</span><span class="ss">:float_millisecond</span><span class="p">)</span> <span class="p">{</span> <span class="nb">sleep</span> <span class="mf">0.5</span> <span class="p">}</span>
<span class="c1">#=&gt; 501.8</span>
</code></pre></div></div>

<p>I think I prefer using the first version,
because the calculation is simple enough,
there’s no dependency on <code class="language-plaintext highlighter-rouge">ActiveSupport</code>,
and it’s less verbose!</p>]]></content><author><name></name></author><category term="rails" /><category term="ruby" /><summary type="html"><![CDATA[Today I stumbled upon this PR which deprecates the Benchmark.ms monkeypatch in Rails, without a replacement. It’s a handy method for instrumenting different parts of the code. Here’s how you might use it:]]></summary></entry><entry><title type="html">Script to bump Ruby version in Rails app</title><link href="https://nithinbekal.com/posts/bump-ruby-script/" rel="alternate" type="text/html" title="Script to bump Ruby version in Rails app" /><published>2024-07-03T00:00:00+00:00</published><updated>2024-07-03T00:00:00+00:00</updated><id>https://nithinbekal.com/posts/bump-ruby-script</id><content type="html" xml:base="https://nithinbekal.com/posts/bump-ruby-script/"><![CDATA[<p>Any time I need to bump a Ruby version in a Rails repo,
I need to find which files
have hardcoded references to the version.
The 3 files that usually have hardcoded versions are
<code class="language-plaintext highlighter-rouge">Dockerfile</code>, <code class="language-plaintext highlighter-rouge">Gemfile</code> and <code class="language-plaintext highlighter-rouge">.tool-versions</code>.
I wrote a script to automate
Ruby version bumps by putting this
in <code class="language-plaintext highlighter-rouge">bin/bumpruby</code>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env ruby</span>

<span class="k">unless</span> <span class="p">(</span><span class="n">new_version</span> <span class="o">=</span> <span class="no">ARGV</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
  <span class="nb">puts</span> <span class="s2">"💥 Usage: bin/bumpruby &lt;version&gt;"</span>
  <span class="k">return</span>
<span class="k">end</span>

<span class="nb">system</span><span class="p">(</span><span class="s2">"asdf install ruby </span><span class="si">#{</span><span class="n">new_version</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="nb">system</span><span class="p">(</span><span class="s2">"asdf shell ruby </span><span class="si">#{</span><span class="n">new_version</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="nb">puts</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">⭐️ Switched to ruby </span><span class="si">#{</span><span class="n">new_version</span><span class="si">}</span><span class="s2">"</span>

<span class="k">def</span> <span class="nf">bump_ruby_version</span><span class="p">(</span><span class="n">file_name</span><span class="p">,</span> <span class="n">pattern</span><span class="p">,</span> <span class="n">replacement</span><span class="p">)</span>
  <span class="n">content</span> <span class="o">=</span> <span class="no">File</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="n">file_name</span><span class="p">)</span>
  <span class="n">new_content</span> <span class="o">=</span> <span class="n">content</span><span class="p">.</span><span class="nf">gsub</span><span class="p">(</span><span class="n">pattern</span><span class="p">,</span> <span class="n">replacement</span><span class="p">)</span>
  <span class="no">File</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">file_name</span><span class="p">,</span> <span class="n">new_content</span><span class="p">)</span>
  <span class="nb">puts</span> <span class="s2">"⭐️ Replaced version in </span><span class="si">#{</span><span class="n">file_name</span><span class="si">}</span><span class="s2">"</span>
<span class="k">end</span>

<span class="n">bump_ruby_version</span><span class="p">(</span><span class="s2">".tool-versions"</span><span class="p">,</span> <span class="sr">/ruby \d+\.\d+\.\d+/</span><span class="p">,</span> <span class="s2">"ruby </span><span class="si">#{</span><span class="n">new_version</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="n">bump_ruby_version</span><span class="p">(</span><span class="s2">"Dockerfile"</span><span class="p">,</span> <span class="sr">/ARG RUBY_VERSION=\d+\.\d+\.\d+/</span><span class="p">,</span> <span class="s2">"ARG RUBY_VERSION=</span><span class="si">#{</span><span class="n">new_version</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="n">bump_ruby_version</span><span class="p">(</span><span class="s2">"Gemfile"</span><span class="p">,</span> <span class="sr">/ruby "\d+\.\d+\.\d+"/</span><span class="p">,</span> <span class="s2">"ruby </span><span class="se">\"</span><span class="si">#{</span><span class="n">new_version</span><span class="si">}</span><span class="se">\"</span><span class="s2">"</span><span class="p">)</span>

<span class="nb">puts</span> <span class="s2">"⭐️ Bundle install"</span>
<span class="nb">system</span><span class="p">(</span><span class="s2">"bundle install"</span><span class="p">)</span>

<span class="nb">puts</span> <span class="s2">"⭐️ Running tests"</span>
<span class="nb">system</span><span class="p">(</span><span class="s2">"bin/rails test"</span><span class="p">)</span>
</code></pre></div></div>

<p>Now we can use it like this
to bump to Ruby 3.3.3:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bin/bumpruby 3.3.3
</code></pre></div></div>

<p>Usually when minor Ruby versions are released,
it’s because there’s an important bug or vulnerability fix,
so it makes sense to upgrade right away.
I have a couple of toy apps
deployed to fly.io
that I don’t touch very often.
Having a command to do that
means I’m less likely to procrastinate
when it comes to version bumps.</p>]]></content><author><name></name></author><category term="ruby" /><category term="rails" /><summary type="html"><![CDATA[Any time I need to bump a Ruby version in a Rails repo, I need to find which files have hardcoded references to the version. The 3 files that usually have hardcoded versions are Dockerfile, Gemfile and .tool-versions. I wrote a script to automate Ruby version bumps by putting this in bin/bumpruby:]]></summary></entry><entry><title type="html">Abstract methods and NotImplementedError in Ruby</title><link href="https://nithinbekal.com/posts/abstract-methods-notimplementederror-ruby/" rel="alternate" type="text/html" title="Abstract methods and NotImplementedError in Ruby" /><published>2024-04-09T00:00:00+00:00</published><updated>2024-04-09T00:00:00+00:00</updated><id>https://nithinbekal.com/posts/abstract-methods-notimplementederror-ruby</id><content type="html" xml:base="https://nithinbekal.com/posts/abstract-methods-notimplementederror-ruby/"><![CDATA[<p>Ruby’s <code class="language-plaintext highlighter-rouge">NotImplementedError</code> exception is often used
as a placeholder in abstract classes
for methods that should be implemented by subclasses.
But did you know that
this is not how this exception class
was intended to be used?</p>

<h2 id="how-is-it-commonly-misused">How is it commonly (mis)used?</h2>

<p>Let’s start with an example
of a common usage pattern of <code class="language-plaintext highlighter-rouge">NotImplementedError</code>.
We have a <code class="language-plaintext highlighter-rouge">BaseSetting</code> class,
and we want to convey that
anyone subclassing this
should implement a <code class="language-plaintext highlighter-rouge">to_html</code> method.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">BaseSetting</span>
  <span class="k">def</span> <span class="nf">to_html</span>
    <span class="k">raise</span> <span class="no">NotImplementedError</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="k">class</span> <span class="nc">Text</span> <span class="o">&lt;</span> <span class="no">BaseSetting</span>
  <span class="k">def</span> <span class="nf">to_html</span>
    <span class="s2">"&lt;input type='text'&gt;"</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="k">class</span> <span class="nc">Number</span> <span class="o">&lt;</span> <span class="no">BaseSetting</span>
  <span class="c1"># Forgot to implement to_html</span>
<span class="k">end</span>

<span class="no">TextSetting</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">to_html</span>   <span class="c1">#=&gt; "&lt;input type='text'&gt;"</span>
<span class="no">NumberSetting</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">to_html</span> <span class="c1">#=&gt; 💥 NotImplementedError</span>
</code></pre></div></div>

<p>In the above case,
the <code class="language-plaintext highlighter-rouge">Text</code> setting class
already implements <code class="language-plaintext highlighter-rouge">to_html</code>,
If we’re introducing
a new <code class="language-plaintext highlighter-rouge">Number</code> setting class,
we’re letting ourselves know
to implement the <code class="language-plaintext highlighter-rouge">to_html</code>
by raising the exception at runtime.
You should only see this exception
when you test the feature locally,
or run your automated tests,
and should never reach production.</p>

<h2 id="what-is-notimplementederror-actually-meant-for">What is <code class="language-plaintext highlighter-rouge">NotImplementedError</code> actually meant for?</h2>

<p><code class="language-plaintext highlighter-rouge">NotImplementedError</code> is raised
when a feature isn’t available
on the current platform.
An example of this is <code class="language-plaintext highlighter-rouge">Process.fork</code>.
JRuby doesn’t support <code class="language-plaintext highlighter-rouge">fork</code>,
so calling that method
will raise this exception.
Here’s what the docs have to say about it:</p>

<blockquote>
  <p>Raised when a feature is not implemented on the current platform.
For example, methods depending on the fsync or fork system calls
may raise this exception
if the underlying operating system or Ruby runtime does not support them.</p>
</blockquote>

<h2 id="another-reason-to-avoid-it">Another reason to avoid it</h2>

<p>If the semantics of this exception
haven’t convinced you to avoid it,
here’s another example.
The <code class="language-plaintext highlighter-rouge">convert_to_html</code> method
tries calling <code class="language-plaintext highlighter-rouge">to_html</code>
but provides a fallback in a rescue block.
What do you think happens
when we call this with a number setting?</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">convert_to_html</span><span class="p">(</span><span class="n">setting</span><span class="p">)</span>
  <span class="n">setting</span><span class="p">.</span><span class="nf">to_html</span>
<span class="k">rescue</span> <span class="o">=&gt;</span> <span class="n">e</span>
  <span class="c1"># do_some_exception_logging(e)</span>
  <span class="s2">"&lt;input type='</span><span class="si">#{</span><span class="n">setting</span><span class="p">.</span><span class="nf">class</span><span class="p">.</span><span class="nf">name</span><span class="p">.</span><span class="nf">downcase</span><span class="si">}</span><span class="s2">'&gt;"</span>
<span class="k">end</span>

<span class="n">convert_to_html</span><span class="p">(</span><span class="no">Number</span><span class="p">.</span><span class="nf">new</span><span class="p">)</span>
</code></pre></div></div>

<p>If you run the above code,
you will still see that
<code class="language-plaintext highlighter-rouge">NotImplementedError</code> will be raised.
This is because <code class="language-plaintext highlighter-rouge">rescue =&gt; e</code>
assumes that you are rescuing
an exception that inherits from <code class="language-plaintext highlighter-rouge">StandardError</code>.
<code class="language-plaintext highlighter-rouge">NotImplementedError</code> inherits from <code class="language-plaintext highlighter-rouge">ScriptError</code>,
which in turn inherits from <code class="language-plaintext highlighter-rouge">Exception</code>.</p>

<p><img src="https://s3.amazonaws.com/nithinbekal.com/blog/ruby-notimplementederror/not-implemented-error-inheritance.png" alt="Ruby's exception classes" /></p>

<p>Since <code class="language-plaintext highlighter-rouge">StandardError</code> is not in this inheritance chain,
the rescue doen’t handle  this exception.
The only way to rescue this is
to specify one of
<code class="language-plaintext highlighter-rouge">NotImplementedError</code>,
<code class="language-plaintext highlighter-rouge">ScriptError</code> or <code class="language-plaintext highlighter-rouge">Exception</code>
like this:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">rescue</span> <span class="no">NotImplementedError</span> <span class="o">=&gt;</span> <span class="n">e</span>
</code></pre></div></div>

<p>Exceptions raised by abstract methods
aren’t really meant to be rescued like this,
so it’s not a huge problem.
However, it is important to understand
Ruby’s exception handling behavior
when choosing which exception class to use.</p>

<h2 id="pythons-notimplementederror">Python’s <code class="language-plaintext highlighter-rouge">NotImplementedError</code></h2>

<p>If this is not how it is meant to be used,
why is this so widely used?
One explanation could be that
<a href="https://docs.python.org/3/library/exceptions.html">python has an exception of the same name</a>,
which is actually intended for abstract methods:</p>

<blockquote>
  <p>This exception is derived from RuntimeError.
In user defined base classes,
abstract methods should raise this exception
when they require derived classes to override the method,
or while the class is being developed to indicate
that the real implementation still needs to be added.</p>
</blockquote>

<h2 id="alternative-approaches">Alternative approaches</h2>

<p>There are quite a few different patterns
that you could follow,
if you wanted to avoid raising <code class="language-plaintext highlighter-rouge">NotImplementedError</code>.
I’ll list a few common patterns
that come to mind.</p>

<h3 id="1-provide-a-default-implementation">1. Provide a default implementation</h3>

<p>When possible,
provide a default implementation
in the base class
instead of raising an exception.
This eliminates the risk
breaking things in production
because we accidentally missed an implementation.
For instance,
we could replace
the <code class="language-plaintext highlighter-rouge">BaseSetting#to_html</code> method like this:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">BaseSetting</span>
  <span class="k">def</span> <span class="nf">to_html</span>
    <span class="s2">"&lt;input type='</span><span class="si">#{</span><span class="nb">self</span><span class="p">.</span><span class="nf">class</span><span class="p">.</span><span class="nf">name</span><span class="p">.</span><span class="nf">downcase</span><span class="si">}</span><span class="s2">'&gt;"</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>An example of this approach
is the way activerecord
infers table names from model names.
If the activerecord model is called <code class="language-plaintext highlighter-rouge">Post</code>,
it assumes that the table is called <code class="language-plaintext highlighter-rouge">posts</code>,
but also lets you override this
by explicitly setting the table name like this:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Post</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
  <span class="nb">self</span><span class="p">.</span><span class="nf">table_name</span> <span class="o">=</span> <span class="s1">'articles'</span>
<span class="k">end</span>
</code></pre></div></div>

<p>This way, you only need an implementation
when you’re deviating
from a convention that you’ve established.</p>

<h3 id="2-explicitly-raise-with-a-message">2. Explicitly raise with a message</h3>

<p>Providing a default implementation
might not be practical
in more complex scenarios.
If you need an abstract method
to raises an exception,
the simplest option
is to raise with a clear error message
that tells you what you need to do to fix it.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Base</span>
  <span class="k">def</span> <span class="nf">foo</span>
    <span class="k">raise</span> <span class="s2">"</span><span class="si">#{</span><span class="nb">self</span><span class="p">.</span><span class="nf">class</span><span class="si">}</span><span class="s2"> must implement the method </span><span class="si">#{</span><span class="n">__method__</span><span class="si">}</span><span class="s2">"</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>If the intention of the exception
is to remind us to implement a method,
there’s no better way than to
tell us the exact steps to do this.</p>

<h3 id="3-create-a-custom-exception">3. Create a custom exception</h3>

<p>Another simple alternative
is to define your own exception
that inherits from <code class="language-plaintext highlighter-rouge">StandardError</code>
and raise that instead.
This approach was favored when the
<a href="https://github.com/rmosolgo/graphql-ruby/issues/2067">graphql-ruby gem</a>
moved away from <code class="language-plaintext highlighter-rouge">NotImplementedError</code>.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">BaseSetting</span>
  <span class="k">class</span> <span class="nc">MethodNotImplemented</span> <span class="o">&lt;</span> <span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span>

  <span class="k">def</span> <span class="nf">to_html</span>
    <span class="k">raise</span> <span class="no">MethodNotImplemented</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p><strong>Update</strong>:
<em><a href="https://ruby.social/@citizen428@chaos.social/112239323523258221">Michael Kohl has an interesting suggestion</a>
for naming this exception:</em></p>

<blockquote>
  <p>Smalltalk had the idiom of implementing abstract methods with the body <code class="language-plaintext highlighter-rouge">self subclassResponsibility</code> which raises an
error. So I generally use <code class="language-plaintext highlighter-rouge">SubclassResponsibility</code> as my exception name for abstract methods.</p>
</blockquote>

<h3 id="4-raise-nomethoderror">4. Raise <code class="language-plaintext highlighter-rouge">NoMethodError</code></h3>

<p>Another alternative is
to raise <code class="language-plaintext highlighter-rouge">NoMethodError</code> instead.
This class inherits from <code class="language-plaintext highlighter-rouge">StandardError</code>,
so rescue clauses will also work as expected.
Saying that a class doesn’t respond to the method
is closer to what we’re trying to convey anyway.
This is the approach preferred in the
<a href="https://github.com/hanami/controller/pull/377">Hanami framework</a>.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">BaseSetting</span>
  <span class="k">def</span> <span class="nf">to_html</span>
    <span class="k">raise</span> <span class="no">NoMethodError</span><span class="p">,</span> <span class="s2">"You must implement </span><span class="si">#{</span><span class="nb">self</span><span class="p">.</span><span class="nf">class</span><span class="si">}</span><span class="s2">#to_html"</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h3 id="5-write-tests">5. Write tests</h3>

<p>In many cases
where you have a base class
and multiple implementations,
you might need to maintain
a list of subclasses.
For instance,
take the following case
where we have a <code class="language-plaintext highlighter-rouge">Setting</code> base class,
and a factory method
that needs to know
which implementation to instantiate:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Setting</span>
  <span class="no">SETTING_CLASSES</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s2">"text"</span> <span class="o">=&gt;</span> <span class="no">TextSetting</span><span class="p">,</span>
    <span class="s2">"number"</span> <span class="o">=&gt;</span> <span class="no">NumberSetting</span><span class="p">,</span>
  <span class="p">}</span>

  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">for</span><span class="p">(</span><span class="n">type</span><span class="p">)</span>
    <span class="no">SETTING_CLASSES</span><span class="p">[</span><span class="n">type</span><span class="p">].</span><span class="nf">new</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Since we already have a list of subclasses,
it’s easier to omit the abstract method,
and instead write a test
that ensures that each of them
responds to a the method.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">test</span> <span class="s2">"all setting types respond to to_html"</span> <span class="k">do</span>
  <span class="no">Setting</span><span class="o">::</span><span class="no">SETTING_CLASSES</span><span class="p">.</span><span class="nf">each_value</span> <span class="k">do</span> <span class="o">|</span><span class="n">setting_class</span><span class="o">|</span>
    <span class="n">assert</span> <span class="n">setting_class</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">respond_to?</span><span class="p">(</span><span class="ss">:to_html</span><span class="p">),</span>
      <span class="s2">"</span><span class="si">#{</span><span class="n">setting_class</span><span class="si">}</span><span class="s2"> should implement to_html method."</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h3 id="6-sorbet">6. Sorbet</h3>

<p>Not everyone in the Ruby community
is convinced by static type checkers,
but for more complex codebases,
Sorbet is a fantastic choice.
Here’s what the above base class will look like
if we added sorbet signatures:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Base</span>
  <span class="kp">extend</span> <span class="no">T</span><span class="o">::</span><span class="no">Helpers</span>

  <span class="n">abstract!</span>

  <span class="n">sig</span> <span class="p">{</span> <span class="n">abstract</span><span class="p">.</span><span class="nf">returns</span><span class="p">(</span><span class="no">String</span><span class="p">)</span> <span class="p">}</span>
  <span class="k">def</span> <span class="nf">foo</span><span class="p">;</span> <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>With the above setup in place,
running the typechecker
using <code class="language-plaintext highlighter-rouge">srb typecheck</code>
will tell you which subclasses
need to implement
which abstract methods.</p>

<p>Sorbet is gradually typed,
so if you’re not a fan of type signatures,
you can just add signatures
for these kinds of classes
without changing anything else.
It is also extremely fast,
so you can get the feedback
in a couple of seconds.
With its excellent editor integrations,
you won’t even need to run the command manually
to see the typechecker results.</p>

<p><strong>Update</strong>:
<em>Ironically,
Sorbet uses <code class="language-plaintext highlighter-rouge">NotImplementedError</code> internally
(<a href="https://github.com/sorbet/sorbet/blob/8f772843bfefecc59cfd2bb5ff5b565d6b048a6f/gems/sorbet-runtime/lib/types/private/methods/_methods.rb?#L272">see here</a>)
when you define abstract methods!
Thanks <a href="https://ufuk.dev/">Ufuk</a>
for pointing this out!</em></p>

<h2 id="closing-thoughts">Closing thoughts</h2>

<p>Although we’ve looked at a few altenatives
to raising <code class="language-plaintext highlighter-rouge">NotImplementedError</code>,
I want to note that
if raising this exception
is working well for your codebase,
there’s no need to go back
and replace everything.</p>

<p>Most people know what to do
when they encounter this exception,
so maybe it’s fine to use the pattern
that everyone is familiar with.
However, if you want to change their minds,
you can always send them
a link to this post. ;)</p>

<p><strong>Update</strong>:
<em>After writing this post,
I found
<a href="https://bugs.ruby-lang.org/issues/18915">this proposal on the Ruby bug tracker</a>
to introduce a new exception class
to replace <code class="language-plaintext highlighter-rouge">NotImplementedError</code>.</em></p>]]></content><author><name></name></author><category term="ruby" /><summary type="html"><![CDATA[Ruby’s NotImplementedError exception is often used as a placeholder in abstract classes for methods that should be implemented by subclasses. But did you know that this is not how this exception class was intended to be used?]]></summary></entry></feed>