LoginSignup
6

More than 5 years have passed since last update.

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

Posted at

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

概要

攻略記事作成中に見つけた pry のバグのプルリクエスト作成への道

経緯

ここ最近 pry の基本操作に関する連載記事を作成しています。
Ruby の 定番対話ツール pry 徹底攻略

履歴に関する記事を作成した際に、 pry のバグを踏みました。
Ruby の 定番対話ツール pry 徹底攻略 | Managing History

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

$ 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'

原因調査

エラー行を確認

hist.rb の 171 行目がエラーというログに従い、該当行を確認。

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

    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

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)>

Pry のインスタンス変数の内容を確認

Pry.history.session_line_countPry.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 で管理)
  • カレントセッションの履歴

があり、それぞれ

に紐付いています。

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

pry をフォーク

GitHub の pry/pry のリポジトリ から
プロジェクトをフォークします。

フォークしたプロジェクトをローカル環境に clone します

$ git clone git@github.com:tbpgr/pry.git

※実際は ghq を使ってます

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

トピックブランチの作成

説明的な名前のブランチを作って、該当ブランチで作業をします。

$ git checkout -b fix_hist_clear_negative_array_size

TDD で直す。エラーのテストケースを追加

まずはエラーを再現させるテストコードを追加します。
pry --clear の処理本体である Pry::History#clear のテストケースに original_lines が
0 になっていることを確認するテストケースを追加します。

    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)>'

TDD で直す。テストケースをパスする実装を行う

テストをパスするように Pry::History#clear を実装します。

    # 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

直りました。

修正内容を push します

$ git push origin fix_hist_clear_negative_array_size

ブラウザでプルリクエストを作成します

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

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

pry_bug_fix1.png
pry_bug_fix2.png

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

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
6