15
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Rails,Django初学者必見】デバックの方法は必ず身につけておきましょう

Last updated at Posted at 2024-11-30

これは「Happiness Chain Advent Calendar 2024」の1日目の記事です。

はじめに

みなさん、こんにちは。Happiness Chainメンターのryoです。

今回は、RailsとDjangoの初学者向けに、デバッグの重要性と、VSCodeを使用したRubyとPythonのデバックの方法、RailsとDjangoのデバッグ環境の構築手順について、さらにDockerを使用している場合のポイントも合わせて解説します。

ぜひ、実際に手を動かしながら進めてみてください。

デバックの重要性

デバッグは、プログラム内のバグを見つけ、修正する作業です。

プログラムはコンパイル(PCが実行できる形式に変換する作業)時のエラーと実行時のエラーが想定されます。RubyとPythonはインタプリタ言語で、基本的にはコンパイルは不要ですので、実行時のエラーでデバックする必要が出てきます。

実行時のエラーに関して、例外発生の有無でデバックの方法が異なります。

例外とは、プログラム実行時に発生する予期せぬエラーです。

例外が発生した場合については、例外発生時点で処理が止まり、例外が発生したファイル名、行数、内容等のエラーメッセージがコンソールに表示されます。ですので、そのエラーメッセージを読み解くことで、ほぼ修正が可能です。(ですので、エラーメッセージは必ず翻訳して、原因を理解しましょう。)

次に例外が発生しない場合についてですが、例外が発生しないエラーは、期待しない挙動をするような時です。
以下のような関数を作成し、実行したとします。

main.rb
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

main.py
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をクリックしましょう。

create_lunch.jpg

そうすると、プロジェクト直下に.vscode/launch.jsonが作成されます。

Rubyの場合

※Rubyの基本的な拡張機能として、Rubyを入れている場合は、Rubyはアンインストールして、Ruby LSPをインストールしましょう。
Ruby LSPがインストールされている状態で、create a launch.jsonをクリックしましょう。

https://qiita.com/rys0707/items/0b0ed63f9b266bc1358b#vscode%E3%81%B8%E3%81%AErubocop%E3%81%AE%E5%B0%8E%E5%85%A5%E6%89%8B%E9%A0%86

以下のようなlunch.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 scriptDebug testAttach debuggerの3つがあるかと思います。使い道は以下の通りです。

  • Debug script: 単純なRubyスクリプトをデバッグする場合に使用
  • Debug test: Rubyのテストコードをデバッグする場合に使用
  • Attach debugger: 既に起動している Rubyプロセスをデバッグする場合に使用

今回の場合は、Debug scriptを使用します。

Pythonの場合

create a launch.jsonをクリックした際に、以下のように表示されるかと思いますので、Python Fileを選択しましょう。

select_python_file.jpg

そうすると、以下のようなlunch.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": [
    {
      "name": "Python Debugger: Current File",
      "type": "debugpy",
      "request": "launch",
      "program": "${file}",
      "console": "integratedTerminal"
    }
  ]
}

このファイルを簡単に説明すると、configurationsにはデバックの構成が記述されており、現在Python Debugger: Current Fileがあります。

今回はこちらを使用します。

これでデバックの準備は整いました。

サイドバーに実行とデバックと表示されていて、その横に再生ボタンとどのデバックの構成を使用するかが表示されているかと思います。

debug_start.jpg

Rubyの場合は、Debug Script、Pythonの場合は、Python Debugger: Current Fileと表示されていればOKです。

この状態で再生ボタン、もしくはfn + F5キーを押すと、デバックが開始されます。
VSCodeの右側、もしくは下側にコンソールが表示され、10と出力結果が表示されるかと思います。コンソールが表示されない場合は、option + command + Bを押すと表示されるかと思います。

