1. OverView
どもども、うん、アドベントカレンダーの完走賞狙いなんだ、すまない。
hexdoc見てたら、おや?デバッグ方法、こんなにあるの?と言うお話をしてみたいと思います。
IO.inspect/2
dbg/2
Pry
Breakpoints
Observer
Other tools and community
ざっと見たのですが、現時点でIO.inspect/2、Pry、Breakpointsくらいはすぐにでも抑えねば!
随時、Elixir入門を参考にさせていただいております。
https://dev.to/gumi/elixir-21--21a1
2. IO.inspect/2
2.1 hexdocで確認
hexdocでIO.inspect/2を読むところからやりましょう。
@spec inspect(item, keyword()) :: item when item: var
Inspects and writes the given item to the device.
引数で与えられたItemをDeviceに書き出してくれると書いてありますね。
こっからが面白い。(俺だけか?)
It's important to note that it returns the given item unchanged.
This makes it possible to "spy" on values by inserting an IO.inspect/2 call
almost anywhere in your code, for example, in the middle of a pipeline.
翻訳すると…
重要なのは、指定された項目を変更せずに返すことです。
これにより、例えばパイプラインの途中など、コードのほぼどこにでもIO.inspect/2呼び出しを挿入して
値を「スパイ」することが可能になります。
…"It's important"とか書かれると、期待が広がりますねぇ。
2.2 IO.inspect/2を使ってみよう。
まずは、hexdocのサンプルから行ってみますか。
重要なオプション、label:width:が出てきております。
iex(2)> IO.inspect(<<0, 1, 2>>, width: 40)
<<0, 1, 2>>
iex(3)> IO.inspect(<<0, 1, 2>>, width: 4)
<<0,
1,
2>>
width: 4にしたのは、飽くまでもサンプルとしてですが、綺麗に改行してくれるものですね。
次、オプションlabel:
iex(5)> IO.inspect(1..100, label: "a wonderful range")
a wonderful range: 1..100
1..100
ラベルを付けてくれるので、とても便利ですね。
さて、パイプの中での使い方例を見てみましょう。
[1, 2, 3] |> IO.inspect(label: "before")
|> Enum.map(&(&1 * 2))
|> IO.inspect(label: "after")
|> Enum.sum()
before: [1, 2, 3]
after: [2, 4, 6]
12
…ちゃんと、前後がわかりやすく綺麗に出てますね。
では、もう一つbinding/0と組み合わせてみましょう。
binding/0は指定されたコンテキストのバインディングをキーワードリストとして返す変数ですが、
指定されたコンテキストがnilの場合、現在のコンテキストのバインディングが返されます。
ぶっちゃけ、そのスコープの変数のbinding状況を、全部出してくれます。
iex(30)> defmodule Example do
...(30)> def show_binding do
...(30)> x = 1
...(30)> y = 2
...(30)> IO.inspect(binding(),label: "Show binding:")
...(30)> end
...(30)> end
iex(31)> Example.show_binding()
Show binding:: [x: 1, y: 2]
[x: 1, y: 2]
いちいち書かなくても、変数の状況が丸わかりですね。
これば便利、ではこの辺で…いや、もう一つちゃんとやりますから。
2.2 IO.inspect/3を使ってみよう。
h IO.inspect/3
def inspect(device, item, opts)
inspect/2のdeviceつきですね。第一引数にdeviceが加わってます。
これ、便利なんでメモしときます。
deviceをファイルに割り当てれば、記録が取れます。
{:ok, file} = File.open("example.txt", [:write])
IO.inspect(file, binding(),label: "Show binding:")
File.close(file)
実行結果はこんな感じになります
iex(20)> test=1
1
iex(21)> test2=2
2
type example.txt
Show binding:: [file: #PID<0.110.0>, test: 1, test2: 2]
と、言うわけでIO.inspect/2で遊んでみました。
…うん、ググればわかるって、まぁ、そう言わずに。