Exploring `it` default block param warning in Ruby 3.3

Exploring `it` default block param warning in Ruby 3.3

Understanding the `it` default block parameter warning in Ruby 3.3 RC1

ยท

5 min read

In the last Ruby Developer Meeting it was accepted that it will be added to Ruby 3.4.

In preparation for this, Ruby 3.3 will show a warning when it is used without a receiver, argument, or block.

I made a quick video about this if you want to watch it or if you prefer the written version continue reading on:

Deprecation warning

For this, let's define some simple code where I will name a method it and then call it from a block:

puts RUBY_VERSION

def it = "works"

def run(&block) = yield

puts run { it }

If you run this with Ruby 3.2.2 then you will get the following result:

3.2.2
works

But if you run this with Ruby 3.3.0.rc1 you will get the following result:

example_deprecation_warning.rb:7: warning: `it` calls without 
arguments will refer to the first block param in Ruby 3.4; 
use it() or self.it
3.3.0
works

How to fix the warning

In case you have a method named it and still want to call it inside a block without a receiver, arguments or params you should either add paranthesis:

- puts run { it }
+ puts run { it() }

or add the receiver:

- puts run { it }
+ puts run { self.it }

When using it as local variable name

There will be no problem when you defined it as local variable name.

Here is an example where I created:

puts RUBY_VERSION

def log(&block) = puts yield

def run
  it = 2

  log { it }
end

run

If you run this it on Ruby 3.3.0.rc1 will not display an error. The output will be:

3.3.0
2

Now if you will extract the local variable to a method with the same name:

puts RUBY_VERSION

def log(&block) = puts yield
+
+ def it = 2
+
def run
-  it = 2

  log { it }
end

run

It will show a warning:

it_as_local_variable.rb:8: warning: `it` calls without arguments 
will refer to the first block param in Ruby 3.4; use it() or self.it
3.3.0
2

RSpec

RSpec uses a lot it. But in most cases (I would argue majority of them) this will not be a problem. Because usually it is called with a string argument:

require "rspec"

describe Galaxy do
  context "when created with a name and number of stars" do
    it "should hold the correct name and star count" do
      galaxy = Galaxy.new("Milky Way", 100_000_000_000)
      expect(galaxy.name).to eq("Milky Way")
      expect(galaxy.number_of_stars).to eq(100_000_000_000)
    end
  end
end

The code above will NOT display a warning.

But if you will try to write something like this:

require "rspec"

describe Galaxy do
  subject { Galaxy.new("Milky Way", 100_000_000_000).stars }

  context "When there are no known stars" do
    it
  end
end

If you run this with Ruby 3.3.0.rc.1 you will get the warning:

explore-it/rspec_example/spec/more_galaxy_spec.rb:8: warning: `it` 
calls without arguments will refer to the first block param in 
Ruby 3.4; use it() or self.it
*

Pending: (Failures listed here are expected and do not affect your suite's status)

  1) Galaxy When there are no known stars 
     # Not yet implemented
     # ./spec/more_galaxy_spec.rb:8


Finished in 0.0074 seconds (files took 0.06474 seconds to load)
1 example, 0 failures, 1 pending

You can fix this by using one of the other alias for it from RSpec:

Here is the complete list of methods defined in RSpec core:

# Defines an example within a group.
define_example_method :example
# Defines an example within a group.
# This is the primary API to define a code example.
define_example_method :it
# Defines an example within a group.
# Useful for when your docstring does not read well off of `it`.
# @example
#  RSpec.describe MyClass do
#    specify "#do_something is deprecated" do
#      # ...
#    end
#  end
define_example_method :specify

# Shortcut to define an example with `:focus => true`.
# @see example
define_example_method :focus,    :focus => true
# Shortcut to define an example with `:focus => true`.
# @see example
define_example_method :fexample, :focus => true
# Shortcut to define an example with `:focus => true`.
# @see example
define_example_method :fit,      :focus => true
# Shortcut to define an example with `:focus => true`.
# @see example
define_example_method :fspecify, :focus => true
# Shortcut to define an example with `:skip => 'Temporarily skipped with xexample'`.
# @see example
define_example_method :xexample, :skip => 'Temporarily skipped with xexample'
# Shortcut to define an example with `:skip => 'Temporarily skipped with xit'`.
# @see example
define_example_method :xit,      :skip => 'Temporarily skipped with xit'
# Shortcut to define an example with `:skip => 'Temporarily skipped with xspecify'`.
# @see example
define_example_method :xspecify, :skip => 'Temporarily skipped with xspecify'
# Shortcut to define an example with `:skip => true`
# @see example
define_example_method :skip,     :skip => true
# Shortcut to define an example with `:pending => true`
# @see example
define_example_method :pending,  :pending => true

Conclusion

I really like that it was accepted as a default block parameter. Looking forward to Ruby 3.4 (next year) to write code using it.

The warning feature in Ruby 3.3 serves as a useful heads-up for potential conflicts with its usage, although these are expected to occur infrequently.

The transition should be smooth for most developers. RSpec tests should not require any changes, as it largely uses it with a string argument and is not likely to trigger the warning. In the instances where it does, alternatives like specify and example can be used to fix the warning.


Enjoyed this article?

๐Ÿ‘‰ Join my Short Ruby News newsletter for weekly Ruby updates from the community and visit rubyandrails.info, a directory with learning content about Ruby.

๐Ÿ‘ Subscribe to my Ruby and Ruby on rails courses over email at learn.shortruby.com - effortless learning anytime, anywhere

๐Ÿค Let's connect on Ruby.social or Linkedin or Twitter where I post mainly about Ruby and Rails.

๐ŸŽฅ Follow me on my YouTube channel for short videos about Ruby

Did you find this article valuable?

Support Lucian Ghinda by becoming a sponsor. Any amount is appreciated!