u16suzuの blog

日々学んだことのメモブログです。

gemのソースを pry-byebug を使ってブレークポイントで止めて読む準備

いつも仕事でもプライベートでも使っている bundler だけど、中身のコードを把握していないのでどうも雰囲気で使っている感が否めない。

そこで、ソースを読み腹落ち感を得るためにコードを読む準備を進めてみた。

pry-byebug を使ってブレークポイントで止められるようにする

(1). システムに pry-byebugを入れる

毎回 Gemfile 書くのは面倒なのでシステムに入れちゃう

gem install pry-byebug
gem install pry-stack_explorer # 超便利なのでこれも

(2). require "pry" して、止めたいところで binding.pryすれば止まる

require "pry"

ar = [1,2,3]

ar.each do |i|
  binding.pry # ここで止まる
  p i
end

便利。

bundlerのコードで試してみる

(1). ソースを git cloneする

$ git clone git@github.com:bundler/bundler.git ~/src

/vendor/bundle に入ったコードだと git blame できなくて普通に読みづらいので、git clone したり gem-src を使うなりして git repository を取得した方が良い。

(2). CUI ツールのエントリポイントであるbundler/exe/bundle でまずは require "pry"する

#!/usr/bin/env ruby
# frozen_string_literal: true

require 'pry'
(略

(3). 実行して止まるか確認

./bundler で実行すればシステムに入っている方ではなく git clone した方の bundler が実行できる。

$ cd /Users/suzuki_y/src/bundler/exe/
suzuki_y@MBP:~/s/b/exe$ ./bundle install                             

From: /Users/suzuki_y/src/bundler/exe/bundle @ line 30 :

    25:   require "bundler/cli"
    26:
    27:   binding.pry
    28:
    29:   # Allow any command to use --help flag to show help for that command
 => 30:   help_flags = %w[--help -h]
    31:   help_flag_used = ARGV.any? {|a| help_flags.include? a }
    32:   args = help_flag_used ? Bundler::CLI.reformatted_help_args(ARGV) : ARGV
    33:
    34:   Bundler::CLI.start(args, :debug => true)
    35: end

[1] pry(main)>

無事止まりました。

show-stackでコールスタックもみれます。

[1] pry(main)> show-stack

Showing all accessible frames in stack (9 in total):
--
=> #0  <main>
   #1 [block]   block in run <Byebug::PryProcessor#run(&_block)>
   #2 [method]  run <Byebug::PryProcessor#run(&_block)>
   #3 [method]  resume_pry <Byebug::PryProcessor#resume_pry()>
   #4 [method]  at_line <Byebug::PryProcessor#at_line()>
   #5 [method]  at_line <Byebug::Context#at_line()>
   #6 [block]   block in <main>
   #7 [method]  with_friendly_errors <Bundler.with_friendly_errors()>
   #8 [main]    <main>

あとは、コードを好きにいじりながら自由にコード読み放題です。

メソッド系のgemの場合

また、 bundlerのようなコマンド系の gem ではなく、tapp gem のようにコード内で使用するメソッド系の gem は、

gemのルートディレクトリに main.rb みたいな適当なファイルを作って、そこから対象のgemを require して呼び出しを行ってとっかかりを作ります。

# src/tapp/main.rb
require "./lib/tapp"
require "pry"

binding.pry

"123".tapp
"123".taputs
From: /Users/suzuki_y/src/tapp/main.rb @ line 6 :

    1: require "./lib/tapp"
    2: require "pry"
    3:
    4: binding.pry
    5:
 => 6: "123".tapp
    7: "123".taputs

[1] pry(main)> step

From: /Users/suzuki_y/src/tapp/lib/tapp.rb @ line 13 Tapp.config:

    12: def config
 => 13:   @config ||= Tapp::Configuration.new
    14: end

[1] pry(Tapp):1>

いい感じですね。

RubyMineを使ったやり方もあとで追記したいと思います。