Pry上でRailsアプリのコードを読むために使える知識まとめ

  • 16
    いいね
  • 1
    コメント

Ruby on Rails 製のソフトウェアのソースを追う時に、できるだけPryを離れずに疑問を解決したいと思いました。疑問はすべてPry上で解決できるのが理想です。

そこで、Pry上でRailsアプリのコードを読むために使える知識をまとめておくことにしました。

Pryとは

REPL(Read-Eval-Print-Loop)です。pry-railsというgemを使えば、Pry上にそのRails製ソフトウェアの環境が立ち上がります。環境という言い方がいいのかよくわかりませんが、要は外部ライブラリやらモデル、コントローラ、ヘルパ、自作モジュールなど全てのコードを参照できる状態でいろいろいじくくれるということです。

show-*

なにかを見る系のコマンド。

show-model

[1] pry(main)> show-model User

とかするとモデルが持つアトリビュートとかリレーションとかが見れます。

show-models

モデル一覧。

pry(main)> show-models --grep User

初めてみるRailsプロジェクトで、ユーザー関係の情報とりたいけどモデル名わからんよというときに--grepと組み合わせてみるととても使える。

show-method

[1] pry(main)> show-method ActionController::MimeResponds#resond_to

指定したクラス#インスタンスメソッドのソースが見れます。上の例だとこんな感じです。

pry
Owner: ActionController::MimeResponds
Visibility: public
Number of lines: 8

def respond_to(*mimes, &block)
  raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?

  if collector = retrieve_collector_from_mimes(mimes, &block)
    response = collector.response
    response ? response.call : render({})
  end
end

find-method

mainのコンテキストからクラス名#インスタンスメソッドのように指定すればソースが見れますが、メソッドがどこで定義されているかわからないときもあります。そんな時にはfind-methodが役立ちます。

pry
[1] pry(main)> find-method url_for


ActionDispatch::Http::URL.url_for

ActionController::Metal
ActionController::Metal#url_for
ActionDispatch::Routing::RouteSet
ActionDispatch::Routing::RouteSet#url_for
ActionDispatch::Routing::UrlFor
ActionDispatch::Routing::UrlFor#url_for
ActionView::Helpers::UrlHelper
ActionView::Helpers::UrlHelper#url_for
ActionView::Helpers::UrlHelper::ClassMethods
ActionView::Helpers::UrlHelper::ClassMethods#_url_for_modules
ActionView::RoutingUrlFor
ActionView::RoutingUrlFor#url_for

show-source

C言語で書かれたRubyのソースも読める。

pry
[7] pry(main)> show-source String#to_i

From: string.c (C Method):
Owner: String
Visibility: public
Number of lines: 17

static VALUE
rb_str_to_i(int argc, VALUE *argv, VALUE str)
{
    int base;

    if (argc == 0) base = 10;
    else {
        VALUE b;

        rb_scan_args(argc, argv, "01", &b);
        base = NUM2INT(b);
    }
    if (base < 0) {
        rb_raise(rb_eArgError, "invalid radix %d", base);
    }
    return rb_str_to_inum(str, base, FALSE);
}

show-routes

bundle exec rake routes (ルーティングをみるコマンド)相当の出力が得られる。pry上でやるので早い。

show-routes --grep users

とかしてgrepと組み合わせて使うことがほとんどだと思う。

show-doc

ドキュメントもみれる。

pry
[6] pry(main)> show-doc Enumerable#take

From: enum.c (C Method):
Owner: Enumerable
Visibility: public
Owner: Enumerable
Visibility: public
Visibility: public
Signature: take(arg1)
Number of lines: 5

Returns first n elements from enum.

   a = [1, 2, 3, 4, 5, 0]
   a.take(3)             #=> [1, 2, 3]
   a.take(30)            #=> [1, 2, 3, 4, 5, 0]

デバッグ

デバッグもできる。

binding.pry

pry-byebug というgemを使うと、コードに挿入して実行するとそこで止まってpryからいろいろみれる。

...
def index
  @user = User.find params[:id]
  binding.pry
end

cd でクラス内部に入ってから show-method

cdでクラスの内部に入れる。

たとえばPostモデルというのがあったとして、その中で呼ばれている何かしらのメソッド定義を知りたい。だけどそれがどこで定義されているのかがわからないという問題がよくある。

こういうときに、クラス内部へcdで入ってからshow-methodでメソッドの定義を調べるということができる。

pry(main)> cd Post
pry(Post):1> show-method belongs_to

From: /Users/foo/bar/vendor/bundle/ruby/2.2.0/gems/activerecord-4.1.14/lib/active_record/associations.rb @ line 1432:
Owner: ActiveRecord::Associations::ClassMethods
Visibility: public
Number of lines: 4

def belongs_to(name, scope = nil, options = {})
  reflection = Builder::BelongsTo.build(self, name, scope, options)
  Reflection.add_reflection self, name, reflection
end

クラスの内部に...と書いたけど、オブジェクトに入れる。

pry(main) user = User.find 1
pry(#<Model::User>):1> cd user

これでほぼなんでも見れる(はず)。

.rails s

.をつけると普通のシェルコマンドも実行できるらしい。

なので、ちょっとサーバー立ち上げようとか、rspec走らようみたいなことをpry停止せずにできそう。

Pryに自作コマンドを定義する方法

Pryは素でも機能が充実しています。しかし、時折Pryを停止したり離れたりしてからRailsアプリに対する作業を行っているときに、「あれ、これPryからできたら嬉しいんじゃ...」と思うことがあります。そういうものに関しては自作コマンドを定義したいですよね。定義しましょう。

といっても簡単で、.pryrcというコマンド定義のファイルをプロジェクト直下に置くだけです。

.pryrc
command_set = Pry::CommandSet.new do
command "hello", "ハロー" do |name|
  puts "hello, #{name}"
end
Pry.config.commands.import command_set
pry
[1] pry(main)> hello :gaaamii
hello, gaaamii

その他

Helperのメソッド呼びたい

helper.でいける。

@kzy52 さんに教えてもらいました。

余談だけど、helpersでアクセスしようとして「うまくいかね〜な〜」ということが何度もあった。

補足: 日本語文字化け問題

今後書き足したいこと

  • ある程度実用性のある自作コマンドを作ってサンプルとして置く
  • 活用例の紹介
  • gifを足してわかりやすくする

参考