こんにちは、@yuskuboです。
本記事は ビビッドガーデン Advent Calendar 2021 の13日目の記事です。
概要
RubyKaigi 2021でRubyの新しいdebuggerであるdebug.gemが紹介されましたね!Ruby 3.1からは標準ライブラリになる予定とのことです。
ビビッドガーデンでは、これまでbyebug gemとpry gemを利用していましたが、今後のバージョンアップ対応のことも考慮してdebug.gemへ移行し、日々の業務で使っています。
本記事では、私がよく使っているdebug.gemの機能をいくつかピックアップして紹介していきます。「それ知らなかった!」や「そんなこともできたんだ!」ということを1つでもお届けできれば嬉しいです。
対象読者
- debug.gemをまだ使ったことないから基本的な使用方法を知りたいという方
- debug.gemは既に使っているけど
binding.break
以外は知らないから、他のコマンドも知りたいという方
前提事項
- 本記事では、Ruby on Railsのサンプルアプリケーションでdebug.gemを使いながらご紹介します。
- サンプルアプリケーションを用意する手順も書いていますが、ご自身のPCでRuby on Railsを利用できる状態であることを前提としています。Ruby on Railsの環境構築から行いたい場合は、以下のRailsガイドなどを参照して行ってみてください。
- debug.gemを使うには、Ruby 2.6以降のバージョンである必要があります。
- RubyやRuby on Rails自体の解説は行っていません。
事前準備
Ruby on Railsのサンプルアプリケーションを用意していきます。既に手元にRuby on Railsを動かせる環境がある方は、スキップして問題ありません。
Railsプロジェクトを新規作成
$ rails new debug_sample_app
Gemfileにdebug.gemを追加
group :development, :test do
gem 'debug'
end
各コマンドの実行
Gemのインストール、マイグレーションの実行、コンパイルを行い、起動します。
debug.gemを使うことが目的なので、scaffoldを使ってUserというリソース一式を作成しています。
$ bundle install
$ bin/rails generate scaffold User name:string email:string
$ bin/rails db:migrate
$ bin/rails webpacker:compile
$ bin/rails s
画面へのアクセス
ユーザー一覧画面のhttp://127.0.0.1:3000/users にアクセスし、以下の画面が表示されればサンプルアプリケーションの準備は完了です。問題なく起動できたら、「New User」から1名ユーザーを作成しておきましょう。
デバッグ用のメソッド追加
メソッド呼び出しがあった方がデバッグを試しやすいので、userモデルにhas_email?
メソッドを追加し、usersコントローラーから呼び出しておきます。あくまでデバッグを試すためのものなので、処理自体は何でも大丈夫です。ここではuserのメールアドレスが存在するかどうかをチェックしています。
また、showアクションの中で処理を止めて色々試したいので、showアクションをbefore_actionの対象から外しておきます。
class UsersController < ApplicationController
# onlyに指定した配列からshowを削除
before_action :set_user, only: %i[ edit update destroy ]
# GET /users or /users.json
def index
@users = User.all
end
# GET /users/1 or /users/1.json
def show
# 以下の2行を追加
@user = User.find(params[:id])
@has_email = @user.has_email?
end
# ~~ 以下省略 ~~
class User < ApplicationRecord
# 以下のメソッドを追加
def has_email?
self.email.present?
end
end
デバッグコマンドを使ってみる
それでは早速デバッグコマンドを使ってみましょう!
binding.break / binding.b / debugger
プログラムを止めたい箇所にbinding.break
を記述すると、ブレークポイントとして処理を止めることができます。binding.break
のエイリアスとして、binding.b
とdebugger
も用意されているので、好きなものを使いましょう。
試しに、users_controllerのshowアクションにbinding.break
を追記して処理を止めてみます。
def show
@user = User.find(params[:id])
# ブレークポイントを追加
binding.break
@has_email = @user.has_email?
end
http://127.0.0.1:3000/users/1 へアクセスすると、以下のようにデバッグコンソールが表示されます。12行目で処理が止まっていることがわかります。
例えば、ここで@user
と入力すると、@user
変数の中身を見ることができます。
また、ここで1 + 1
と入力すると、rubyの式として評価され2
という結果を得ることができます。
何も入力せずにEnterを押した場合は、最後に実行したコマンドや式(ここでは1 + 1)が繰り返し実行されます。何度もステップオーバーやステップインする時に使うと同じコマンドを繰り返し入力する必要がないので楽ですね!
ブレークポイントでの確認が終わったらcontinue
(この後ご紹介するコマンド)を実行してください。continue
を実行すると、最後まで処理が実行されて、ブラウザにユーザー詳細画面が表示されます。
binding.break do: 'debug_command'
binding.breakは、doキーワードにデバッグコマンドを指定して使うこともできます。例えば、以下のように指定すれば、ActiveRecord::RecordNotFoundが発生した時のみブレークポイントで止めることができます。
def show
@user = User.find(params[:id])
binding.break do: 'catch ActiveRecord::RecordNotFound'
@has_email = @user.has_email?
end
上記のように指定するとユーザーが存在するhttp://127.0.0.1:3000/users/1 へアクセスした時は処理は止まりません。
しかし、ユーザーが存在しないhttp://127.0.0.1:3000/users/2 へアクセスすると、ActiveRecord::RecordNotFoundが発生して処理が止まります。
このように特定の条件で処理を止めてデバッグしたい時に使うと便利です!
next (n) / step (s) / continue (c)
以下の3つのコマンドはデバッグコンソール上での基本操作となるコマンドです。それぞれ意味は以下の通りです。
-
next
:ステップオーバー(同じメソッド内で次の行へ進む) -
step
:ステップイン(別のメソッド呼び出しである場合、呼び出したメソッド内へ進む。メソッド呼び出しでない場合は、同じメソッド内で次の行へ進む。) -
continue
:後続の処理を実行
なお、それぞれ省略形も用意されていて、頭文字だけでコマンドを実行できます。例えば、n
だけ入力すると右側に「# next command」と表示され、next
であることがわかります。細かい部分がとても親切ですね!
info (i)
現在実行中のフレームのローカル変数、インスタンス変数、定数を表示することができます。ざっと変数や定数を確認したい時に便利です。省略形のi
でも実行できます。
outline (o) / ls
現在のスコープで利用可能なメソッド、定数、ローカル変数、インスタンス変数を表示できます。「ここであのメソッド使えるっけ?」となった時に役立ちます。省略形のo
でも実行できますし、エイリアスとしてls
も用意されています。
frame (f) / list (l)
デバッグ中に変数の中身を見たり、式を実行したりしているうちに、「あれ?今どこで何してたんだっけ?」となることがあります。frame
を実行すると、現在実行中のフレーム情報を表示することができます。これでusers_controllerの12行目でデバッグしていたことを確認できます。省略形のf
でも実行できます。
さらに、「ブレークポイントを設定した周辺のソースコードはどんなものだったっけ?」を確認したい時は、list
を実行します。省略形のl
でも実行できます。
list
を実行すると、現在実行中のフレーム情報周辺のソースコードを表示することができます。複数回実行すると、実行する度に後続の行を表示します。list -
のように-
オプションを付けると1つ前のフレーム情報を表示することができます。また、list 10-15
のように表示する行数を指定することもできます。
record
これはすごく便利だなと思ったのが、record
です。record
を有効にすると実行した処理を記録して、後から処理を遡って確認することができます。例えば、「5行前ではこの変数の値はどうなってるんだっけ?」のように前の状態を確認したいときに重宝します。
処理の記録を開始する時は、以下のようにブレークポイントで止めた後、record on
で記録を開始します。その後、任意の場所までnext
で処理を進めます。
処理を進めた後にstep back
すると、実行した処理を遡ることができます。step back
している間は、「replay」と表示されます。複数回step back
していると、userモデルやusersコントローラー内の見覚えのある処理まで遡ってきます。なお、戻り過ぎた時は、step
で処理を進めることもできます。
確認が終わったらstep reset
で、replayを終了し、ブレークポイントまで戻れます。
また、処理の記録を終了する時は、record off
で記録が終了となります。
trace
trace
コマンドを使うと、トレース情報を表示することができます。trace line
を実行するとLineTracerが有効になり、トレース情報が表示されるようになります。
この状態で処理を進めてみると、以下のようにトレース情報が表示されます。赤枠の部分を見ると、usersコントローラーの13行目の次にuserモデルの3行目(has_email?
メソッドの処理)が実行されていることがわかります。
トレース情報の表示が不要になったら、trace off line
で表示を終了することができます。
backtrace (bt)
backtrace
コマンドを使うと、バックトレース情報を表示することができます。省略形のbt
でも実行できます。ここではモデル側に用意したメソッド内にブレークポイントを設定して、バックトレース情報を確認してみます。
class User < ApplicationRecord
def has_email?
# ブレークポイントを設定する
binding.break
self.email.present?
end
end
backtrace
を実行すると多くの情報が出てくるので、今回はbacktrace 5
として、5行だけ表示してみます。has_email?
メソッドがusersコントローラーから呼び出されたことがわかります。
config
config
を使うとdebug.gemの設定内容を確認することができます。例えば、binding.break
で処理を止めた時に表示されるソースの行数(show_src_lines)はデフォルトでは10行であることがわかります。
さらにconfig set
を使うと設定内容を変更することができます。試しに表示する行数を変更してみましょう。config set show_src_lines 20
と実行すると、表示を20行に変更することができます。
表示行数の設定変更後に処理を止めてみると、20行表示されるようになっています。
なお、デバッグコンソールで変更した設定はRailsアプリケーションを再起動するとデフォルト値へ戻ってしまいます。常に反映させたい場合は、~/.rdbgrc
という初期化スクリプトに記述する必要があります。初期化スクリプトはデバッグセッション開始時に読み込まれて記述した設定が反映されます。初期化スクリプトに以下のように記述しておくと、binding.break
で処理を止めた時に表示されるソースの行数は常に20行となります。
config set show_src_lines 20
help (h)
「あのコマンドどうやって使うんだっけ?」や「他にどんなコマンドがあるだろう?」というのが知りたくなったら、help
コマンドを使いましょう。全てのコマンドの使い方を表示することができます。省略形のh
でも実行できます。また、help record
のようにコマンドを指定すれば、指定したコマンドの使い方だけ表示することもできます。
その他補足事項
各コマンドの紹介時にi
、n
、s
のような省略形もご紹介しました。これらは1文字のデバッグコマンドであるため、ローカル変数としてi
、n
、s
が使われていたとしてもデバッグコマンドとして実行されます。ローカル変数の中身を確認したい場合は、p i
とすることで表示することができます。
以下のようにローカル変数i
を用意して、デバッグコンソールから確認してみます。
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
# ローカル変数iを用意
i = 'test'
binding.break
@has_email = @user.has_email?
end
i
だとinfo
コマンドが実行されていますが、p i
とするとローカル変数i
の中身が表示されていることがわかります。
まとめ
いかがだったでしょうか?「それ知らなかった」、「明日からそのコマンド使ってみよう」といったものが1つでもあったなら幸いです。debug.gemには便利で魅力的なコマンドがたくさん用意されているので、私もどんどん使い込んで行きたいと思います。
なお、本記事は、以下のdebug.gemのREADMEを元にいくつかの内容をピックアップしてご紹介したものです。最新の仕様や機能・コマンドを網羅的に知りたい場合は、debug.gemのREADMEをご確認ください。
また、RubyKaigi 2021のスライド資料や動画では、使用方法に加えて「開発された背景」や「どうやって動いているのか」なども解説されているので、ぜひこちらも合わせてご確認ください。
最後に
ビビッドガーデンが運営する食べチョクのバックエンドは、Ruby on Railsで開発されています。ビビッドガーデンではエンジニアを絶賛募集中ですので、気になった方はこちらもぜひ覗いてみてください!!