読者です 読者をやめる 読者になる 読者になる

Rails Webook

自社のECを開発している会社で働いています。Rails情報やサービスを成長させる方法を書いていきます

Railsの開発効率をあげる - Pryを使ってRailsのコンソールをパワーアップ & デバッグをする

Rails gem 開発を効率化する

Pryとは

Rubyには、標準で付属されているirb(Interactive Ruby)というツールがあります。
consoleで、irbと入力するとirbが実行されます。
そこで、対話的にRubyの式を入力・実行することができます。

$ irb
irb(main):001:0> 1 + 2
=> 3


そして、Pryはirbの代替となるパワフルな対話ツールです。
なにがパワフルかというと次のようなことができます。

  • ドキュメントが見れる
  • シンタックスハイライト
  • デバッグができる(binding.pryをソース二記載するとブレイクポイントになる)
$ pry
[1] pry(main)> ls     # 現在のスコープの変数とメソッドを表示
self.methods: inspect  to_s
locals: _  __  _dir_  _ex_  _file_  _in_  _out_  _pry_

動作確認

  • Rails 4.1
  • pry-rails 0.3.2
  • pry-doc 0.6.0
  • pry-byebug 2.0
  • pry-stack_explorer 0.4.9.1

目次


1. Railsプロジェクトを作成

まずはRailsプロジェクトを作成します。

rails new pry_test
cd pry_test

次にPryで捜査を確認するために、UserモデルとCommentモデルを作成します。

rails g scaffold User firstname:string lastname:string email:string
rails g model Comment body:string user:references
rake db:migrate

Userモデルにアソシエーションを追加します。

# app/models/user.rb
class User < ActiveRecord::Base
  has_many :comments
end

Commentモデルを作成したときにuser:referencesを追加したので既にアソシエーションがついています。

# app/models/comment.rb
class Comment < ActiveRecord::Base
  belongs_to :user
end

2. pryをインストール

GemfileにPryを追加します。

# Gemfile

group :development, :test do
  gem 'pry-rails'  # rails console(もしくは、rails c)でirbの代わりにpryを使われる
  gem 'pry-doc'    # methodを表示
  gem 'pry-byebug' # デバッグを実施(Ruby 2.0以降で動作する)
  gem 'pry-stack_explorer' # スタックをたどれる
end

Bundlerでgemをインストールします。

bundle install

では、rails cでconsoleを開きましょう。
consoleにpry(main)と表示されれば、Pryが使われている証拠です。

$ rails c
Loading development environment (Rails 4.1.5)
[1] pry(main)>


ちなみに、Pryを入れていない場合は、Rubyに標準で付属しているirbが使われます。

$ rails c
Loading development environment (Rails 4.1.5)
irb(main):001:0>

3. pryコマンド

Railsのルートで、rails cでPryを使うことができます。

$ rails c
Loading development environment (Rails 4.1.5)
[1] pry(main)> a = 10
=> 10
[2] pry(main)> watch a  # watch:変更されたら通知するように設定するコマンド
Watching a
watch: a => 10
[3] pry(main)> a = 20
watch: a => 20          # 変更されたので通知される
=> 20


Pryでは次のようなコマンドが使えますので、ざっと確認しておいて損はないと思います。

ヘルプ
  help               コマンドの情報を表示

コンテキスト
  cd                 新しいコンテキスト(オブジェクトかスコープ)に移動
  find-method        現在のclass/module、ネームスペース内のメソッドを探す
  ls                 現在のスコープ内の変数やメソッドのリストを表示
  pry-backtrace      pryセッションのバックトレースを表示
  raise-up           現在のpryインスタンス外でExceptionを発生させる
  reset              Reset the repl to a clean state.
  watch              Expressionの値を確認し、変わったときにNotificationを表示する
  whereami           現在のコンテキスト周辺のコードを表示
  wtf?               直近のExceptionのバックトレースを表示

編集
  /^\s*!\s*$/        入力バッファをクリア
  amend-line         マルチラインモードでの入力ラインを修正
  edit               Invoke the default editor on a file.
  hist               コマンド入力の履歴を表示する
  play               Playback a string variable, method, line, or file as input.
  show-input         Show the contents of the input buffer for the current multi-line expression.

Introspection
  ri                 View ri documentation.
  show-doc           Show the documentation for a method or class.
  show-source        Show the source for a method or class.
  stat               View method information and set _file_ and _dir_ locals.

Gems
  gem-cd             Change working directory to specified gem's directory.
  gem-install        Install a gem and refresh the gem cache.
  gem-list           List and search installed gems.
  gem-open           Opens the working directory of the gem in your editor.

Commands
  import-set         Import a pry command set.
  install-command    Install a disabled command.

