久々の更新。
もうちょっと筆まめにならないと。
今日は、Shibuya.rbに参加してきました。
今回のメインのテーマはRackミドルウェアを読むってことで、
各自、読みたいミドルを選んで、テーブルに分かれて、
それぞれ読んでみるという感じです。
私は、Rack::Auth::Basicを読んでました。
シンプルですぐに完結しているので読みやすい感じだったと思います。
私のテーブルでは、皆Macだったので、画面共有を使って、
同じ画面で一緒にソースを見て、順に追っていくという感じで、読んでみました。
テーブルごとにモニタがあれば、一緒に読む時にはかなり捗るんだろうけど。
中盤にLT枠があったので、突発で資料も無しにPryについて話をさせてもらいました。
テーマがソースコードリーディングだったので、、
最近、ソースコードを読んだり、Railsでspec書いたりする時に、
これは便利だ!と感じたことを話すいい機会だと思ったわけです。
というわけで、ざっくりと話した内容について書いてみます。
pryを起動したら、まずはhelpと打ってみましょう。
ずらずらとコマンドが出てきます。
この中で、メインで活用したいコマンドは以下です。
- cd: 引数で指定したオブジェクトのコンテキストに移動する
- ls: 自分が今居るコンテキストで参照できるメソッド、変数を一覧で見る
- show-method: 引数で指定したメソッドの定義されているコードを見る。(ファイル、行数も分かる)
- edit: エディタでpryに入力するコードを記述できる。
- edit-method: 引数で指定したメソッドを定義しているファイルをエディタで開く。
cdとlsはこんな感じで使えます。
[5] pry(main)> a=[] => [] [6] pry(main)> cd a [7] pry(#<Array>):1> ls Enumerable#methods: all? any? chunk collect_concat detect each_cons each_entry each_slice each_with_index each_with_object entries find find_all flat_map grep group_by inject max max_by member? min min_by minmax minmax_by none? one? partition reduce slice_before sort_by Array#methods: & * + - << <=> == [] []= assoc at clear collect collect! combination compact compact! concat count cycle delete delete_at delete_if drop drop_while each each_index empty? eql? fetch fill find_index first flatten flatten! frozen? hash include? index insert inspect join keep_if last length map map! pack permutation place pop pretty_print pretty_print_cycle product push rassoc reject reject! repeated_combination repeated_permutation replace reverse reverse! reverse_each rindex rotate rotate! sample select select! shelljoin shift shuffle shuffle! size slice slice! sort sort! sort_by! take take_while to_a to_ary to_s transpose uniq uniq! unshift values_at zip | self.methods: __binding_impl__ locals: _ _dir_ _ex_ _file_ _in_ _out_ _pry_
(実際には戻り値には色が付いてます。)
こんな感じで、配列のインスタンスにコンテキストに入ってlsを実行すると、
配列が持っているインスタンスメソッドや、インクルードしているモジュールで定義されているメソッド、
他にインスタンス変数なんかも参照できます。
また、ls -vを使うと、スーパークラスも含めてメソッドを参照できます。
irbで、pp hoge.methods.sortとかやることがあると思いますが、
pryのコマンドは定義されているコンテキストごとにまとまって表示されるため、非常に見やすいところがいい感じです。
show-methodはこんな感じで使います。
[3] pry(User):1> show-method database_authenticatable From: /Users/joker/pasokara_player_rails3/vendor/bundle/ruby/1.9.1/gems/devise-2.0.0/lib/devise/schema.rb @ line 16: Number of lines: 8 Owner: Devise::Schema Visibility: public def database_authenticatable(options={}) null = options[:null] || false default = options.key?(:default) ? options[:default] : ("" if null == false) include_email = !respond_to?(:authentication_keys) || self.authentication_keys.include?(:email) apply_devise_schema :email, String, :null => null, :default => default if include_email apply_devise_schema :encrypted_password, String, :null => null, :default => default, :limit => 128 end
このように、メソッドが定義されているファイル、行数と定義内容が参照できます。
これは、MongoidをインクルードしているUserクラスに入り、deviseの認証定義をshow-methodしたところです。
この参照結果だけ見て、内容が全部分かることはあまり無いと思いますが、
ざっくりとした動作イメージの把握はできますし、
定義場所が明確に分かるので、細かく知りたい時は、コードを読む起点として活用できます。
ここで、edit-methodを使って、database_authenticatableを引数に渡すと、
環境変数EDITORで指定しているエディタでこの定義場所を開くことができます。
このまま付近を読んでもいいし、そのまま閉じれば、pryに戻ってくることができます。
editはpryで実行するrubyのコードをそのままエディタで記述して処理できるもので、
irbにおけるinteractie_editorと同様なので、詳しい説明は省きます。
これだけでもかなり便利なのですが、
pryには更に強力な機能があります。
今回、Rackミドルウェアを読む際にも活用しましたが、
コード中にbinding.pryと記述することで、
そのタイミングでインタラクティブモードに移行して
pry上でコードを実行できるようになります。
今回だとこんな感じ。
require "rubygems" require "rack" require "pry" class Hello def call(env) binding.pry [200, {"Content-Type" => "text/plain"}, ["Hello World"]] end end use Rack::Auth::Basic do |user, pass| user == "test" && pass == "password" end run Hello.new
rackupしてアクセスしてみるとこうなります。
[2012-02-16 02:02:52] INFO WEBrick 1.3.1 [2012-02-16 02:02:52] INFO ruby 1.9.3 (2011-10-30) [x86_64-darwin10.8.0] [2012-02-16 02:02:52] INFO WEBrick::HTTPServer#start: pid=76976 port=9292 From: /Users/joker/temp/config.ru @ line 8 in Hello#call: 3: require "pry" 4: 5: class Hello 6: def call(env) 7: binding.pry => 8: [200, {"Content-Type" => "text/plain"}, ["Hello World"]] 9: end 10: end 11: 12: use Rack::Auth::Basic do |user, pass| 13: user == "test" && pass == "password" [1] pry(#<Hello>)>
envと打ってみる。
[2] pry(#<Hello>)> env => {"GATEWAY_INTERFACE"=>"CGI/1.1", "PATH_INFO"=>"/", "QUERY_STRING"=>"", "REMOTE_ADDR"=>"127.0.0.1", "REMOTE_HOST"=>"localhost", "REQUEST_METHOD"=>"GET", "REQUEST_URI"=>"http://localhost:9292/", "SCRIPT_NAME"=>"", "SERVER_NAME"=>"localhost", "SERVER_PORT"=>"9292", "SERVER_PROTOCOL"=>"HTTP/1.1", "SERVER_SOFTWARE"=>"WEBrick/1.3.1 (Ruby/1.9.3/2011-10-30)", "HTTP_HOST"=>"localhost:9292", "HTTP_USER_AGENT"=> "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:7.0.1) Gecko/20100101 Firefox/7.0.1", "HTTP_ACCEPT"=> "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "HTTP_ACCEPT_LANGUAGE"=>"ja,en-us;q=0.7,en;q=0.3", "HTTP_ACCEPT_ENCODING"=>"gzip, deflate", "HTTP_ACCEPT_CHARSET"=>"Shift_JIS,utf-8;q=0.7,*;q=0.7", "HTTP_CONNECTION"=>"keep-alive", "HTTP_AUTHORIZATION"=>"Basic dGVzdDpwYXNzd29yZA==", "HTTP_CACHE_CONTROL"=>"max-age=0", "rack.version"=>[1, 1], "rack.input"=> #<Rack::Lint::InputWrapper:0x00000100b48920 @input=#<StringIO:0x00000100b5e400>>, "rack.errors"=> #<Rack::Lint::ErrorWrapper:0x00000100b48880 @error=#<IO:<STDERR>>>, "rack.multithread"=>true, "rack.multiprocess"=>false, "rack.run_once"=>false, "rack.url_scheme"=>"http", "HTTP_VERSION"=>"HTTP/1.1", "REQUEST_PATH"=>"/", "REMOTE_USER"=>"test"}
Rack::Auth::Basicでは、HTTP_AUTHORIZATIONヘッダから、
credentialsを取得しているという記述がありました。
じゃあ、内容を確認してみようってことで、
アクセスの途中で実行を止めて、envを見る、という形で活用してみました。
exitしてpryを終了すると続きのコードが流れてきます。
ちなみに、jugyoさんが作った、ir_bというgemでirb上でも同じことができます。
その場合は、binding.pryではなく、ir bという形で記述します。
同じコードでpryも起動できるように、pry用のインターフェースも用意してくれています。
(jugyo/ir_b - GitHub https://github.com/jugyo/ir_b)
他にもrspecのコードの中にbinding.pryを仕込むと、以下のようなことが出来ます。
[1] pry(#<RSpec::Core::ExampleGroup::Nested_1::Nested_2>)> subject => #<Repository _id: 4f3be74c91ea182d9e00000a, _type: nil, url: "https://github.com/joker1007/pasokara_player3", username: "joker1007", name: "pasokara_player3"> [2] pry(#<RSpec::Core::ExampleGroup::Nested_1::Nested_2>)> subject.username.should eq("joker1007") => true [3] pry(#<RSpec::Core::ExampleGroup::Nested_1::Nested_2>)> subject.username.should eq("joker") RSpec::Expectations::ExpectationNotMetError: expected: "joker" got: "joker1007" (compared using ==) from /Users/joker/gemfilestats/vendor/bundle/ruby/1.9.1/gems/rspec-expectations-2.8.0/lib/rspec/expectations/fail_with.rb:32:in `fail_with' [4] pry(#<RSpec::Core::ExampleGroup::Nested_1::Nested_2>)>
subjectには、Mongoidのオブジェクトが入っています。
このオブジェクトに対して、即座にshouldを呼び出し、テストコードの動きを確認できます。
また、そのコンテキストで使えるメソッドに対して補完が効くため、
have_と打って、TABを押すと、こんな感じでマッチャーが見れたりします。
[19] pry(#<RSpec::Core::ExampleGroup::Nested_1::Nested_2>)> subject.should have have have_and_belong_to_many have_at_least have_at_most have_exactly have_field have_fields have_many have_many_related have_one have_one_related have_instance_method
rspecを対話的に実行するなら、a_matsudaさん作のinteractive_rspecなんかもあります。
irbでシンプルに実行するなら、こちらの方が良いでしょう。
(amatsuda/interactive_rspec - GitHub https://github.com/amatsuda/interactive_rspec)
今回、話した事は、irbの拡張設定をやったり、拡張するgemを導入することで、
ある程度、irbでも実現することが可能だったりします。
色付けたり、エディタで編集したり、実行途中で止めたり。
pryは設定無しで、こういった機能をすぐに利用できる点は非常にありがたいと思いますし、
cdによるコンテキストの移動、ls, show-methodによる参照は非常に強力で、これはirbには無い機能です。
これらの機能は、ちょっとした時に覚えておくと凄く便利感を味わえます。
cd, ls, show-method, binding.pry, とりあえずこれだけ覚えておきましょう。
一方で、ruby-debug等と違ってデバッガでは無いため、
ステップ実行、ブレークポイントの指定などは出来ません。
一連のフローを検証したい時は、ruby-debug等を活用し、場合によって使い分けていくのが良いと思います。
Shibuya.rbでは、シェルを直接操作してデモしながら発表という形だったので、
発表内容にいくつか付け加えて、まとめてみました。
公式のwikiには結構色々書いてあるんですが、
活用法については、日本語の情報ってあんまり無かったような気がします。
この記事が誰かの参考になれば幸いです。
追記:
書いた後で、Twitterで情報を頂いて、ruby-debugにpryコマンドを追加できる、ruby-debug-pryなるものがあるらしいです。
全然知らなかった。まだまだ奥が深いですね。
(AndrewO/ruby-debug-pry - GitHub https://github.com/AndrewO/ruby-debug-pry)
後、スタックを追っ掛けて移動できるpry-stack_explorerというプラグインもあります。
こっちは知ってたんですが、まだ利用したことが無いため、今回は話に入れてませんでした。
ついでにこちらも貼っておきます。
(banister/pry-stack_explorer - GitHub https://github.com/banister/pry-stack_explorer)
よし、今度使ってみよう。
Thanks! @Kirika_K2