homeASCIIcasts

280: RailsでPry 

(view original Railscast)

Other translations: En Pl Es Fr

Other formats:

Written by Naomi Fujimoto

PryはIRBの代替となるツールで、 IRBと同じようにRubyコードを実行するためのプロンプトを提供します。IRBと比較して、多くの付加機能を持っています。今回のエピソードでは、PryのしくみとRailsアプリケーションに統合する方法を紹介します。

Pryはgemで提供されるのでインストールは簡単です。pry-doc gemも一緒にインストールしますが、これについては後ほど説明します。

$ gem install pry pry-doc

RVM gemsetを利用しているので、すべてのgemsetから使用できる形でPryをインストールします。そのためには次の2つのコマンドを実行します。

$ rvm gemset use global
$ gem install pry pry-doc

Pryがインストールできたらpryコマンドで起動し、irbコマンドでおこなうのと同じようにRubyコードを実行します。

$ pry
pry(main)> 1 + 2
=> 3

Pryは単なる電卓以上の機能を提供しますが、機能の説明の前に、Railsアプリケーションから利用できるように設定する方法を見ていきます。使用するアプリケーションは、今までも何回か使用したおなじみのブログアプリケーションです。

ブログアプリケーション

アプリケーションのディレクトリからrails cを実行するとIRBが起動します。代わりにPryを使うためには、Railsのenvironmentファイルを指定してpryコマンドを実行します。これによって、標準のRailsコンソールと同じように、アプリケーションのモデルにアクセスできるようになります。

$ pry -r ./config/environment
pry(main)> Article.count
=> 3

これでPryの準備ができたので、その機能について見ていきます。helpとタイプすると、Pryがサポートしているすべてのコマンドの一覧が表示されます。もっともよく使うコマンドは、cdlsの2つなのでこれらのコマンドを見てみましょう。cdコマンドは現在のスコープを変更します。cd ArticleとタイプするとArticleクラス内に移動し、selfを実行することでいつでも現在のスコープを確認できます。

