Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Ruby との対話 : pry を使い pry をデバッグし pry のバグを直す話

zaru
November 29, 2018

Ruby との対話 : pry を使い pry をデバッグし pry のバグを直す話

zaru

November 29, 2018
Tweet

More Decks by zaru

Other Decks in Technology

Transcript

  1. edit の使い方 $ pry [1] pry(main)> 100 * 10 =>

    1000 [2] pry(main)> edit edit と打つと直前に入力した内容をエディタで開いて編集できる。エ ディタの指定は $EDITOR もしくは Pry.config.editor で指定可能。
  2. 例えば ! を使う ! で入力バッファをクリアできる。すべてを無にしたい性格の人向け。 [10] pry(main)> class Mu [10]

    pry(main)* def kyomu [10] pry(main)* ! Input buffer cleared! 入力中のものを修正したい場合は amend-line か ! を使う。
  3. 例えば edit --in を使う edit --in は過去に入力したものを指定して編集可能。 [2] pry(main)> class

    RubyLT [2] pry(main)* def start [2] pry(main)* end [2] pry(main)* end => :start [3] pry(main)> edit --in 2 これで class RubyLT 自体の編集が可能。edit で編集したものはファイ ルに保存され、その場でリロードされる。便利。
  4. edit のオプション いくつかあるが、これだけ覚えておけば大丈夫。 edit : 直前に INPUT したものを編集 edit sample.rb

    : 指定ファイルを編集 edit MyClass#my_method : 指定クラスの指定メソッドを編集 edit --temp : 新規に入力 edit --in n : 指定番目に INPUT したものを編集
  5. 発端 何回もコードを実行して、ふと前に定義したメソッドを編集したくなっ た時。 [2] pry(main)> class RubyLT [2] pry(main)* def

    start [2] pry(main)* end [2] pry(main)* end . . . [113] pry(main)> [114] pry(main)> edit --in 2 2 番目を指定するも vim は空っぽ…
  6. そこで edit --in オプションの仕様を知るためにコードを確認する [2] pry(main)> edit edit def input_expression

    case opts[:i] when Range (_pry_.input_ring[opts[:i]] || []).join when Integer _pry_.input_ring[opts[:i]] || "" else raise Pry::CommandError, "Not a valid range: #{opts[:i]}" end end _pry_.input_ring というオブジェクトから取得しているっぽい
  7. Pry::Ring クラスの @buffer に配列で入力コマンドが格納されている。 @max_size という変数もあるので記録する最大値が決まっていて、その 値を超えたから、さっきの edit --in 2

    を指定しても空になったと推 測。 [5] pry(main)> _pry_.input_ring => #<Pry::Ring:0x007feb2e07ba68 @buffer= [nil, "class Hoge\n def piyo\n end\nend\n", "Hoge.new.piyo\n", "_pry_.input_ring\n @count=6, @max_size=100, @mutex=#<Thread::Mutex:0x007feb2e07ba40>>
  8. [1] pry(main)> 1 => 1 [2] pry(main)> 1 => 1

    [3] pry(main)> 1 => 1 [4] pry(main)> 1 => 1 [5] pry(main)> _pry_.input_ring.to_a => ["1\n"] 1 を4 回入力しているので @max_size が 3 の場合 ["1\n", "1\n", "1\n"] が返ってきて欲しい。
  9. オブジェクの監視には watch _pry_.input_ring の変化を知りたいので watch コマンドを使う。 [1] pry(main)> watch _pry_.input_ring

    Watching _pry_.input_ring watch: _pry_.input_ring => #<Pry::Ring:0x007fde395ab468 @buffer=[nil, ""], @count=2, @max_size=3, @mutex=#<Thread::Mutex:0x007fde395ab440>> watch で指定したオブジェクトに変更があったら、それを自動で表示し てくれる便利なやつ。
  10. 次に Pry::Ring#to_a の実装を確認する。 last_part + (@buffer - last_part) があやしい (*)

    `$` コマンドは `show-source` のエイリアス。指定クラスやオブジェクトのソースが見れる [8] pry(main)> $ Pry::Ring#to_a From: /Users/hiro/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/pry-0.12.2/lib/pry/ri Owner: Pry::Ring Visibility: public Number of lines: 6 def to_a return @buffer.dup if count <= max_size last_part = @buffer.slice(count % max_size, @buffer.size) last_part + (@buffer - last_part) end
  11. やりたい事は入力した順番通りに @buffer を並び替えたいだけ。一番最 初の入力したところから、2 つの配列に分けて前後を入れ替えて結合す るという感じ。でも配列同士の引き算は同一の要素を全部除く仕様。 [1, 1, 1] -

    [1] => [] コードをその場で修正してデバッグしてみる。 [8] pry(main)> edit Pry::Ring#to_a last_part = @buffer.slice(count % max_size, @buffer.size) last_part.concat @buffer.slice(0, count % max_size) これで OK
  12. [16] pry(main)> 1 => 1 [17] pry(main)> 1 => 1

    [18] pry(main)> 1 => 1 [19] pry(main)> 1 => 1 [20] pry(main)> _pry_.input_ring.to_a => ["1\n", "1\n", "1\n"] めでたい
  13. あとはこの配列のインデックスを使えば edit --in に利用できるはず。 [1] pry(main)> 1 => 1 [2]

    pry(main)> 2 => 2 [3] pry(main)> 3 => 3 [4] pry(main)> 4 => 4 [5] pry(main)> edit --in 0 ここでは最初に入力した 1 は消えているので 0 番目のインデックス指 定は先頭の 2 になっているはず。
  14. 次に [] の実装を確認する。 [6] pry(main)> $ Pry::Ring#[] From: /Users/hiro/Sites/pry/lib/pry/ring.rb @

    line 55: Owner: Pry::Ring Visibility: public Number of lines: 11 def [](index) @mutex.synchronize do return @buffer[(count + index) % max_size] if index.is_a?(Integer) return @buffer[index] if count <= max_size # Swap parts of array when the array turns page and starts overwriting # from the beginning, then apply the range. last_part = @buffer.slice([index.end, max_size - 1].min, count % max_size) (last_part + (@buffer - last_part))[index] end end
  15. まとめ デバッグには edit が便利 監視には watch が便利 $ cd ls

    辺りは普通に便利 edit --in をもっと活用するためには、もうひと工夫必要 もし時間があれば紹介
  16. 入力内容の記録は hist と input_ring の2種類 hist コマンドは入力改行ごとに番号が入る。つまり edit --in には使え

    ない番号。 [124] pry(main)> class Hoge [124] pry(main)* def piyo [124] pry(main)* end [124] pry(main)* end => :piyo [125] pry(main)> hist 1: hist 2: class Hoge 3: def piyo 4: end 5: end
  17. _pry_.input_ring というオブジェクトは、1 回の入力が1 つの値になって いる。つまり複数行でも1 つ。 この @buffer を hist

    のように表示すれば OK [1] pry(main)> class Hoge [1] pry(main)* def piyo [1] pry(main)* end [1] pry(main)* end => :piyo [2] pry(main)> Hoge.new.piyo => nil [3] pry(main)> _pry_.input_ring => #<Pry::Ring:0x007feb2e07ba68 @buffer=[nil, "class Hoge\n def piyo\n end\nend\n", "Hoge.new.piyo\n", "_pry_.input @count=4, @max_size=100, @mutex=#<Thread::Mutex:0x007feb2e07ba40>>
  18. 雑に hist コマンドを編集する。 [1] pry(main)> edit hist Pry::Code が番号表示用のクラスなので、そこに input_ring

    配列をつっ こむだけ。 opt.on :i, :'input-numbers', "Show input numbers" @history = if opts.present?(:'input-numbers') Pry::Code.new(_pry_.input_ring.to_a.slice(1..-1), 0) else find_history end
  19. [16] pry(main)> def hoge [16] pry(main)* 'hoge' [16] pry(main)* end

    => :hoge [17] pry(main)> 100 * 10 => 1000 [18] pry(main)> hist -i 0: def hoge 'hoge' end 1: 100 * 10 [19] pry(main)> edit --in 0 幸せ