はじめに
Ruby 2.7では、ブロックの仮引数として番号指定パラメータが導入されました。
[1, 2, 3].map { _1 * 10 }
#=> [10, 20, 30]
# 上のコードは以下のコードと同じ
[1, 2, 3].map { |n| n * 10 }
2024年にリリースされる(2023年じゃないよ!)のRuby 3.4では1、_1
の代わりにit
が使えるようになる予定です。
# Ruby 3.4(2024年12月25日リリース予定)
[1, 2, 3].map { it * 10 }
#=> [10, 20, 30]
この前準備として、2023年リリースのRuby 3.3では、ブロックの内部でit
がレシーバも引数もブロックもなしに呼ばれた場合に警告を出すようになります(この警告はRubyの実行オプションによらず常に出力されます2)。
# Ruby 3.3(2023年12月25日リリース予定)
def it = 10
[1, 2, 3].map { it * 10 }
#=> warning: `it` calls without arguments will refer to the first block param in Ruby 3.4; use it() or self.it
#=> [100, 100, 100]
この内容について、Lucian Ghinda氏が "Exploring `it` default block param warning in Ruby 3.3" という記事で詳しく説明していたので、原著者の了承をいただいた上でこの記事の日本語訳をお届けします。
なお、翻訳は日本語の読みやすさを重視して、意訳している部分があります。あらかじめご了承ください。
【翻訳】Ruby 3.3で発生するit
デフォルトブロックパラメータ警告について
Original: Exploring `it` default block param warning in Ruby 3.3 by Lucian Ghinda
先日のRuby開発者ミーティングでit
がRuby 3.4で追加されることが了承されました。
この準備として、Ruby 3.3ではit
がレシーバも引数もブロックもなしに呼ばれた場合に警告を出すようになります。
私はこの内容に関する簡単な動画を作成したので、動画を見たい方はこちらをご覧ください。テキストバージョンを好む方はこのまま読み進めてください。
非推奨警告
この内容を確認するために、シンプルなコードを書いてみましょう。it
という名前のメソッドを定義し、それをブロックの内部で呼び出します。
puts RUBY_VERSION
def it = "works"
def run(&block) = yield
puts run { it }
このコードをRuby 3.2.2で実行すると、以下のような実行結果になります。
3.2.2
works
しかし、このコードをRuby 3.3.0.rc1で実行すると、次のような実行結果になります。
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
この警告の修正方法
もしあなたがit
という名前のメソッドを定義していて、今後もブロック内でレシーバも引数もブロックパラメータもなしに呼び出したい場合は、括弧を追加してください。
-puts run { it }
+puts run { it() }
またはレシーバを追加してください。
-puts run { it }
+puts run { self.it }
it
をローカル変数名として使用した場合
it
をローカル変数の名前として定義した場合は問題ありません。
以下はその例です。
puts RUBY_VERSION
def log(&block) = puts yield
def run
it = 2
log { it }
end
run
Ruby 3.3.0.rc1でこのコードを実行してもエラーは表示されません。出力結果は以下のようになります。
3.3.0
2
ですが、もしこのローカル変数を同じ名前でメソッドとして抽出すると……
puts RUBY_VERSION
def log(&block) = puts yield
+
+def it = 2
+
def run
- it = 2
log { it }
end
run
警告が出力されます。
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ではit
がよく使われます。しかし、RSpecではほとんど(いや、滅多に)問題はおきません。なぜならit
は通常、文字列の引数と一緒に呼ばれるからです。
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
上のコードは警告が 表示されません。
(訳注:文字列を省略して it do ... end
のように書いた場合も警告は表示されません。なぜならブロック付きでit
を呼び出しているからです)
しかし、こんなコードを書くと……
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
Ruby 3.3.0.rc.1では警告が発生します。
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
RSpecではit
のエイリアスが定義されています。この問題を修正するには、以下のエイリアスのいずれかを使用してください。
specify
example
なお、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
まとめ
it
がデフォルトのブロックパラメータとして了承されたことを私は喜んでいます。(来年の)Ruby 3.4を使ってコードを書くのがとても楽しみです。
Ruby 3.3の警告機能は潜在的な名前の衝突を検知するのに役立ちます。とはいえ、この問題はごくまれにしか発生しないでしょう。
大半の開発者はスムーズに移行できるはずです。RSpecのテストは通常、文字列の引数を付けてit
を使うので、警告対象になりません。よって、修正不要です。万一、警告が発生したとしても、it
の代わりにspecify
やexample
を使えば、警告をなくせます。
この記事をお楽しみいただけましたか?
Rubyの最新情報を毎週お届けするニュースレター「Short Ruby News」にご登録ください。Rubyの学習リソースについては、rubyandrails.infoをご覧ください。また、Ruby.social、Linkedin、TwitterでもRubyとRailsについて投稿しています。私のYouTubeチャンネルでは、Rubyに関する短い動画を配信しています。
(翻訳はここまで)
訳者による補足
記事の中では明示的なサンプルコードがありませんでしたが、警告が発生する条件は、ブロックの内部でit
がレシーバも引数もブロックもなしに呼ばれた場合に加えて、「ブロックパラメータがない」という条件も必要です。
たとえば、[1, 2, 3].map { it }
は警告が出ますが、 [1, 2, 3].map { |n| n + it }
だと警告は出ません。
def it = 10
# ブロックパラメータがないので警告が出る
[1, 2, 3].map { it * 10 }
#=> warning: `it` calls without arguments will refer to the first block param in Ruby 3.4; use it() or self.it
#=> [100, 100, 100]
# ブロックパラメータ(ここではn)があるので警告が出ない
[1, 2, 3].map { |n| n + it }
#=> [11, 12, 13]
[1, 2, 3].map { it * 10 }
の場合、Ruby 3.4では暗黙的なブロックパラメータとして it
に1, 2, 3という値が代入されるように仕様が変わりますが、 [1, 2, 3].map { |n| n + it }
ではRuby 3.3でも3.4でも明示的なブロックパラメータである n
に1, 2, 3が代入されます。このため、 [1, 2, 3].map { |n| n + it }
の it
はブロックパラメータとして使われていないことが明らかなので、警告が出ないものと思われます。
-
Rubyは毎年12月25日に新バージョンがリリースされます。このため、Ruby 3.3は2023年12月25日に、Ruby 3.4は2024年12月25日にそれぞれリリースされる予定です。 ↩
-
参考:"The warning should be printed always (even without $VERBOSE)" ↩