pry(main)> cd Article
pry(#<Class:0x1022f60e0>):1> self
=> Article(id: integer, name: string, content: text, created_at: datetime, updated_at: datetime, published_at: datetime)

Articleクラス内にいるので、どのメソッドでも呼び出すことができます。firstとタイプすると最初の記事が返されますが、これはArticle.firstを指定するのと同じ意味になります。

pry(#<Class:0x1022f60e0>):1> first
=> #<Article id: 1, name: "What is Music", content: "Music is an art form in which the medium is sound o...", created_at: "2011-08-24 20:35:29", updated_at: "2011-08-24 20:37:22", published_at: "2011-05-13 23:00:00">

またあらゆるオブジェクトにcdすることができるので、Articleスコープ内でcd firstを実行するとその記事にスコープが移ります。そこでまたメソッドやプロパティを呼び出すことができるので、nameとタイプします。

pry(#<Class:0x1022f60e0>):1> cd first
pry(#<Article:0x102300c98>):2> name
=> "What is Music"

記事のnamecdしてその文字列に対してメソッドを呼び出すことも可能です。

pry(#<Article:0x102300c98>):2> cd name
pry("What is Music"):3> upcase
=> "WHAT IS MUSIC"

Pryは移動の履歴を覚えていて、nestingを呼び出して参照することができます。

pry("What is Music"):3> nesting
Nesting status:
--
0. main (Pry top level)
1. #<Class:0x1022f60e0>
2. #<Article:0x102300c98>
3. "What is Music"

このコマンドは、それまでに訪れたオブジェクトのリストを返します。exitとタイプすると、直前にいたオブジェクトの中に戻されるので、今回の場合は最初の記事に戻ります。再度exitとタイプすると、Articleクラスに戻ります。

もっともよく使用されるもうひとつのコマンドはlsで、これは変数とメソッドをリスト表示します。デフォルトでは現在のスコープのすべての変数をリスト表示します。今Articleクラスの中にいたとすると、これを実行するとメソッドの一覧が表示されます。

pry(#<Class:0x1022f60e0>):1> ls
[:_, :_pry_, :inp, :out, :@_create_callbacks, :@_defined_class_methods, :@_save_callbacks, :@_update_callbacks, :@_validate_callbacks, :@arel_engine, :@arel_table, :@attribute_methods_generated, :@cached_attributes, :@column_names, :@columns, :@columns_hash, :@finder_needs_type_condition, :@generated_attribute_methods, :@inheritable_attributes, :@inheritance_column, :@parent_name, :@quoted_primary_key, :@quoted_table_name, :@relation]

Pryコマンドにはフラグをサポートするものがあり、その場合は-hオプションをつけてコマンドを実行するとコマンドのフラグの一覧が表示されます。ls -hを実行すると、サポートされているすべてのオプションが表示されます。その中の-mを指定するとクラスが持つメソッドのリストが表示され、-Mを指定するとインスタンスメソッドのリストが表示されます。現在のスコープの代わりにオブジェクトやクラスを渡すと、それが持つメソッドのリストが返されます。

もうひとつの便利なPryコマンドはshow-docです。例えばArrayクラスのin_groups_ofメソッドの動作を知りたいとします。その場合はshow-doc Array#in_groups_ofを実行して調べることができます。

pry(#<Class:0x1022f60e0>):1> show-doc Array#in_groups_of

From: /Users/eifion/.rvm/gems/ruby-1.9.2-p290/gems/activesupport-3.0.10/lib/active_support/core_ext/array/grouping.rb @ line 19:
Number of lines: 15

signature: in_groups_of(number, fill_with=?)

Splits or iterates over the array in groups of size number,
padding any remaining slots with fill_with unless it is false.

  %w(1 2 3 4 5 6 7).in_groups_of(3) {|group| p group}
  ["1", "2", "3"]
  ["4", "5", "6"]
  ["7", nil, nil]

  %w(1 2 3).in_groups_of(2, '&nbsp;') {|group| p group}
  ["1", "2"]
  ["3", "&nbsp;"]

  %w(1 2 3).in_groups_of(2, false) {|group| p group}
  ["1", "2"]
  ["3"]

オブジェクトに対して直接show-docを呼び出すこともできます。現在のスコープはArticleクラスなので、allを指定すると記事の配列が返されます。show-doc all.in_groups_ofを実行しても、上と同じドキュメンテーションが返されます。

もうひとつの便利なコマンドはshow-methodです。このコマンドはあらゆるメソッドのソースコードを表示します。これを使ってin_groups_ofのソースを見てみます。(ここで念のため触れておきますが、Pryにはコマンドの入力時に自動補完の機能がありタブキーを押すことで呼び出すことができます。)

pry(#<Class:0x104e63de0>):1> show-method all.in_groups_of

From: /Users/eifion/.rvm/gems/ruby-1.9.2-p290/gems/activesupport-3.0.10/lib/active_support/core_ext/array/grouping.rb @ line 19:
Number of lines: 19

def in_groups_of(number, fill_with = nil)
  if fill_with == false
    collection = self
  else
    # size % number gives how many extra we have;
    # subtracting from number gives how many to add;
    # modulo number ensures we don't add group of just fill.
    padding = (number - size % number) % number
    collection = dup.concat([fill_with] * padding)
  end

  if block_given?
    collection.each_slice(number) { |slice| yield(slice) }
  else
    groups = []
    collection.each_slice(number) { |group| groups << group }
    groups
  end
end

また、これと似たedit-methodというコマンドがあります。メソッドに対してこのコマンドを実行すると、関連するソースコードのファイルをテキストエディタで開き、対応する行を表示します。

コマンドの前にピリオドをつけることで、シェルコマンドを実行することもできます。Pryの中で.lsを実行すると、シェルコマンドのlsが実行され、カレントディレクトリのすべてのファイルがリスト表示されます。

Pryはデバッグ時にも役に立ちます。Articleモデルにはword_countというメソッドがあり、記事のcontentに含まれる単語の数を返します。しかしこのメソッドにはバグがあるため、記事のcontentが何であっても常に0を返します。メソッドを調べるために最初のArticlecdして、edit-method word_countを実行します。メソッドの内容は以下のとおりです。

/app/models/article.rb
class Article < ActiveRecord::Base
  attr_accessible :name, :content, :published_at
  has_many :comments

  def word_count
    words = content.scan(/\\w+/)
    words.size
  end
end

binding.pryを呼び出すことで、コードの任意の場所にブレイクポイントを追加できます。上のコードのwords.sizeの行の直前にブレイクポイントを追加してファイルを保存してword_countメソッドを再度呼び出すと、binding.pryの場所で停止してPryプロンプトに戻ります。

> word_count

From: /Users/eifion/blog/app/models/article.rb @ line 7 in Article#word_count:

     2:   attr_accessible :name, :content, :published_at
     3:   has_many :comments
     4: 
     5:   def word_count
     6:     words = content.scan(/\\w+/)
 =>  7:     binding.pry
     8:     words.size
     9:   end
    10: end

メソッドのすべてのローカル変数にアクセスできるので、wordsを呼び出すとwordsという配列の内容を見ることができます。

pry(#<Article:0x1008c3f38>):3> words
=> []

配列は空になっているので、内容をスキャンする正規表現に何か問題があると思われます。調べてみると、バックスラッシュが1つであるべきところが2つになっています。これを修正するために再度edit-method word_countを実行し、正規表現を修正し、binding.pryの行を削除してファイルを保存します。

/app/models/article.rb

class Article < ActiveRecord::Base
  attr_accessible :name, :content, :published_at
  has_many :comments

  def word_count
    words = content.scan(/\w+/)
    words.size
  end
end

修正部分をテストするために再度word_countを呼び出すと、今度は期待通りに動作します。

pry(#<Article:0x1008c3f38>):3> word_count
=> 55

アプリケーションのコントローラ層やビュー層の何かをデバッグするときはコンソール経由でなく作業したい場合もあります。この場合もPryが役に立ちます。まずGemfileにPryへの参照を追加します。

/Gemfile

source 'http://rubygems.org'

gem 'rails', '3.0.10'
gem 'sqlite3'
gem 'nifty-generators'
gem 'pry', :group => :development

bundleを実行してgemをインストールし、rails sでサーバを起動します。これで、コントローラのどこにでもbinding.pryの呼び出しを追加することで、ブレイクポイントを設定できます。

/app/controllers/articles_controller.rb

def index
  @articles = Article.all
  binding.pry
end

プラウザでそのページを開くと、読み込みが途中で中断し、一方ターミナルではそのブレイクポイントで停止した状態のPryプロンプトが表示されます。モデルでの場合と同じように、ローカル変数やインスタンス変数を検査(inspect)できます。作業が終わったら、exit-allとタイプしてリクエストを完了させることができます。

From: /Users/eifion/blog/app/controllers/articles_controller.rb @ line 4 in ArticlesController#index:

     1: class ArticlesController < ApplicationController
     2:   def index
     3:     @articles = Article.all
 =>  4:     binding.pry
     5:   end
     6: 
     7:   def show
     8:     @article = Article.find(params[:id])
     9:   end

Pryに関する今回のエピソードは以上です。とても便利なgemで、ここで紹介しきれなかった多くの機能を持っています。wikiにはそれらの機能の説明や、Joshua Cheekによる非常に参考になるスクリーンキャストのリンクもあります。