Ruby の 定番対話ツール pry 徹底攻略 | 番外編 攻略記事作成中に見つけた pry のバグのプルリクエスト作成への道 #pry #ruby

# Ruby の 定番対話ツール pry 徹底攻略 | :bug::boom::hammer: 番外編 攻略記事作成中に見つけた pry のバグのプルリクエスト作成への道
## <i class="fa fa-cubes" style="font-size:1em;"></i> 概要
攻略記事作成中に見つけた pry のバグのプルリクエスト作成への道

## <i class="fa fa-history" style="font-size:1em;"></i> 経緯
ここ最近 pry の基本操作に関する連載記事を作成しています。
[Ruby の 定番対話ツール pry 徹底攻略](http://qiita.com/tbpgr/items/12a60f2f9e3de61517b5)

履歴に関する記事を作成した際に、 pry のバグを踏みました。
[Ruby の 定番対話ツール pry 徹底攻略 | Managing History](http://qiita.com/tbpgr/items/077681dff1aac0bfd708)

具体的にはこんな感じのエラーになります。

~~~bash
$ pry
[1] pry(main)> "hoge"
=> "hoge"
[2] pry(main)> hist --clear
History cleared.
[3] pry(main)> hist
ArgumentError: negative array size
from /home/user/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/pry-0.10.1/lib/pry/commands/hist.rb:171:in `last'
~~~

## <i class="fa fa-search" style="font-size:1em;"></i> 原因調査
### エラー行を確認
hist.rb の 171 行目がエラーというログに従い、該当行を確認。

エラーメッセージの ArgumentError: negative array size と合わせて判断すると
`Pry.history.session_line_count` がマイナスを返しているようです。
※ Array.last は負の値を受け取ると ArgumentError になる

~~~ruby
    def find_history
      h = if opts.present?(:all)
            Pry.history.to_a
          else
            # ここが 171 行目
            Pry.history.to_a.last(Pry.history.session_line_count)
          end
      # The last value in history will be the 'hist' command itself.
      Pry::Code(h[0..-2])
    end
~~~

## <i class="fa fa-code" style="font-size:1em;"></i> Pry.history.session_line_count のソースコードを確認
さて、はじめてのコードベースを探索する必要があるようです。
つまり ***pry*** の出番です。
***pry*** のバグ修正に ***pry*** を活用します。

`Pry.history.session_line_count` のソースコードを確認します。

~~~
[1] pry(main)> show-source -l Pry.history.session_line_count

From: /path/to/gems/pry-0.10.1/lib/pry/history.rb @ line 70:
Owner: Pry::History
Visibility: public
Number of lines: 3

70: def session_line_count
71:   @history.count - @original_lines
72: end
[2] pry(main)>
~~~

## <i class="fa fa-code" style="font-size:1em;"></i> Pry のインスタンス変数の内容を確認
`Pry.history.session_line_count` と `Pry.history.original_lines` の値を
バグを再現する操作の中で確認してみます。

~~~
$ pry
[1] pry(main)> "hoge"
=> "hoge"
[2] pry(main)> hist
1: "hoge"
[3] pry(main)> hist --clear
History cleared.
[4] pry(main)> hist
ArgumentError: negative array size
from /home/user/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/pry-0.10.1/lib/pry/commands/hist.rb:171:in `last'
[4] pry(main)> Pry.history.original_lines
=> 693
[5] pry(main)> Pry.history.session_line_count
=> -690
~~~

## :bug: 原因判明
`hist --clear` は履歴をクリアする機能です。
履歴には、

* 以前の履歴 （ .pry_history で管理）
* カレントセッションの履歴

があり、それぞれ

* 以前の履歴  => @original_lines
* カレントセッションの履歴  => @history

に紐付いています。

`hist --clear` 時に、以前の履歴にあたる `@original_lines` の値をクリアしていないため、
`@original_lines` の行数分だけマイナスになってしまっているのです。
そのため、 `hist --clear` 時に `@original_lines` もクリアする処理を実装します

## <i class="fa fa-code-fork" style="font-size:1em;"></i> pry をフォーク
GitHub の <i class="fa fa-external-link" style="font-size:1em;"></i>[pry/pry のリポジトリ](https://github.com/pry/pry) から
プロジェクトをフォークします。

## <i class="fa fa-github" style="font-size:1em;"></i> フォークしたプロジェクトをローカル環境に clone します
~~~bash
$ git clone git@github.com:tbpgr/pry.git
~~~

※実際は ghq を使ってます

~~~bash
$ ghq get git@github.com:tbpgr/pry.git
~~~

## <i class="fa fa-code-fork" style="font-size:1em;"></i> トピックブランチの作成
説明的な名前のブランチを作って、該当ブランチで作業をします。

~~~bash
$ git checkout -b fix_hist_clear_negative_array_size
~~~

## <i class="fa fa-flask" style="font-size:1em;"></i> TDD で直す。エラーのテストケースを追加
まずはエラーを再現させるテストコードを追加します。
`pry --clear` の処理本体である `Pry::History#clear` のテストケースに original_lines が
0 になっていることを確認するテストケースを追加します。

~~~ruby
    it "clears this session's history" do
      Pry.history.to_a.size.should > 0
      Pry.history.clear
      Pry.history.to_a.size.should == 0
      # この1行を追加
      Pry.history.original_lines.should == 0
    end
~~~

* テストがエラーになることを確認します

~~~
  1) Pry#clear clears this session's history
     Failure/Error: Pry.history.original_lines.should == 0
       expected: 0
            got: 6 (using ==)
     # ./spec/history_spec.rb:56:in `block (3 levels) in <top (required)>'
~~~

## <i class="fa fa-flask" style="font-size:1em;"></i> TDD で直す。テストケースをパスする実装を行う
テストをパスするように `Pry::History#clear` を実装します。

~~~ruby
    # Clear this session's history. This won't affect the contents of the
    # history file.
    def clear
      @clearer.call
      # この 1 行を追加
      @original_lines = 0
      @history = []
    end
~~~

テストを実行します

~~~
$bundle exec rspec spec/history_spec.rb
Finished in 0.07801 seconds (files took 0.39155 seconds to load)
14 examples, 0 failure
~~~

直りました。

## <i class="fa fa-github-square" style="font-size:1em;"></i> 修正内容を push します

~~~bash
$ git push origin fix_hist_clear_negative_array_size
~~~

## <i class="fa fa-github-square" style="font-size:1em;"></i> ブラウザでプルリクエストを作成します

* バグの原因の再現
* バグの原因
* バグ修正後の操作例

をソースコードの例示を主に説明しました。
英語力が低いのを補う手法。
ついでに emoji も織り交ぜたり。

![pry_bug_fix1.png](https://qiita-image-store.s3.amazonaws.com/0/43480/53c2a5a0-4f41-111e-c812-aab5800adf47.png)
![pry_bug_fix2.png](https://qiita-image-store.s3.amazonaws.com/0/43480/a7a0f9c9-9567-cc49-90e7-a67d5de01954.png)

以上でプルリクエストが完了しました。

https://github.com/pry/pry/pull/1340