2011年 12月 26日
Pryは結構前からgithubのリポジトリを追いかけている人達には認知されていましたが、RailsCastsでも紹介されたことから、Ruby界で一気に広がりを見せています。
ちなみに発音はpra'i
(ぷらい)です。英単語で「覗く」などを意味します。
今回はそんな便利なPryについて少し紹介したいと思います。
Pryを一言で説明すると、irbと同様にREPL環境を提供してくれます。
では、さっそくインストールしてみましょう。
$ gem install pry pry-doc
さて、これでインストールできました。Pryを起動しましょう。
$ pry
[1] pry(main)>
なんの変哲もないプロンプトですね。irb>からpry>に変わっただけです。
では、何か計算させてみましょうか。
[2] pry(main)> ->n{ i=0;j=1;n.times{j=i+i=j};i }.call(10)
=> 55
お気づきでしょうか? 式を評価した結果が色づけされていますよね。
irbでは評価した式の値を色づけする場合、wieble等、他のgemを使う必要がありましたが、Pryではデフォルトで色づけされています。
さらに設定ファイルから、自分好みの色に設定できます。
次はメソッドを書いてみましょう。
[3] pry(main)> def foo
[3] pry(main)* ^
メソッドの定義を書いて改行すると自動でインデントされることがわかります。大変便利です。
もちろん、今まで通りirbと同じことがそのまま実現できます。
Pryはshellと統合し、shellのコマンドをPryコンソール上で実行できます。
ただ、コマンドの前に’.’を付けて実行します。
[1] pry(main)> .ls
[2] pry(main)> .pwd
[3] pry(main)> .git log --pretty=oneline
従来irbではsystem()
を使って評価していましたが、統合されたことにより煩わしさから解放されています。
また、統合されていることにより、次のようにRubyの#{}も評価して式展開してくれます。
[4] pry(main)> x = "class Hoge"
[5] pry(main)> .find . -name "*.rb" | xargs grep #{x}
組み込みコマンドのshell-mode
を使うと、プロンプトにカレントディレクトリが表示され、tabキーでディレクトリ名を補完できるようになり、よりファイル操作が簡単になります。
[6] pry(main)> shell-mode
pry main:/Users/yoppi $ shell-mode
[8] pry(main)>
shell-modeを抜けるには、再度shell-mode
コマンドを実行します。
Pryにはいくつか組み込みコマンドが用意されています。
コマンドの一覧を見るにはPryコンソール上でhelp
コマンドを実行してみましょう。
[1] pry(main)> help
以下に便利な組み込みコマンドの一部を紹介します。
やはりよく使うコマンドはhist
とls
でしょうか。
Pry独自の概念として、オブジェクト間を移動できます。
デバッガのフレームに似ています。
lsコマンドで現在のオブジェクトの一覧を表示し、cdコマンドでそのオブジェクトに移動できます。
[1] pry(main)> a = "foo"
[2] pry(main)> ls
locals: _ _dir_ _ex_ _file_ _in_ _out_ _pry_ a version
[3] pry(main)> cd a
[4] pry("foo"):1> nesting
Nesting status:
--
0. main (Pry top level)
1. "foo"
[5] pry("foo"):1> ls
Comparable methods: < <= > >= between?
String methods: ...
[6] pry("foo"):1> upcase
=> "FOO"
この機能のおかげで、とある深くネストしたオブジェクトの調査もcdして潜っていけば、調査する範囲を狭めることができるので大変便利ですね。
Rubyのコードを書いていてある程度の量になってくると、個々のモジュールはRSpecによるユニットテストにがっちり守られていても、やはり、ソフトウェアを開発する以上デバッガの恩恵を受けたいものです。
往々にして、個々のモジュールを結合したときに、予想できなかった問題が発生するものだからです。
原因をすばやく追及するために、デバッガはプログラマにとって必要不可欠なツールでしょう。
実は、Pryの真の機能はREPLではなく、この先ほどのオブジェクトの調査機能にあるようにデバッガとしての機能によるものが多いです。
では、例としてRailsアプリを作っているときにデバッガで追いかけたいとしましょう。
適当なコントローラのアクションで次のコードを差し込みましょう。
binding.pry
そうすると、ここがブレークポイントになり、Pryコンソールが立ち上がります。
つまり自分でbinding.pryを評価する式を書くので、その式を評価するかどうかは、プログラマが自由に決定できるので、
条件付きブレークポイントを自由に書けることになります。
binding.pry if expression
さて、これでRailsを起動して アプリケーションを実行してみましょう。
binding.pryを書いたところで停止するはずです。
停止すると同時にPryコンソールが起動します。
まさにデバッガのように前後のコードが表示されつつ停止していることがわかるでしょう。
停止した時点で評価されたオブジェクトにアクセスできるので、デバッグを自由に進められます。
ステップ実行したければ、binding.pry
を停止させたい箇所に埋め込みましょう。
Railsはconrollerに変更があれば再ロードするのでstep実行も簡単ですね。
普通のRubyアプリケーションであれば、edit-method
コマンドで現在のメソッド名を指定することでそのメソッドを編集でき、また、直接エディタで編集した場合reload-method
で読み込みなおすとstep実行できます。
show-method、stat等のコマンドが用意されています。
また、シェル経由でriコマンドをそのまま使用できます。
VimやEmacs使い達は、それぞれのエディタ上で素早くドキュメントを牽けるようにしているはずですが、irbで作業しているときには牽けません。
Pryではこれらの組み込みコマンドが提供されていることで、素早くドキュメントにアクセスできます
[1] pry(main)> ri Array#sample
[2] pry(main)> require 'pathname'
[3] pry(main)> show-doc Pathname#children
さて、Rubyのオブジェクトに対してメソッドを呼び出すわけですが、そのオブジェクトがどう実装されているか気になるときがあります。
特に、gemライブラリを使っているときに、APIがどんなことをしているのか素早く知りたいわけです。
[4] pry(main)> require 'nokogiri'
[5] pry(main)> show-method Nokogiri::HTML::Document.parse
From: /Users/yoppi/opt/local/ruby/ruby-1.9.3-p0/lib/ruby/gems/1.9.1/gems/nokogiri-1.4.4/lib/nokogiri/html/document.rb @ line 64:
Number of lines: 22
Owner: #<Class:Nokogiri::HTML::Document>
Visibility: public
def parse string_or_io, url = nil, encoding = nil, options = XML::ParseOptions::DEFAULT_HTML
...
また、Rubyの組み込みライブラリはCで実装されています。
よくRubyiest達はCで実装しているコードを眺めて、実装を楽しんだり、もっと改善できるところはなかな? と考えるときがあります。
Pryなら簡単に該当する組み込みライブラリのCのコードを読めます。
Cのコードを読むには、pry-doc
をインストールしておく必要があります。
たとえば、Array#sampleは同様に
[6] pry(main)> show-method Array#sample
static VALUE
rb_ary_sample(int argc, VALUE *argv, VALUE ary)
{
VALUE nv, result, *ptr;
long n, len, i, j, k, idx[10];
...
と、show-method
コマンドで牽くだけです。
これでC実装を素早く読めるので今まで実装を読まなかった人も読むようになり、Rubyの開発も活発になりそうです。
大変便利ですね。
さて、ここまでデフォルトの設定でPryを触ってきましたが、カスタマイズしてより便利にしましょう。
Pryのカスタマイズは、irbでは~/.irbrc
で設定していたように、Pryも~/.pryrc
ファイルに設定します。
Pry-wiki Customizing Pryで詳しい説明があります。
ここではデフォルト設定されているもの以外で、知っておくと便利な機能を紹介します。
組み込みコマンドに対してprefixを設定できます。この記事では直接コマンドを実行していましたよね。
しかし、そのコマンド名を変数名に使いたい場合があると困ることになります。
そこで、prefixを設定することで競合を避けられます。
Pry.config.command_prefix = "%"
と設定すると、コマンドを実行するときには’%’を付けて実行しなければ評価されないようになります。
[1] pry (main)> ls
NameError: undefined local variable or method `ls' for main:Object
[2] pry (main)> %ls
locals: _ _dir_ _ex_ _file_ _in_ _out_ _pry_ a version
僕は常にいくつかのRubyの版を切り替えて使っているので、irbを使用していたときもプロンプトにRubyのバージョンを表示させていました。
Pryでもプロンプトを柔軟にカスタマイズできるようになっています。
デフォルトのプロンプトは次のように設定されています。
DEFAULT_PROMPT = [
proc { |target_self, nest_level, pry|
if nest_level == 0
"[#{pry.input_array.size}] pry(#{Pry.view_clip(target_self)})> "
else
"[#{pry.input_array.size}] pry(#{Pry.view_clip(target_self)}):#{nest_level}> "
end
},
proc { |target_self, nest_level, pry|
if nest_level == 0
"[#{pry.input_array.size}] pry(#{Pry.view_clip(target_self)})* "
else
"[#{pry.input_array.size}] pry(#{Pry.view_clip(target_self)}):#{nest_level}* "
end
}
]
ではデフォルトのプロンプトに加えて、Rubyの版を表示させるように設定してみましょう。
Pry.config.prompt = [
proc {|target_self, nest_level, pry|
nested = (nest_level.zero?) ? '' : ":#{nest_level}"
"[#{pry.input_array.size}] #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}(#{Pry.view_clip(target_self)})#{nested}> "
},
proc {|target_self, nest_level, pry|
nested = (nest_level.zero?) ? '' : ":#{nest_level}"
"[#{pry.input_array.size}] #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}(#{Pry.view_clip(target_self)})#{nested}* "
}
]
pryを起動すると、
$ pry
[1] 1.9.3-p0(main)>
Rubyの版が表示されてます。やりましたね。
RailsCastsのビデオを一度視聴してみてください。Pryの使い方が一望できますし、説明もわかりやすいです。
より深く使いこなすにはドキュメントには一度目を通しましょう。ここで紹介してない機能もあります。