ここまでは、通常と変わらず、ただコードが実行されただけですが、ブレイクポイントを設置することで、詳細なデバックが可能となります。
ブレイクポイントの設置方法は、行数の左側をクリックすることで設置する事ができます。もしくは設置したい行数を選択した状態でfn + F9キーを押すことで設置することができます。では、resultを定義している行にブレイクポイントを設置しましょう。
設置すると、行数の左側に赤丸が表示されるかと思います。

breakpoint.jpg

この状態でfn + F5キーを押し、デバックを開始すると、ブレイクポイントで処理が中断していることが分かります。
左側を見ると、その時点で定義されている変数を確認することができます。
そして、上部に以下画像の様なツールバーが表示されているかと思います。

  • Rubyの場合

ruby_tool.jpg

  • Pythonの場合

python_tool.jpg

上記のボタンについて、共通のボタンについて説明していきます。


続行(fn + F5キー)

continue.jpg

次のブレイクポイントまで処理を進めます。
puts・printの行にブレイクポイントを設置しましょう。
現在resultの行にて処理が止まっているかと思いますが、fn + F5キーを押すと、puts・printの行で処理が止まります。再度fn + F5キーを押すと、処理が最後まで実行されるかと思います。

debug_continue.gif

ステップオーバー(fn + F10キー)

step_over.jpg

停止位置の処理を行い、次の行に移動します。resultの行にて処理が止まっている状態で、fn + F10キーを押すと、puts・printの行で処理が止まります。

debug_stepover.gif

ステップイン(fn + F11キー)

stepin.jpg

停止位置の処理を行い、次の行に移動します。ステップオーバーとの違いは、現在の停止位置で関数やメソッドが呼び出されている場合は、関数内の最初の行で処理が止まります。resultの行にて処理が止待っている状態で、fn + F11キーを押すと、sum_of_evens関数内の最初の行にて処理が止まります。

debug_tepin.gif

ステップアウト(shift + fn + F11キー)

stepout.jpg

停止位置が関数やメソッド内の場合は、現在の関数やメソッドの実行を完了させて、呼び出し元に戻ります。ステップインを実行し、sum_of_evens関数内の最初の行にて処理が止まっている状態で、shift + fn + F11キーを押すと、resultを定義している行に移動します

Rubyの場合は、sum_of_evens関数endにフォーカスがされるかと思いますが、内部的にはsum_of_evensの処理が終わった状態で停止しています。ですので、再度ステップオーバーもしくはステップインを行うと、puts・printの行に移動します

debug_stepout.gif

再起動(command + shift + fn + F5キー)

debug_reload.jpg

デバックを最初の行からやり直します。fn + F5キーを押し、デバックを開始後、command + shift + fn + F5キーを押すと、最初の行から処理をやり直し、resultの行にて処理が止まります。

停止(shift + fn + F5キー)

stop.jpg

デバックを中断します。
fn + F5キーを押し、デバックを開始後、shift + fn + F5キーを押すと、デバックが中断されます。


では、上記のコマンドを使いこなし、今回のエラーをデバックしていきましょう。

今回はresultが期待する値にならないので、sum_of_evens関数内の処理が間違っていると分かっている状態です。
ですので、sum_of_events関数内の処理を確認していきます。
resultを定義している所にブレイクポイントを設置した状態で、デバックを開始し、ステップインして、sum_of_events関数内に入ります。

func_internal.jpg

さらにステップインをして、ループ処理内に入ります。初回のループ内処理はnum1が格納されている事が分かります。条件分岐はfalseになるので、初回のループ内処理は終わります。

loop_debug.jpg

2回目のループ内処理はnum2が格納されており、条件式はtrueとなり、条件式内の処理に入っていることが分かります。
そして、sum2が格納されていることが分かります。

loop_debug.gif

3回目のループ内処理では、条件式がfalseになるので、sumは変化しません。
4回目のループ内処理は、条件式はtrueとなり、条件式内の処理が実行されますが、この時にsumの値を確認すると4が格納されていることが分かります。

loop_debug2.gif

