ruby/debugメンテナーが教える2025年のデバッグ便利技集(翻訳)
これは、私が編み出したRubyのデバッグ技と推奨事項を未完成のまま取り急ぎまとめたものです。
- 🔗 VS CodeのRuby LSP拡張を使ってdebug.gemに接続することも可能です。
この場合、launch.json
設定をわずかに変更する必要があります(例を参照)。これによって接続時の問題に対処するためのエラー処理が改善されます。
- 🔗
launch.json
設定ファイルで、attach
の代わりにlaunch
リクエストを使ってみてください。
これにより、デバッグサーバーを手動で起動/停止する必要がなくなるので、デバッグ作業がシンプルになります。ほとんどのRailsプロジェクトは、以下のようなシンプルなエントリで十分です。
{
"version": "0.2.0",
"configurations": [
{
"type": "ruby_lsp",
"name": "Launch Server",
"request": "launch",
"program": "bin/rails s",
},
]
}
- 🔗 デバッグセッションの効率は、メソッド/クラス/ファイル間を自在に移動する能力に大きく依存します。エディタの設定を万全にしておきましょう(Ruby LSPなど)。
- Ruby LSPは、VS Code以外の多くのエディタやIDEでも利用できます。
- 🔗 Ruby LSPのCode Lens機能は、ターミナルとVS Codeのどちらでもテストのデバッグを楽にしてくれます。
- なお、RSpecのテストにcode lensを提供するためにruby-lsp-rspec gemを作りました。
- 🔗
Gemfile
にgem "debug", require: "debug/prelude"
と記述しましょう。
debug.gemは必要に応じてアクティブになりますが、デバッグしていないときは不要です。
require: "debug/prelude"
を指定することで、breakpoint
やbinding.break
に似たブレークポイント用メソッドを定義しても、このgemはすぐにはアクティブになりません。
(この振る舞いは、Rails 7.2以降で生成されたプロジェクトではデフォルトになっています) -
🔗 debug.gemを特定の環境でのみ無効にしておきたい場合は、
RUBY_DEBUG_ENABLE
を0
に設定できます。
たとえば、これをCIに設定しておけば、消し忘れのdebugger
やbinding.break
でエラーがraiseされるようになり、デバッガのせいでCIが立ち往生せずに済みます。
begin
# debugの読み込みを試みるが、誤って有効にされないよう
# configコンポーネントだけを読み込む
require "debug/config"
zeitwerk_paths = Gem.loaded_specs["zeitwerk"].full_require_paths.freeze
bootsnap_paths = Gem.loaded_specs["bootsnap"].full_require_paths.freeze
DEBUGGER__::CONFIG[:skip_path] = Array(DEBUGGER__::CONFIG[:skip_path]) + zeitwerk_paths + bootsnap_paths
rescue LoadError
# debug.gemが何らかの理由で読み込まれていなかった場合。
# 例: このファイルがproduction環境で読み込まれた場合
# (通常はproduction環境にdebug.gemをインストールしない)
end
例: Sorbet
を使っている場合は、無視するgemリストに以下のようにsorbet-runtime
を追加できます。
sorbet_paths = Gem.loaded_specs["sorbet-runtime"].full_require_paths.freeze
DEBUGGER__::CONFIG[:skip_path] = Array(DEBUGGER__::CONFIG[:skip_path]) + sorbet_paths
- 🔗 debug.gemとIRBの統合を有効にするとデバッグのエクスペリエンスが改善されるので、ほとんどのRubyユーザーにおすすめできる便利技です。これは以下の設定で行えます。
RUBY_DEBUG_IRB_CONSOLE
を1
に設定するDEBUGGER__::CONFIG[:irb_console]
をtrue
に設定する
- 🔗 以下のdebug.gemコマンドは、Enterキーを押すと同じコマンドを繰り返せます。
step
(s
)next
(n
)continue
(c
)finish
(fin
)until
(u
)up
down
たとえば、s
を入力してからEnterキーを押し、もう一度Enterキーを押すとstep
コマンドが繰り返されます。
- 🔗 ブレークポイントに到達した後でコマンドを自動実行するには、
debugger(do: "...")
やdebugger(pre: "...")
が使えます。
# ローカル変数を出力してからコンソールを開く
debugger(pre: "info locals")
# ローカル変数を出力してからプログラムを続行する
debugger(do: "info locals")
- 🔗
trace exception
コマンドとcatch [exception]
コマンドを組み合わせると、制御フローに関連するデバッグがやりやすくなります。trace exception
は、例外がraiseされたときにトレースを出力するcatch [exception]
は、例外がraiseされたときに実行を中断する
- 🔗
bt [n]
コマンドとup
/down
コマンドの組み合わせは、同じコードパスにブレークポイントを複数設定するよりも多くの場合効率的です。
- 🔗 debug.gemは、ブレークポイントにさしかかるとすべてのスレッドをフリーズさせます。これが問題になる場合は、代わりに
binding.irb
をお使いください。binding.irb
はREPLで簡単なデバッグを行えます。そこでdebug
コマンドを実行するとdebug.gemを有効にして本格的にデバッグできます。
- 🔗 デバッグの基本的な概念について学びたい方には、私がRubyKaigi 2022で登壇したときの動画が今でも役立つでしょう。
以上のデバッグ技がお役に立てば幸いです。Happy debugging!
概要
元サイトの許諾を得て翻訳・公開いたします。
日本語タイトルは内容に即したものにしました。