Ruby 3.3で大幅に強化されたIRBの解説(翻訳)
本記事では、Ruby 3.3でIRBに導入された主な機能強化と、現時点で来年に導入が予定されている機能について詳しく解説いたします。
Ruby 3.3のIRB強化の要点:
耳より情報: Ruby 2.7以降のプロジェクトであれば、Gemfileにgem "irb"
を追加することで、Ruby 3.3に今すぐアップグレードしなくてもRuby 3.3と同じバージョンのIRB(v1.11.0
)を利用できます。
🔗 IRBでruby/debugの高度なデバッグ機能をサポート
Ruby 3.2のIRB (v1.6
)のときは、debug
やnext
などのコマンドセットを新たに導入し、binding.irb
のブレークポイントからruby/debug gemのセッションに速やかに移行できるようにしました(混乱を避けるため、本記事では以後debug gemのセッションをrdbg
と表記します)。
これは大きな改善ですが、このままではrdbgセッションに切り替えた途端に複数行入力機能など一部のIRBの機能が使えなくなってしまいます。
irb(main):001:0> debug
(ruby) if true
eval error: (rdbg)/test.rb:1: syntax error, unexpected end-of-input, expecting `then' or ';' or '\n'
if true
^
nil
(rdbg)
今年になってirb:rdbg
セッションを導入したことで、ユーザーエクスペリエンスを新たなレベルに引き上げました。これによって、ユーザーはIRBを終了せずにrdbg
の全コマンドを利用できます。
irb(main):001> debug
irb:rdbg(main):002* if true
irb:rdbg(main):003* puts "Multi-line input and more!"
irb:rdbg(main):004> end
Multi-line input and more!
nil
irb:rdbg(main):005> th
# `th`(全スレッドを表示)は`rdbg`セッションにのみ存在するコマンドの例
--> #0 (sleep)@test.rb:2:in `<main>'
irb:rdbg(main):006>
rdbg
セッションをIRBに緊密に統合することで、以下のような多くのメリットを得られます。
- IRBのコマンドとrdbgのコマンドを両方利用できるようになる
- 複数行を入力可能になる
- オートコンプリートが効くようになる
- コマンドを以下のように記号のエイリアスでも指定可能になる
@
(whereami
と同等)$
(show_source
と同等)
- ページャーのサポート
しかし最も重要なのは、従来のpry-byebug gemと同等レベルのデバッグエクスペリエンスをユーザーに提供できることです。
🔗 binding.break
やdebugger
からirb:rdbg
にアクセスする
Ruby 3.3の一部に組み込まれるdebug gem( v1.9.0
)を利用すると、以下のいずれかの方法でirb:rdbg
セッションの実行を設定できるようになります。
RUBY_DEBUG_IRB_CONSOLE=1
環境変数でirb_console
コンフィグを設定するrdbg
セッションで直接irb
コマンドを実行する
これで、以下のようにdebug
コンソールで同じirb:rdbg
セッションが表示されます。
$ RUBY_DEBUG_IRB_CONSOLE=1 bundle exec rdbg test.rb
[1, 1] in test.rb
=> 1| a = 1
=>#0 <main> at test.rb:1
irb:rdbg(main):002>
🔗 オートコンプリートの強化
🔗 オートコンプリートのドロップダウン表示問題を修正
IRBのオートコンプリート機能はRuby 3.1で導入されました。多くのユーザーが重宝する機能であることは証明されたものの、気づきにくい問題もいくつか潜んでいました。
- 候補リストが長くなるとドロップダウンによってプロンプトが上に押し上げられてしまうことがありました。これはユーザーの集中を妨げるだけでなく、セッション中の表示を頻繁に上下スクロールしなくてはならなくなります。
- ドロップダウンの色をカスタマイズできないため、ターミナルのテーマによってはテキストが読みにくくなることがありました。
- 特定条件下では、既に表示された部分がドロップダウンで消されてしまうことがありました。
これらの問題を以下の短い動画で示します。
しかし、Reline gem(Rubyのみで書かれたreadlineライブラリの代替)で多くの修正が行われたおかげで、これらの問題は修正されました。
他にも既存の問題があることは私たちも承知しています(表示のバウンス防止機能やTab補完オプションがないなど)。これらの機能は今後のリリースで改善していく予定です。
🔗 Reline::Face
でドロップダウンUIをカスタマイズ可能になった
Reline(v0.4.0以降)のおかげで、.irbrc
内でReline::Face
クラスを利用してドロップダウンのUIスタイルをカスタマイズ可能になりました。
たとえば、以下のスニペットはドロップダウンのデフォルトカラースキームを白黒テーマに変更します。
Reline::Face.config(:completion_dialog) do |conf|
conf.define :default, foreground: :white, background: :black
conf.define :enhanced, foreground: :black, background: :white
conf.define :scrollbar, foreground: :white, background: :black
end
変更結果は以下のようになります。
左: デフォルト、右: カスタム
🔗 実験的機能: RBSを用いた高精度のオートコンプリート
IRBの現在のオートコンプリート機能は、「正規表現」と「Binding
のランタイム情報」の組み合わせを利用しています。
この場合、最初のレベルではオートコンプリートで適切な結果が得られますが、チェインされたメソッド呼び出しについてはサポートできません。
たとえば、ユーザーがIRBで'Ruby'.up
まで入力すると、'Ruby'
がStringクラスであることが認識されるのでupcase
とupcase!
がオートコンプリートにリストに表示されます。
しかしIRBで'Ruby'.upcase.
まで入力した場合はサジェスチョンを表示できません。副作用が発生して値が変わってしまうリスクがあるため'Ruby'.upcase
を評価して値を得るわけにはいかないのです(たとえばRailsでUser.all.
を呼ぶ場合を考えてみましょう)。
しかしRubyでは、評価を行わずにオブジェクトの情報を得る方法が他にもあります。すなわち段階的型付け(gradual typing)です。そういうわけで、今年@tompngさんがrepl_type_completorプロジェクトを開始しました。このプロジェクトは、現在の正規表現ベースのオートコンプリート機能を段階的型付けで置き換え可能かどうかを調査するのが目的です。
repl_type_completorを試すには、Gemfileに以下を追加する必要があります。
gem "repl_type_completor", group: [:development, :test]
続いて以下のいずれかを実行して有効にします。
- IRB起動時に
--type-completor
フラグを渡す ~/.irbrc
ファイルに以下を追加する。
IRB.conf[:COMPLETOR] = :type # デフォルトは:regexp
詳しくはIRBのREADMEにあるType Based Completionセクションを参照してください。
🔗 使用感の改善
🔗 ページャーのサポート
今年、IRBの以下の出力でページャーがサポートされるようになりました。
show_source
、ls
、show_cmds
コマンド- 新しい
history
コマンド - 評価結果
出力の行数がターミナルの高さを超えると、IRBが以下のようにページネーションするようになります。
これにより多くのメリットが得られます。
- 上下矢印キーで自由に結果を上下スクロールできるようになる
- 上にスクロールしてもIRBで直前に出力した行を超えないようになる
- ページャー内で
/検索したい語
(例:/foo
)を入力することで、foo
を含む任意のテキストを手軽に検索できるようになる- 特にRailsコンソールでレコードを調べるときに便利です
私たちは、ページャーがユーザーの好みや条件によってはむしろ邪魔になる場合があることも認識しています。そこで、--no-pager
フラグを指定してIRBを起動するか、~/.irbrc
ファイルに以下を追加することでページャーを無効にできるようにしました。
IRB.conf[:USE_PAGER] = false
🔗 ;
を末尾に追加して戻り値の評価を抑制する
評価結果をページングすることで長い出力を調べやすくなりますが、特定の式では長い戻り値を表示したくないこともあります。たとえば、users = User.all
で数百件のレコードが表示されるとしましょう。
こういうときは、IRBの新機能である;
をusers = User.all;
のように式の末尾に追加することで、戻り値を非表示にできます。
例:
irb(main):001> long_string = "foo" * 10000;
irb(main):002> long_string.size
=> 30000
🔗 history
コマンド
入力履歴をすべて表示するhistory
コマンドが新らしくIRBに追加されました(デフォルトでは1000
個が上限です)。
history
コマンドの結果は長くなりがちなので、-g 検索語
フラグで出力をフィルタすることも可能になっています。
irb(main):006> history -g self
1000: history -g self
803: self
769: self
731: watch self @foo
698: break self.inspect
584: self
582: self
🔗 プロンプトがシンプルになった
既にお気づきの方もいると思いますが、IRBのプロンプトが少し短くなりました。
- 変更前
irb(main):001:0>
- 変更後
irb(main):001>
表示されなくなった:0
の部分は、インデントの深さを表す数値です。IRBで複数行に対して安定したオートインデントが実装されたので、今やこの表示は冗長です。
🔗 show_source
コマンドの強化
IRBのshow_source
コマンドは、特にデバッグ時には多くのユーザーになくてはならないツールです。今年、show_source
コマンドをさらに便利にする機能が2つ追加されました。
-s
フラグを指定すると、そのメソッドのsuper
に相当する定義を取得できるようになった(定義が存在する場合)- privateメソッドも表示できるようになった
🔗 -s
フラグ
以下のコードがあるとします。
class Foo
def foo
"foo"
end
end
class Bar < Foo
def foo
super + "bar"
end
end
この場合、Bar#foo
メソッドの定義と、そのsuper
に相当するFoo#foo
メソッドの定義を両方取得できます。
irb(main):001> show_source Bar#foo
From: test.rb:8
def foo
super + "bar"
end
=> nil
irb(main):002> show_source Bar#foo -s
From: test.rb:2
def foo
"foo"
end
=> nil
-ss
のようにフラグのs
を重ねることでsuper
のチェインを辿れます。super
に相当する定義が見つからない場合は、以下のように表示されます。
irb(main):003> show_source Bar#foo -ss
Error: Couldn't locate a super definition for Bar#foo
=> nil
🔗 次のRuby 3.4ではどうなるか
🔗 help
コマンドでIRBのヘルプを表示可能にする
一般に、ターミナルベースのアプリケーションを初めて使うときは、help
コマンドを入力して使い方を学びます。
しかしIRBのhelp
コマンドは、歴史的な理由によりRubyドキュメント検索用のri
入力を表示します。コマンドの設計が一般常識と異なっていると、特に新しいRubyアプリ開発者がIRBの使い方を学ぶときの妨げになります。そこで、以下の段取りでhelp
コマンドの振る舞いを変更することにしました。
- Ruby 3.2では、IRBヘルプ表示用の
show_cmds
コマンドと、従来のhelp
コマンドのエイリアスであるshow_doc
コマンドを追加しました。 - Ruby 3.3で
help
コマンドを実行すると、help
コマンドが今後show_cmds
と同じ振る舞いに変更されることをユーザーに警告します1。 - 2024年の早い時期に、IRB
v2.0
をリリースする計画があります。このv2.0
では、help
コマンドの変更がbreaking changesに含まれます。
🔗 拡張性の強化: コマンドとヘルパーメソッド
RailsやHanamiなど、Ruby製Webフレームワークの多くがコンソールのプラットフォームとしてIRBを利用しています。さらに、ライブラリによってはコマンドを利用するカスタム機能でIRBを拡張しているものもあります。
しかし従来のIRBには、こうした新しいコマンドやヘルパーメソッドに対応する標準APIがありませんでした。これによって、以下のような問題が発生しています。
- 拡張機能をIRBのヘルプで表示できない
- 拡張機能は基本的にpublicなので、private APIが直接利用されているとリファクタリングが難しくなる
- IRBを利用するには独自の手法を編み出す必要があるため、ほとんど違いのないソリューションがいくつも林立してしまう
私たちはこうした問題に対処するため、ライブラリやアプリケーションがIRBを拡張するための公式のAPIとドキュメントを提供する計画を立てています。最終的な目標は、IRBを単なる有用なツールから、他のツールからも利用可能な優れたプラットフォームに変えることです。
🔗 謝辞
IRBがこれほど目覚ましい進歩を遂げたのは、IRBおよびRelineのメンテナーチームの皆さまのおかげです。
また、コミュニティの全コントリビュータの皆さまにも深く感謝申し上げます。
関連記事
- この警告はRuby 3.2.2でも表示されます。 ↩
概要
原著者の依頼を受けて翻訳・公開いたします。
CC BY-NC-SA 4.0 Deed | 表示 - 非営利 - 継承 4.0 国際 | Creative Commons