Aliases
  !!!                Alias for `exit-program`
  !!@                Alias for `exit-all`
  $                  Alias for `show-source`
  ?                  Alias for `show-doc`
  @                  Alias for `whereami`
  breakpoint         Alias for `break`
  breaks             Alias for `breakpoints`
  c                  Alias for `continue`
  clipit             Alias for `gist --clip`
  f                  Alias for `finish`
  file-mode          Alias for `shell-mode`
  history            Alias for `hist`
  n                  Alias for `next`
  quit               Alias for `exit`
  quit-program       Alias for `exit-program`
  reload-method      Alias for `reload-code`
  s                  Alias for `step`
  show-method        Alias for `show-source`

Input and output
  .<shell command>   All text following a '.' is forwarded to the shell.
  cat                Show code from a file, pry's input buffer, or the last exception.
  change-inspector   Change the current inspector proc.
  change-prompt      Change the current prompt.
  fix-indent         Correct the indentation for contents of the input buffer
  list-inspectors    List the inspector procs available for use.
  list-prompts       List the prompts available for use.
  save-file          Export to a file using content from the repl.
  shell-mode         Toggle shell mode. bring in pwd prompt and file completion.

Misc
  gist               Upload code, docs, history to https://gist.github.com/.
  pry-version        Show pry version.
  reload-code        Reload the source file that contains the specified code object.
  toggle-color       Toggle syntax highlighting.

Navigating pry
  !pry               Start a pry session on current self.
  disable-pry        Stops all future calls to pry and exits the current session.
  exit               Pop the previous binding.
  exit-all           End the current pry session.
  exit-program       End the current program.
  jump-to            Jump to a binding further up the stack.
  nesting            Show nesting information.
  switch-to          Start a new subsession on a binding in the current stack.

Prompts
  simple-prompt      Toggle the simple prompt.

4. pry-railsコマンド

pry-railsを入れることで、モデルやルートなどを確認することができます。

$ rails c
[1] pry(main)> show-models   # すべてのモデルを表示する
# 今回定義したCommentとUserモデルが表示さています
Comment
  id: integer
  body: string
  user_id: integer
  created_at: datetime
  updated_at: datetime
  belongs_to :user
User
  id: integer
  firstname: string
  lastname: string
  email: string
  created_at: datetime
  updated_at: datetime
  has_many :comments

では、pry-railsのコマンド一覧です。

Rails
  recognize-path     どのルートにマッチするURLか確認する
  show-middleware    すべてのミドルウェアを表示する
  show-model         引数で指定したモデルを表示する
  show-models        すべてのモデルを表示する
  show-routes        すべてのルートを表示する

5. pry-byebugでデバッグ

pry-byebugを使うことにより、デバッグを実行することが可能です。

ブレークポイントを設定

binding.pryとソースに入力し、そこがブレークポイントになります。
そこの箇所でプログラムが中断されます。

# app/controllers/users_controller.rb
  ...
  # POST /users
  # POST /users.json
  def create
    @user = User.new(user_params)
    binding.pry # ブレークポイントを設定
    puts "execute"

    respond_to do |format|
      if @user.save
        format.html { redirect_to @user, notice: 'User was successfully created.' }
        format.json { render :show, status: :created, location: @user }
      else
        format.html { render :new }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end
  ...

プログラム実行

.をつけることでTerminalコマンドを実行することができるので、.rails sとPry上で実行して、サーバーを起動しましょう。

[29] pry(main)> .rails s
=> Booting WEBrick
=> Rails 4.1.5 application starting in development on http://0.0.0.0:3000
=> Run `rails server -h` for more startup options
=> Notice: server is listening on all interfaces (0.0.0.0). Consider using 127.0.0.1 (--binding option)
=> Ctrl-C to shutdown server
[2014-10-24 01:12:01] INFO  WEBrick 1.3.1
[2014-10-24 01:12:01] INFO  ruby 2.0.0 (2013-06-27) [x86_64-darwin13.2.0]
[2014-10-24 01:12:01] INFO  WEBrick::HTTPServer#start: pid=13779 port=3000


そして、ユーザー新規画面を開きます。
f:id:nipe880324:20141024011854p:plain:w480

ステップ実行

「Create User」ボタンを押すとサーバーを起動させていたconsoleが次のようにブレークポイントを設定した箇所で止まっています。

Started POST "/users" for 127.0.0.1 at 2014-10-24 01:00:33 +0900
Processing by UsersController#create as HTML
  Parameters: {"utf8"=>"", "authenticity_token"=>"7T1ISCnaEA5UqHK/FEn7E820I1ql6k5UdvaZQxTKC20=", "user"=>{"firstname"=>"太郎", "lastname"=>"鈴木", "email"=>"t.suzuki@test.com"}, "commit"=>"Create User"}

