これは「Happiness Chain Advent Calendar 2024」の1日目の記事です。
はじめに
みなさん、こんにちは。Happiness Chainメンターのryoです。
今回は、RailsとDjangoの初学者向けに、デバッグの重要性と、VSCodeを使用したRubyとPythonのデバックの方法、RailsとDjangoのデバッグ環境の構築手順について、さらにDockerを使用している場合のポイントも合わせて解説します。
ぜひ、実際に手を動かしながら進めてみてください。
デバックの重要性
デバッグは、プログラム内のバグを見つけ、修正する作業です。
プログラムはコンパイル(PCが実行できる形式に変換する作業)時のエラーと実行時のエラーが想定されます。RubyとPythonはインタプリタ言語で、基本的にはコンパイルは不要ですので、実行時のエラーでデバックする必要が出てきます。
実行時のエラーに関して、例外発生の有無でデバックの方法が異なります。
例外とは、プログラム実行時に発生する予期せぬエラーです。
例外が発生した場合については、例外発生時点で処理が止まり、例外が発生したファイル名、行数、内容等のエラーメッセージがコンソールに表示されます。ですので、そのエラーメッセージを読み解くことで、ほぼ修正が可能です。(ですので、エラーメッセージは必ず翻訳して、原因を理解しましょう。)
次に例外が発生しない場合についてですが、例外が発生しないエラーは、期待しない挙動をするような時です。
以下のような関数を作成し、実行したとします。
def sum_of_evens(numbers)
sum = 0
numbers.each do |num|
if num.even?
sum = num
end
end
sum
end
list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = sum_of_evens(list)
puts result
def sum_of_evens(numbers):
sum = 0
for num in numbers:
if num % 2 == 0:
sum = num
return sum
list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = sum_of_evens(list)
print(result)
上記の関数は数値が要素の配列を受け取り、偶数の合計値を取得する関数を想定し作成しました。
そして、1から10の要素を持つ配列を渡した際に、出力値
として30を期待しますが、実際には10
と出力されます。例外は発生せず、エラーの原因はコンソールに出力されません。
これくらいの行数の処理であれば、ひと目見ただけでエラーの原因が分かると思います。しかし、実務ではこれ以上に膨大なコード量で、どこにエラーがあるのか見つけるのが大変ですので、デバックの方法を知っておくのは大切です。
Ruby,Pythonのデバックの方法
実際に手を動かす方は、新しいディレクトリを作成して、ディレクトリ内に、main.rb
もしくはmain.py
を作成して、上記のコードをコピペで貼り付けてください。
上記を例にデバックしてみましょう。VSCodeには便利なデバック機能があります。
左側のアイコンの上から4つ目をクリックし、右側に表示されるcreate a launch.json
をクリックしましょう。
そうすると、プロジェクト直下に.vscode/launch.json
が作成されます。
Rubyの場合
※Rubyの基本的な拡張機能として、Rubyを入れている場合は、Ruby
はアンインストールして、Ruby LSP
をインストールしましょう。
Ruby LSPがインストールされている状態で、create a launch.json
をクリックしましょう。
以下のようなlunch.json
が作成されます。
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "ruby_lsp",
"name": "Debug script",
"request": "launch",
"program": "ruby ${file}"
},
{
"type": "ruby_lsp",
"name": "Debug test",
"request": "launch",
"program": "ruby -Itest ${relativeFile}"
},
{
"type": "ruby_lsp",
"name": "Attach debugger",
"request": "attach"
}
]
}
このファイルを簡単に説明すると、configurations
にはデバックの構成が記述されており、現在nameがDebug script
、Debug test
、Attach debugger
の3つがあるかと思います。使い道は以下の通りです。
- Debug script: 単純なRubyスクリプトをデバッグする場合に使用
- Debug test: Rubyのテストコードをデバッグする場合に使用
- Attach debugger: 既に起動している Rubyプロセスをデバッグする場合に使用
今回の場合は、Debug script
を使用します。
Pythonの場合
create a launch.json
をクリックした際に、以下のように表示されるかと思いますので、Python File
を選択しましょう。
そうすると、以下のようなlunch.jsonを作成されるかと思います。
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python Debugger: Current File",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal"
}
]
}
このファイルを簡単に説明すると、configurations
にはデバックの構成が記述されており、現在Python Debugger: Current File
があります。
今回はこちらを使用します。
これでデバックの準備は整いました。
サイドバーに実行とデバック
と表示されていて、その横に再生ボタンとどのデバックの構成を使用するかが表示されているかと思います。
Rubyの場合は、Debug Script
、Pythonの場合は、Python Debugger: Current File
と表示されていればOKです。
この状態で再生ボタン、もしくはfn
+ F5
キーを押すと、デバックが開始されます。
VSCodeの右側、もしくは下側にコンソールが表示され、10
と出力結果が表示されるかと思います。コンソールが表示されない場合は、option
+ command
+ B
を押すと表示されるかと思います。
ここまでは、通常と変わらず、ただコードが実行されただけですが、ブレイクポイントを設置することで、詳細なデバックが可能となります。
ブレイクポイントの設置方法は、行数の左側をクリックすることで設置する事ができます。もしくは設置したい行数を選択した状態でfn
+ F9
キーを押すことで設置することができます。では、result
を定義している行にブレイクポイントを設置しましょう。
設置すると、行数の左側に赤丸が表示されるかと思います。
この状態でfn
+ F5
キーを押し、デバックを開始すると、ブレイクポイントで処理が中断していることが分かります。
左側を見ると、その時点で定義されている変数を確認することができます。
そして、上部に以下画像の様なツールバーが表示されているかと思います。
- Rubyの場合
- Pythonの場合
上記のボタンについて、共通のボタンについて説明していきます。
続行(fn + F5キー)
次のブレイクポイントまで処理を進めます。
puts・print
の行にブレイクポイントを設置しましょう。
現在result
の行にて処理が止まっているかと思いますが、fn
+ F5
キーを押すと、puts・print
の行で処理が止まります。再度fn
+ F5
キーを押すと、処理が最後まで実行されるかと思います。
ステップオーバー(fn + F10キー)
停止位置の処理を行い、次の行に移動します。result
の行にて処理が止まっている状態で、fn
+ F10
キーを押すと、puts・print
の行で処理が止まります。
ステップイン(fn + F11キー)
停止位置の処理を行い、次の行に移動します。ステップオーバーとの違いは、現在の停止位置で関数やメソッドが呼び出されている場合は、関数内の最初の行で処理が止まります。result
の行にて処理が止待っている状態で、fn
+ F11
キーを押すと、sum_of_evens関数
内の最初の行にて処理が止まります。
ステップアウト(shift + fn + F11キー)
停止位置が関数やメソッド内の場合は、現在の関数やメソッドの実行を完了させて、呼び出し元に戻ります。ステップインを実行し、sum_of_evens関数
内の最初の行にて処理が止まっている状態で、shift
+ fn
+ F11
キーを押すと、result
を定義している行に移動します
Rubyの場合は、sum_of_evens関数
のend
にフォーカスがされるかと思いますが、内部的にはsum_of_evens
の処理が終わった状態で停止しています。ですので、再度ステップオーバーもしくはステップインを行うと、puts・print
の行に移動します
再起動(command + shift + fn + F5キー)
デバックを最初の行からやり直します。fn
+ F5
キーを押し、デバックを開始後、command
+ shift
+ fn
+ F5
キーを押すと、最初の行から処理をやり直し、result
の行にて処理が止まります。
停止(shift + fn + F5キー)
デバックを中断します。
fn
+ F5
キーを押し、デバックを開始後、shift
+ fn
+ F5
キーを押すと、デバックが中断されます。
では、上記のコマンドを使いこなし、今回のエラーをデバックしていきましょう。
今回はresult
が期待する値にならないので、sum_of_evens関数
内の処理が間違っていると分かっている状態です。
ですので、sum_of_events関数
内の処理を確認していきます。
result
を定義している所にブレイクポイントを設置した状態で、デバックを開始し、ステップインして、sum_of_events関数
内に入ります。
さらにステップインをして、ループ処理内に入ります。初回のループ内処理はnum
に1
が格納されている事が分かります。条件分岐はfalse
になるので、初回のループ内処理は終わります。
2回目のループ内処理はnum
に2
が格納されており、条件式はtrue
となり、条件式内の処理に入っていることが分かります。
そして、sum
に2
が格納されていることが分かります。
3回目のループ内処理では、条件式がfalse
になるので、sum
は変化しません。
4回目のループ内処理は、条件式はtrue
となり、条件式内の処理が実行されますが、この時にsum
の値を確認すると4
が格納されていることが分かります。
上記のことから、sum
に加算ではなく、代入するようになっているので、sum += num
と修正すれば良いことが分かりました。
ここまでは、単純なスクリプト実行の場合の方法で、
RailsやDjangoのデバックは方法が異なります。
Railsのデバック方法
※これからRailsアプリを作成する際や、既存で作成したアプリにて是非試してみてください。
Railsはdebug
というgem
を使用することをオススメします(Rails7以降はデフォルトでインストールされるようになっています)。
debug
がインストールされていれば、通常通りrails s
で起動し、
デバックしたい箇所にdebugger
を記述することで、そこで処理を止めることができます。
例えば、tasksコントローラのindexアクションにdebugger
を記述し、/tasks
にアクセスします。
class TasksController < ApplicationController
before_action :set_task, only: %i[ show edit update destroy ]
# GET /tasks or /tasks.json
def index
@tasks = Task.all
debugger
end
...
end
そうすると、ページは読み込み状態のままで、レンダリングされず、rails s
実行中のコンソールにて、以下画像の様に表示され、最下部には(rdbg)
と表示されているかと思います。
ここでは、Rubyコードを実行できます。また、現在実行中の処理内の変数を参照することができます。上記の例だと@tasks
を参照する事ができます。
(rdbg) @tasks
[#<Task:0x0000000104ed16b0
id: 1,
title: "タイトル1",
content: "",
created_at: Thu, 28 Nov 2024 13:03:54.774480000 UTC +00:00,
updated_at: Thu, 28 Nov 2024 13:03:54.774480000 UTC +00:00>,
#<Task:0x0000000106811418
id: 2,
title: "タイトル2",
content: "",
created_at: Thu, 28 Nov 2024 13:04:05.514263000 UTC +00:00,
updated_at: Thu, 28 Nov 2024 13:04:05.514263000 UTC +00:00>]
c
と入力すると、次のdebugger
と記述されているところまで処理を進めます。
debug
のgemの詳細な使用方法は、以下Railsガイドを参照してください。
VSCodeを使用した方法もあるのですが、デバックのしやすさでは、gemを使用するほうが良いです。一応、以下にVSCodeを使用した方法を載せておきます。以下をlunch.json
のconfigurations
にセットすることで、デバックができます。
...
{
"type": "ruby_lsp",
"name": "Rails Server",
"request": "launch",
"program": "bin/rails server"
}
...
Dockerを使用しているRailsのデバック方法
Dockerを使用しているRailsで、gemのdebugger
を使用する場合、対話型操作が必要となるため、まずdocker-compose.yml
のRailsのコンテナに以下のオプションを追記する必要があります。
...
services:
web: # Railsのコンテナ
...
tty: true
stdin_open: true
....
そしてdocker compose up
で起動させます。
次に別タブでターミナルを開き、docker compose ps
で起動しているコンテナを確認します。
docker compose ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
427bfc7d4748 rails_template:latest "entrypoint.sh bundl…" 22 seconds ago Up 21 seconds 0.0.0.0:3000->3000/tcp rails_template-web-1
...
このコンテナ名を指定して、docker attach rails_template-web-1
を実行して、コンテナにアタッチします。
docker attach rails_template-web-1
これで、Railsのデバック方法
のセクションと同じ流れで、
コード中にdebugger
を記述し、その処理が動くようにページアクセスを行うと、処理が止まり、docker attach
を実行したコンソールで、Rubyコマンドを実行することができます。
Djangoのデバック方法
※これからDjangoアプリを作成する際や、既存で作成したアプリにて是非試してみてください。
VScodeにてcreate a launch.json
をクリックした後に、python Debugger
を選択、Django
、manage.py
を選択しましょう。
そうすると、以下のようなlunch.jsonが作成されます。
{
// IntelliSense を使用して利用可能な属性を学べます。
// 既存の属性の説明をホバーして表示します。
// 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python デバッガー: Django",
"type": "debugpy",
"request": "launch",
"args": [
"runserver"
],
"django": true,
"autoStartBrowser": false,
"program": "${workspaceFolder}/manage.py"
}
]
}
後は、Pythonスクリプトのデバック時と同様に、ブレイクポイントを設置することで、処理を止めることができます。例えば、TaskListViewの処理にブレイクポイントを設置し、/tasks
にアクセスすると、ページは読み込み状態のままで、レンダリングされず、処理が中断し、左側で変数の確認ができます。後は、ツールバーのコマンドを使いデバックを行います。
Dockerを使用しているDjangoのデバック方法
まず、Dev Containers
という拡張機能をインストールしましょう。
次に、左下のアイコンをクリックし、Add Dev Container Configuration Files...
をクリックし、
Add configuration to workspace
を選択し、
From docker-compose.yml
を選択し、
後の2回は何も選択せずOKを押してください
これで、.devcontainer/devcontainer.json
ファイルが作成されます。
また、.vsocode/lunch.jsonを以下のように修正しましょう。
{
// IntelliSense を使用して利用可能な属性を学べます。
// 既存の属性の説明をホバーして表示します。
// 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python デバッガー: Django",
"type": "debugpy",
"request": "launch",
"args": [
"runserver",
+ "0.0.0.0:8001"
],
"django": true,
"autoStartBrowser": false,
"program": "${workspaceFolder}/manage.py"
}
]
}
"0.0.0.0:8001"と記述しましたが、docker compose up
で使用しているポート以外であれば、大丈夫です。
これで設定は完了です。
通常通りdocker compose up
でコンテナを起動しましょう。
次にVSCodeの左下のアイコンをクリックし、Attach to Running Container...
を選択し、
先ほど起動したコンテナを選択しましょう。
そうすると、新しいVSCodeウィンドウが開きます。
これはコンテナに接続して、コンテナ内をVSCodeで開いている状態です。
このVSCodeにてDjangoアプリのディレクトリを開きましょう。
左側の一番上のアイコンをクリックし、Open Folder
をクリック、入力欄にDjangoアプリを配置しているディレクトリを入力して、OK
を押しましょう。
Djangoアプリを配置しているディレクトリは、基本的にDockerfileのWORKDIR
に配置しているかと思います。例えば、WORKDIR /app
の場合は、 /app/
と入力します。
左側にDjangoアプリのディレクトリ構成が表示されていればOKです。
次に、コンテナ内を開いているVSCodeにて、Pythonの拡張機能をインストールしましょう。
そして、コンテナ内を開いているVSCodeにて、デバックと実行をしましょう。
これで、lunch.json
で指定したポート番号(今回の場合は8001)にアクセスできるかと思います。
デバックする際は、Djangoのデバック方法
のセクションと同じ流れで、
VSCode上にブレイクポイントを設置するのですが、注意点として、コンテナ内を開いているVSCodeにて、ブレイクポイントを設置してください。
終わりに
いかがだったでしょうか?
デバッグの方法を知っておくと、特に初学者にとってエラーによるストレスを軽減できます。また、デバッグを活用することでプログラムの挙動を深く理解することにもつながります。ぜひ積極的に活用してください!