上記のことから、sumに加算ではなく、代入するようになっているので、sum += numと修正すれば良いことが分かりました。
ここまでは、単純なスクリプト実行の場合の方法で、
RailsやDjangoのデバックは方法が異なります。

Railsのデバック方法

※これからRailsアプリを作成する際や、既存で作成したアプリにて是非試してみてください。

Railsはdebugというgemを使用することをオススメします(Rails7以降はデフォルトでインストールされるようになっています)。
debugがインストールされていれば、通常通りrails sで起動し、
デバックしたい箇所にdebuggerを記述することで、そこで処理を止めることができます。

例えば、tasksコントローラのindexアクションにdebuggerを記述し、/tasksにアクセスします。

tasks_controller.rb
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)と表示されているかと思います。

task_list_debug.jpg

ここでは、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.jsonconfigurationsにセットすることで、デバックができます。

lunch.json
...
    {
      "type": "ruby_lsp",
      "name": "Rails Server",
      "request": "launch",
      "program": "bin/rails server"
    }
...

Dockerを使用しているRailsのデバック方法

Dockerを使用しているRailsで、gemのdebuggerを使用する場合、対話型操作が必要となるため、まずdocker-compose.ymlのRailsのコンテナに以下のオプションを追記する必要があります。

docker-compose.yml
...
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を選択、Djangomanage.pyを選択しましょう。

create_lunch_json.jpg

create_lunch_django.jpg

create_lunch_manage.jpg

そうすると、以下のような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にアクセスすると、ページは読み込み状態のままで、レンダリングされず、処理が中断し、左側で変数の確認ができます。後は、ツールバーのコマンドを使いデバックを行います。

django_debugger.jpg

Dockerを使用しているDjangoのデバック方法

まず、Dev Containersという拡張機能をインストールしましょう。

dev_containers.jpg

次に、左下のアイコンをクリックし、Add Dev Container Configuration Files...をクリックし、

add_container_file.jpg

Add configuration to workspaceを選択し、

add_configuration.jpg

From docker-compose.ymlを選択し、

from_docker_compose.jpg

後の2回は何も選択せずOKを押してください

select_feature.jpg

ignore_file.jpg

これで、.devcontainer/devcontainer.jsonファイルが作成されます。

また、.vsocode/lunch.jsonを以下のように修正しましょう。

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...を選択し、

attach_container.jpg

先ほど起動したコンテナを選択しましょう。

select_container.jpg

そうすると、新しいVSCodeウィンドウが開きます。
これはコンテナに接続して、コンテナ内をVSCodeで開いている状態です。
このVSCodeにてDjangoアプリのディレクトリを開きましょう。
左側の一番上のアイコンをクリックし、Open Folderをクリック、入力欄にDjangoアプリを配置しているディレクトリを入力して、OKを押しましょう。
Djangoアプリを配置しているディレクトリは、基本的にDockerfileのWORKDIRに配置しているかと思います。例えば、WORKDIR /appの場合は、 /app/と入力します。

select_dir.jpg

左側にDjangoアプリのディレクトリ構成が表示されていればOKです。

django_dir.jpg

次に、コンテナ内を開いているVSCodeにて、Pythonの拡張機能をインストールしましょう。

python_install.jpg

そして、コンテナ内を開いているVSCodeにて、デバックと実行をしましょう。

container_debug.jpg

これで、lunch.jsonで指定したポート番号(今回の場合は8001)にアクセスできるかと思います。
デバックする際は、Djangoのデバック方法のセクションと同じ流れで、
VSCode上にブレイクポイントを設置するのですが、注意点として、コンテナ内を開いているVSCodeにて、ブレイクポイントを設置してください。

終わりに

いかがだったでしょうか?
デバッグの方法を知っておくと、特に初学者にとってエラーによるストレスを軽減できます。また、デバッグを活用することでプログラムの挙動を深く理解することにもつながります。ぜひ積極的に活用してください!

15
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?