From: /Users/nipe0324/rails_samples/pry_test/app/controllers/users_controller.rb @ line 30 UsersController#create:

    26: def create
    27:   @user = User.new(user_params)
    28:   binding.pry # ブレークポイントを設定
 => 29:   puts "execute"
    30:
    31:   respond_to do |format|
    32:     if @user.save
    33:       format.html { redirect_to @user, notice: 'User was successfully created.' }
    34:       format.json { render :show, status: :created, location: @user }
    35:     else
    36:       format.html { render :new }
    37:       format.json { render json: @user.errors, status: :unprocessable_entity }
    38:     end
    39:   end
    40: end

[1] pry(#<UsersController>)>

通常のRubyやPryコマンドが使えるので、変数の内容などを表示できます。

[1] pry(#<UsersController>)> p @user
#<User id: nil, firstname: "太郎", lastname: "鈴木", email: "t.suzuki@test.com", created_at: nil, updated_at: nil>
=> #<User id: nil, firstname: "太郎", lastname: "鈴木", email: "t.suzuki@test.com", created_at: nil, updated_at: nil>

では、次の行へステップ実行します。

[1] pry(#<UsersController>)> next  # 次の行へ実行
execute     # puts "execute" でexecuteが出力された

From: /Users/nipe0324/Google Drive/rails/rails_samples/pry_test/app/controllers/users_controller.rb @ line 31 UsersController#create:

    26: def create
    27:   @user = User.new(user_params)
    28:   binding.pry # ブレークポイントを設定
    29:   puts "execute"
    30:
 => 31:   respond_to do |format|
    32:     if @user.save
    33:       format.html { redirect_to @user, notice: 'User was successfully created.' }
    34:       format.json { render :show, status: :created, location: @user }
    35:     else
    36:       format.html { render :new }
    37:       format.json { render json: @user.errors, status: :unprocessable_entity }
    38:     end
    39:   end
    40: end

[1] pry(#<UsersController>)>

といったようにデバッグができます。

pry-byebugで使えるコマンドは次のとおりです。

Pry-byebug (v2.0.0)
  break              ブレークポイントをセットか編集する
  breakpoints        定義されたブレークポイントのリストを表示
  continue           プログラム実行をコンティニューする。そして、Pryセッションを終了させる。
  finish             現在のスタックフレームが終わるまで実行する
  next               現在のスタックフレーム内の次の列を実行する
  step               次の行かメソッドの内をステップ実行する

pry-stack_explorerでより深くデバッグ

では、また、画面からUserを作成しましょう。
すると、binding.pryを記述したブレークポイントで止まります。

Frame number: 0/76

From: /Users/nipe0324/Google Drive/rails/rails_samples/pry_test/app/controllers/users_controller.rb @ line 29 UsersController#create:

    26: def create
    27:   @user = User.new(user_params)
    28:   binding.pry # ブレークポイントを設定
 => 29:   puts "execute"
    30: 
    31:   respond_to do |format|
    32:     if @user.save
    33:       format.html { redirect_to @user, notice: 'User was successfully created.' }
    34:       format.json { render :show, status: :created, location: @user }
    35:     else
    36:       format.html { render :new }
    37:       format.json { render json: @user.errors, status: :unprocessable_entity }
    38:     end
    39:   end
    40: end

次にpry上でshow-stackと打つとスタックトレースが表示されます。

[1] pry(#<UsersController>)> show-stack
when_started hook failed: NoMethodError: private method `eval' called for nil:NilClass
/Users/nipe0324/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/pry-stack_explorer-0.4.9.1/lib/pry-stack_explorer.rb:109:in `bindings_equal?'
(see _pry_.hooks.errors to debug)

Showing all accessible frames in stack (77 in total):
--
=> #0  create <UsersController#create()>
   #1 [block]   block in run <PryByebug::Processor#run(initial=?, &_block)>
   #2 [method]  run <PryByebug::Processor#run(initial=?, &_block)>
   #3 [method]  resume_pry <PryByebug::Processor#resume_pry(context)>
   #4 [method]  at_line <PryByebug::Processor#at_line(context, _file, _line)>
   #5 [method]  at_line <Byebug::Context#at_line(file, line)>
   #6 [method]  create <UsersController#create()>
   #7 [method]  send_action <ActionController::ImplicitRender#send_action(method, *args)>
   #8 [method]  process_action <AbstractController::Base#process_action(method_name, *args)>
   #9 [method]  process_action <ActionController::Rendering#process_action(*arg1)>

そして、frame [スタック番号]を打つと、スタックを辿れ、そこでコマンドを実行することができます。

[2] pry(#<UsersController>)> frame 7

Frame number: 7/76
Frame type: method

From: /Users/nipe0324/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/actionpack-4.1.5/lib/action_controller/metal/implicit_render.rb @ line 4 ActionController::ImplicitRender#send_action:

    3: def send_action(method, *args)
 => 4:   ret = super
    5:   default_render unless performed?
    6:   ret
    7: end


以上です。
Pryを使うことで、rails consoleを拡張できるだけでなく、有用な情報を簡単に表示できること、デバッグができることなど便利ですので、是非使いこなせるようになってみるといいかもしれません。

参考文献