もう puts/p をデバッグに使わない！ デバッグライブラリ IceCream の Ruby 版

先日、Python 向けのデバッグライブラリ [IceCream](https://github.com/gruns/icecream) が[話題になっていました。](https://qiita.com/purun/items/c7aca300b970344214cf)

Python 以外にもいくつかの言語版があるようですが、残念ながら Ruby 版が無い。というわけで作ってしまいました。

[Ricecream (icecream-ruby)](https://github.com/nodai2hITC/ricecream)

# はじめに

まず、このライブラリはいわゆる「print デバッグ」[^printdebug]の代替となるものです。

[^printdebug]: 実際には Ruby の場合 `print` ではなく `puts` や `p` を使うでしょうが、そうした標準の出力メソッドを使ってデバッグを行うことの総称をここでは「print デバッグ」と呼ぶことにします。

そもそも本格的にデバッグを行う場合、print デバッグではなく Byebug 等のデバッガを用いるべきです。

参考：[printデバッグにさようなら！Ruby初心者のためのByebugチュートリアル](https://qiita.com/jnchito/items/5aaf323ab4f24b526a61)

とはいえ print デバッグが非常に手軽なものであることは間違いなく、ちょっとしたデバッグにはいちいちデバッガを使うより print デバッグで済ませてしまうという方も多いでしょう。

しかし print デバッグは手軽な分そのままでは困ることも少なくありません。 print デバッグの手軽さはそのままに、もう少し便利にしてくれるライブラリが IceCream （そしてこの Ricecream ）だと思います。

# 概要

## print デバッグの困るところ

例えば、変数 `foo` の値を調べたいとします。print デバッグの場合、以下のようなコードを追加することになります。

```ruby
p foo
```

この場合、`123` のような形で `foo` の値が表示されます。

print デバッグをする箇所が 1 箇所であればこれで困りませんが、複数の変数をさまざまな場所で print デバッグしようとすると、先のコードでは出力されたのがどの変数なのか判りません。また、もともとさまざまな標準出力を行うプログラムの場合、print デバッグの出力値と本来の出力値が混ざってしまい見づらくなります。

以下のように書けば、そうした問題点を解決できます。

```ruby
puts "[debug] foo: #{foo}" # => [debug] foo: 123
```

ですが、こんなのを入力するのは面倒です。[^snipet] これではせっかくの print デバッグの手軽さを台無しにしてしまいます。

[^snipet]: 例えば VSCode であれば、スニペットを利用するなどして簡単化することもできるとは思いますが。

## Ricecream の場合

Ricecream を使えば、前者のような手軽な

```ruby
ic foo
```

というコード [^require] で、

[^require]: 省略しましたが、 `require "ricecream"` している前提です。

```
ic| foo: 123
```

と表示することができるようになります。

# 変数等の表示

上の例では単に変数の値をそのまま表示させただけですが、`ic` は任意の式を取ることができます。また、複数の引数も受け取れますし、引数が複数行にまたがっていても問題ありません。

```ruby
require "ricecream"
ic foo * 2,
   Integer.sqrt(foo)
# => ic| foo * 2: 246, Integer.sqrt(foo): 11
```

また、 `ic` は引数をそのまま戻り値にするので、既存のコードに簡単に追加できます。例えば以下のコードにおいて `foo.bar` の値を確認したいとしましょう。

```ruby
return "result = #{foo.bar}"
```

`puts` を使う場合、

```ruby
puts foo.bar
return "result = #{foo.bar}"
```

としてしまうと `foo.bar` が副作用を伴うようなメソッドだった場合、2 回呼び出されてしまうことで予期せぬ動作になる可能性があります。ですので

```ruby
foobar = foo.bar
puts foobar
return "result = #{foobar}"
```

のように書くことになるでしょう。書くのも面倒ですし、デバッグ後の修正作業も面倒です。

一方、`ic` を使えば

```ruby
return "result = #{ic foo.bar}"
```

と簡単に書けます。

…とはいえ `p` や `pp` も同様に引数を戻り値にするので、この点に関してはあまり Ricecream の優位性はありません。

# 実行の流れを調べる

以下のようなメソッドが、`foo` に到達しなかったとします。

```ruby
def mymethod
  return if cond1?
  return if cond2?
  return if cond3?
  foo
end
```

このとき print デバッグでは以下のように様々な出力値の `p` をあちこちに追加することで、どこで return したのかを調べることになるでしょう。例えば下の例で「1」「2」が出力されたのであれば、`return if cond2?` のところで return したことが分かります。…しかし、出力値を一つ一つ変えて入力する手間がかかりますし、どこで出力された値なのかが直感的には判りません。

```ruby
def mymethod
  p 1
  return if cond1?
  p 2
  return if cond2?
  p 3
  return if cond3?
  p 4
  foo
end
```

一方、以下のように引数無しで `ic` を使えば

```ruby
def mymethod
  ic
  return if cond1?
  ic
  return if cond2?
  ic
  return if cond3?
  ic
  foo
end
```

```plaintext:output
ic| script.rb:2 in mymethod at 23:59:52.822
ic| script.rb:4 in mymethod at 23:59:52.822
```

のように、どのスクリプトの何行目が実行されたのかが判りやすく表示されます。

# 限定されたスコープで ic を使う

Ricecream は Kernel を拡張してしまうので、使うのが不安だという方もいるかもしれません。

そんな方のために、Refinements 版もあります。

```ruby
require "ricecream/refine"

using Ricecream
ic
```

また、`using Ricecream::P` すれば `p` をオーバーライドできます。

```ruby
require "ricecream/refine"

using Ricecream::P
foo = 123
p foo # => ic| foo: 123
```

# ic の表示を消す

print デバッグが終わった際に、うっかり追加した `p` を消し忘れてしまうことってありませんか？特に普段は実行されない部分に追加したところとか。

もちろん Ricecream を使った場合も `ic` を消し忘れる可能性はあります。ですが `Ricecream.disable` を実行しておけば、万が一消し忘れたとしても出力がされなくなるので安心です。

```ruby
require "ricecream"
ic # => 出力される
Ricecream.disable
ic # => 出力されない
Ricecream.enable
ic # => 出力される
```

# その他いろいろ

## prefix を変更

prefix （デフォルトでは `ic| ` ）を変更できます。

```ruby
Ricecream.prefix = "ricecream| "
```

メソッド `Ricecream.prefix` を定義することで、より柔軟な変更ができます。

```ruby
def Ricecream.prefix(location)
  "ic #{Time.now}| "
end
```

## 出力先の変更

デフォルトでは出力先は STDERR ですが、これも変更できます。

```ruby
  Ricecream.output = STDOUT
```

```ruby
  def Ricecream.output(str)
    @log ||= Logger.new("logfile.log")
    @log.debug(str)
  end
```

## 出力方法の変更

デフォルトでは `Object#inspect` （つまり `p` 相当）を用いて値を出力しますが、`Ricecream.arg_to_s` を定義することで変更できます。

```ruby
  def Ricecream.arg_to_s(arg)
    PP.pp(arg, +"", 60)
  end
```

## 呼び出し元情報を表示

`Ricecream.include_context = true` すると、`ic` を呼び出した場所の情報を付けて表示します。

```ruby
  ic foo # => ic| foo: 123
  Ricecream.include_context = true
  ic foo # => ic| script.rb:10 in <main>- foo: 123
```

## 色付け（実験的機能）

`Ricecream.colorize = true` すると、色付きで表示します。ただし irb 1.2 以上が必要です。

# 対応できない場合

以下のような場合、`ic` は適切な表示ができません。

## 同じ行に、引数の数が同じ ic

```ruby
ic foo; ic bar
# => ic| foo: 123
# => ic| foo: 456
#         ↑ bar なのに…
```

のように同じ行に引数の数が同じ ic があると、正しい表示ができません。

引数の数が異なれば問題ないので、無理やり引数を増やしてしまえば大丈夫です。

```ruby
ic foo; ic bar, 1
# => ic| foo: 123
# => ic| bar: 456, 1
```

## eval 中で ic

`eval` 中の `ic` には対応できません。以下のように、とりあえず引数を `<arg1>` のように表示します。

```ruby
eval("ic foo") # => ic| (eval):1 in <main>- <arg1>: 123
```

## 引数展開

`ic` に引数を展開して与えると対応できません。しかし、そもそもそんな行為に何の意味があるのでしょうか？

```ruby
ic *[1, 2, 3] # => ic| script.rb:4 in <main>- <arg1>: 1, <arg2>: 2, <arg3>: 3
```

以上のように対応できない場合はありますが、レアケースだと思うので大抵の状況には対応できるはずです。

# まとめ

デバッガの使い方を覚えようと思いつつ ついつい面倒で print デバッグしてしまう方は、この Ricecream を使ってみてはいかがでしょうか？