Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Ruby 3.3で大幅に強化されたIRBの解説(翻訳)

概要

原著者の依頼を受けて翻訳・公開いたします。

ruby/irb - GitHub

CC BY-NC-SA 4.0 Deed | 表示 - 非営利 - 継承 4.0 国際 | Creative Commons

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)のときは、debugnextなどのコマンドセットを新たに導入し、binding.irbのブレークポイントからruby/debug  gemのセッションに速やかに移行できるようにしました(混乱を避けるため、本記事では以後debug gemのセッションをrdbgと表記します)。

ruby/debug - GitHub

これは大きな改善ですが、このままでは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に緊密に統合することで、以下のような多くのメリットを得られます。

しかし最も重要なのは、従来のpry-byebug gemと同等レベルのデバッグエクスペリエンスをユーザーに提供できることです。

deivid-rodriguez/pry-byebug - GitHub

🔗 binding.breakdebuggerから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で導入されました。多くのユーザーが重宝する機能であることは証明されたものの、気づきにくい問題もいくつか潜んでいました。

  • 候補リストが長くなるとドロップダウンによってプロンプトが上に押し上げられてしまうことがありました。これはユーザーの集中を妨げるだけでなく、セッション中の表示を頻繁に上下スクロールしなくてはならなくなります。
  • ドロップダウンの色をカスタマイズできないため、ターミナルのテーマによってはテキストが読みにくくなることがありました。
  • 特定条件下では、既に表示された部分がドロップダウンで消されてしまうことがありました。

これらの問題を以下の短い動画で示します。

autocompletion issues demo

しかし、Reline gem(Rubyのみで書かれたreadlineライブラリの代替)で多くの修正が行われたおかげで、これらの問題は修正されました。

improved autocompletion demo

ruby/reline - GitHub

他にも既存の問題があることは私たちも承知しています(表示のバウンス防止機能や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クラスであることが認識されるのでupcaseupcase!がオートコンプリートにリストに表示されます。

しかしIRBで'Ruby'.upcase.まで入力した場合はサジェスチョンを表示できません。副作用が発生して値が変わってしまうリスクがあるため'Ruby'.upcaseを評価して値を得るわけにはいかないのです(たとえばRailsでUser.all.を呼ぶ場合を考えてみましょう)。

しかしRubyでは、評価を行わずにオブジェクトの情報を得る方法が他にもあります。すなわち段階的型付け(gradual typing)です。そういうわけで、今年@tompngさんがrepl_type_completorプロジェクトを開始しました。このプロジェクトは、現在の正規表現ベースのオートコンプリート機能を段階的型付けで置き換え可能かどうかを調査するのが目的です。

ruby/repl_type_completor - GitHub

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_sourcelsshow_cmdsコマンド
  • 新しいhistoryコマンド
  • 評価結果

出力の行数がターミナルの高さを超えると、IRBが以下のようにページネーションするようになります。

pager demo

これにより多くのメリットが得られます。

  • 上下矢印キーで自由に結果を上下スクロールできるようになる
  • 上にスクロールしても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コマンドの振る舞いを変更することにしました。

  1. Ruby 3.2では、IRBヘルプ表示用のshow_cmdsコマンドと、従来のhelpコマンドのエイリアスであるshow_docコマンドを追加しました。
  2. Ruby 3.3でhelpコマンドを実行すると、helpコマンドが今後show_cmdsと同じ振る舞いに変更されることをユーザーに警告します1
  3. 2024年の早い時期に、IRB v2.0をリリースする計画があります。このv2.0では、helpコマンドの変更がbreaking changesに含まれます。

🔗 拡張性の強化: コマンドとヘルパーメソッド

RailsHanamiなど、Ruby製Webフレームワークの多くがコンソールのプラットフォームとしてIRBを利用しています。さらに、ライブラリによってはコマンドを利用するカスタム機能でIRBを拡張しているものもあります。

しかし従来のIRBには、こうした新しいコマンドやヘルパーメソッドに対応する標準APIがありませんでした。これによって、以下のような問題が発生しています。

  • 拡張機能をIRBのヘルプで表示できない
  • 拡張機能は基本的にpublicなので、private APIが直接利用されているとリファクタリングが難しくなる
  • IRBを利用するには独自の手法を編み出す必要があるため、ほとんど違いのないソリューションがいくつも林立してしまう

私たちはこうした問題に対処するため、ライブラリやアプリケーションがIRBを拡張するための公式のAPIとドキュメントを提供する計画を立てています。最終的な目標は、IRBを単なる有用なツールから、他のツールからも利用可能な優れたプラットフォームに変えることです。

🔗 謝辞

IRBがこれほど目覚ましい進歩を遂げたのは、IRBおよびRelineのメンテナーチームの皆さまのおかげです。

また、コミュニティの全コントリビュータの皆さまにも深く感謝申し上げます。

関連記事

Ruby 3.3.0がリリースされました

Ruby 3.2のIRBに導入された新機能(翻訳)


  1. この警告はRuby 3.2.2でも表示されます。 

CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。