Rails

【Rails】Rails開発者に絶対おすすめしたいデバッグ手法5選(初心者向け)

Rails

 

今回は、Rails開発で活躍するデバッグ手法5選を紹介したいと思います。Railsでは様々なデバッグ手法やgemが用意されていますが、今回はその中でも頻繁に使われるもののみをまとめました。

個人的には、これら5つのデバッグ手法が使えればRailsでのアプリケーション開発は十分だと思います。ぜひこの機会にマスターしていきましょう。

デバッグ手法5選

使うべきデバッグ手法は以下の5つです。この後の項目で、導入手順や使い方についてそれぞれ詳しく見ていきます。

  • pp(printデバッグ)
  • rails console
  • pry-rail
  • (binding.pry)
  • pry-byebug

ppメソッド

pp(printデバッグ)とはppメソッドをコード内に埋め込み、変数の値や実行された条件分岐を確認するデバッグ手法です。

ppメソッドとは「pretty print」の略で、オブジェクトを読みやすく表示してくれるメソッドです。原始的なデバッグ方法ですが、簡単なコードであれば最も効率よくデバッグできる手法でもあります。

ではその使い方を見ていきましょう。例えば、以下のようにArticle(投稿記事)を一覧表示するアクションが定義されていたとします。

def index
  @articles = Article.all
end

 

では、どのような投稿記事が取得されているか@articlesの中身を確認してみましょう。ppメソッドを@articlesの直下に記述します。

def index
  @articles = Article.all
  pp @articles
end

 

サーバーを立ち上げ、ターミナルのログを確認してみます。すると以下のように、@articlesの中身が表示されていることが分かると思います。

[#<Article:0x00007fd1d61cb358
  id: 1,
  url: "aaa",
  post: "テスト1",
  created_at: Sun, 27 Sep 2020 23:51:20 UTC +00:00,
  updated_at: Sun, 27 Sep 2020 23:51:20 UTC +00:00>,
 #<Article:0x00007fd1d538f280
  id: 2,
  url: "bbb",
  post: "テスト2",
  created_at: Sun, 27 Sep 2020 23:51:32 UTC +00:00,
  updated_at: Sun, 27 Sep 2020 23:51:32 UTC +00:00>,
 #<Article:0x00007fd1d538f1b8
  id: 3,
  url: "ccc",
  post: "テスト3",
  created_at: Sun, 27 Sep 2020 23:51:45 UTC +00:00,
  updated_at: Sun, 27 Sep 2020 23:51:45 UTC +00:00>]

 

このようにppメソッドを使用することで、インスタンス変数の中身やどこまでコードが来ているかなどを確認することができます。

pメソッドでも同様に使用可能ですが、出力結果が横並びになってしまいコードが見にくくなってしまいます。

rails console

rails consoleとは、ターミナル上でRubyのプログラムを実行したり、データベースを直接操作できたり、コードの動作確認などができるデバッグ手法です。

先ほどの@articlesの中身をrails consoleを使って見てみましょう。

ターミナルでrails consoleと入力します。

% rails console
irb>
rails cと省略して書くこともできます。

 

irbが起動し、以下のように入力することで@articlesの中身を確認することができます。

% rails console
irb> @articles = Article.all
   (0.8ms)  SET NAMES utf8mb4,  @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'),  @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
  Article Load (0.5ms)  SELECT `articles`.* FROM `articles` LIMIT 11
=> #<ActiveRecord::Relation [#<Article id: 1, url: "aaa", post: "テスト1", created_at: "2020-09-27 23:51:20", updated_at: "2020-09-27 23:51:20">, #<Article id: 2, url: "bbb", post: "テスト2", created_at: "2020-09-27 23:51:32", updated_at: "2020-09-27 23:51:32">, #<Article id: 3, url: "ccc", post: "テスト3", created_at: "2020-09-27 23:51:45", updated_at: "2020-09-27 23:51:45">]>
irbとは「interactive ruby」の略で「対話的プログラム」と呼ばれています。

rails consoleでは、URLを確認することも可能です。

% rails console
irb> app.articles_path
=> "/articles"

 

コントローラーとアクションからURLを確認することもできます。

% rails console
rirb> app.url_for(controller: 'articles', action: 'new')
=> "http://www.example.com/articles/new"

 

この他にもirbではクラス名を調べたり、継承元を確認できたりと様々な機能があります。詳しくはこちらの公式ドキュメントを参照してください。

library irb:https://docs.ruby-lang.org/ja/latest/library/irb.html

pry-rails

突然ですが、Rubyのコンソールには2種類存在しています。

1つがirb、もう1つがpryです。先ほどのrails consoleの項目を見ていただくと分かる通り、コンソールを起動するとデフォルトではirbが立ち上がるようになっています。

しかし、Railsで開発をするのであればirbではなく、迷わずpryを使いましょう。

Railsでpryを使用するには「pry-rails」というGemをインストールする必要があり、以下の手順で「pry-rails」を導入することができます。

①Gemfileに「pry-rails」を記載
gem 'pry-rails'

②bundle installを実行

③rails consoleを実行し、irbではなくpryになっているか確認

 

では、なぜirbよりもpryが推奨されているのでしょうか?

pryを推奨する理由は以下の3つです。

  • シンタックスハイライトが標準装備されている
  • 便利なコマンドが標準装備されている
  • shellコマンドが使用可能である

シンタックスハイライトが標準装備されている

まず何と言っても、シンタックスハイライト(出力結果が色付けされること)があることでコマンドの可読性が上がります。

試しにrails consoleを立ち上げ、以下の計算式を入力してみてください。

% rails console
Running via Spring preloader in process 36475
Loading development environment (Rails 6.0.3.3)
pry(main)> 6 * 6
=> 36
この記事上では黒で統一されていますが、ターミナル上では色付けされているはずです。

 

irbで色付けすることも可能ですが、それには新しく別のGemをインストールする必要があります。しかしpryを使用することで、簡単にシンタックスハイライトを装備することができるようになります。

便利なコマンドが標準装備されている

pryにはいくつかの組み込みコマンドが標準装備されており、それらを使用することで効率よくデバッグを行うことができます。

使用できるコマンドはrails consoleで「help」と打ち込むことで確認できます。実際に試してみてください。

% rails console
Running via Spring preloader in process 36475
Loading development environment (Rails 6.0.3.3)
pry(main)> help
Help
  help               Show a list of commands or information about a specific command.

Context
  cd                 Move into a new context (object or scope).
  find-method        Recursively search for a method within a class/module or the current namespace.
  ls                 Show the list of vars and methods in the current scope.
  pry-backtrace      Show the backtrace for the pry session.
  raise-up           Raise an exception out of the current pry instance.
  reset              Reset the repl to a clean state.
  watch              Watch the value of an expression and print a notification whenever it changes.
  whereami           Show code surrounding the current context.
  wtf?               Show the backtrace of the most recent exception.

Editing
  !                  Clear the input buffer.

以下省略

 

例えばですが「show-routes」という組み込みコマンドがあります。これはアプリケーションで定義されているルーティングを確認することができるコマンドです。

% rails console
pry(main)> show-routes
                               Prefix Verb   URI Pattern                                                                              Controller#Action
                             articles GET    /articles(.:format)                                                                      articles#index
                                      POST   /articles(.:format)                                                                      articles#create
                          new_article GET    /articles/new(.:format)                                                                  articles#new
                         edit_article GET    /articles/:id/edit(.:format) 

以下省略
rails routesコマンドと同じ役割ですが、show-routesはspringを使用している分高速になります。

 

また「show-models」というコマンドもあります。これはアプリケーションにある全てのモデルとそのカラム、さらにはデータタイプを返してくれるコマンドになります。

% rails console
pry(main)> show-models
  (5.6ms)  SET NAMES utf8mb4,  @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'),  @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
ActionMailbox::InboundEmail
  Table doesn't exist
  has_one :raw_email_attachment (class_name :ActiveStorage::Attachment)
  has_one :raw_email_blob (through :raw_email_attachment, class_name :ActiveStorage::Blob)
ActionText::RichText
  Table doesn't exist
  belongs_to :record
  has_many :embeds_attachments (class_name :ActiveStorage::Attachment)
  has_many :embeds_blobs (through :embeds_attachments, class_name :ActiveStorage::Blob)
ActiveStorage::Attachment
  Table doesn't exist
  belongs_to :blob (class_name :ActiveStorage::Blob)
  belongs_to :record
ActiveStorage::Blob
  Table doesn't exist
  has_many :attachments
  has_one :preview_image_attachment (class_name :ActiveStorage::Attachment)
  has_one :preview_image_blob (through :preview_image_attachment, class_name :ActiveStorage::Blob)
ApplicationRecord
  Table doesn't exist
Article
  id: integer
  url: string
  post: string
  created_at: datetime
  updated_at: datetime

 

「reload!」というコマンドもあり、これを実行することでアプリケーションが再読み込みされます。

% rails console
pry(main)> reload!
Reloading...
=> true
毎回exitで抜けたり、サーバーの再起動をしなくて済むね。

shellコマンドが使用可能

pryはshellと結合することによって、shellコマンドをコンソール上で入力することができるようになります。

% rails console
pry(main)> .ls
Gemfile                 babel.config.js         lib                     public                  yarn.lock
Gemfile.lock            bin                     log                     storage
README.md               config                  node_modules            test
Rakefile                config.ru               package.json            tmp
app                     db                      postcss.config.js       vendor

pry(main)> .pwd
/Users/ユーザー名/Desktop/debug-app
pryでshellコマンドを使用する場合は「ドット(.)」を付けて実行しなければなりません。

 

このようにpryを使用することで、より拡張性が高い機能を使うことができます。こういった理由から、Railsで開発する際は必ず「pry-rails」をインストールするようにしましょう。

(binding.pry)

binding.pryは先ほど解説したGem「pry-rails」をインストールすることで使用できる機能です。binding.pryを使用することで、ブレークポイントをコード内に挿入することができます。

binding.pryは先ほどの「pry-rails」の項目に含めても良かったのですが、デバッグで一番使える機能と言っても過言ではないので、新たに項目を追加して記載する形にしています。

 

では、binding.pryの使い方を見ていきましょう。例えば、以下のようにArticle(投稿記事)を一覧表示するアクションが定義されていたとします。

def index
  @articles = Article.all
end

 

ここにbinding.pryを差し込み、@articlesの中身がどのようになっているか確認してみます。以下のように記述し、サーバーを立ち上げ、ブラウザにアクセスしてみましょう。

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

 

するとターミナル上では、以下のようにプログラムの処理が止まっていると思います。

    6: def index
    7:   @articles = Article.all
 => 8:   binding.pry
    9: end

pry(#<ArticlesController>)> 

 

ここで@articlesと入力しましょう。そうすることで、@articlesの中身を確認することができます。

    6: def index
    7:   @articles = Article.all
 => 8:   binding.pry
    9: end

pry(#<ArticlesController>)> @articles
  Article Load (13.3ms)  SELECT `articles`.* FROM `articles`
  ↳ app/controllers/articles_controller.rb:8:in `index'
=> [#<Article:0x00007fd8a7ecf2e8
  id: 1,
  url: "aaa",
  post: "テスト1",
  created_at: Sun, 27 Sep 2020 23:51:20 UTC +00:00,
  updated_at: Sun, 27 Sep 2020 23:51:20 UTC +00:00>,
 #<Article:0x00007fd8a9880ac0
  id: 2,
  url: "bbb",
  post: "テスト2",
  created_at: Sun, 27 Sep 2020 23:51:32 UTC +00:00,
  updated_at: Sun, 27 Sep 2020 23:51:32 UTC +00:00>,
 #<Article:0x00007fd8a98807f0
  id: 3,
  url: "ccc",
  post: "テスト3",
  created_at: Sun, 27 Sep 2020 23:51:45 UTC +00:00,
  updated_at: Sun, 27 Sep 2020 23:51:45 UTC +00:00>]

 

また、ビューファイル(HTML)でもbinding.pryを使用することができます。ビューで使用する場合は、以下のように<% %>で囲むことで記述が可能になり、articleの中身を確認することができます。

<tbody>
  <% @articles.each do |article| %>
    <% binding.pry %>
    <tr>

以下省略
    14:   <tbody>
    15:     <% @articles.each do |article| %>
 => 16:       <% binding.pry %>
    17:       <tr>

pry(#<#<Class:0x00007fd8c12d7f48>>)> article
=> #<Article:0x00007fd8bed70ea8
 id: 1,
 url: "aaa",
 post: "テスト1",
 created_at: Sun, 27 Sep 2020 23:51:20 UTC +00:00,
 updated_at: Sun, 27 Sep 2020 23:51:20 UTC +00:00>

pry(#<#<Class:0x00007fd8c12d7f48>>)> exit!
pryはexitを打ち込むことで抜け出すことができますが、繰り返し処理の中に記載した場合はexitを繰り返し処理分打たなくてはなりません。その場合は、exit!を入力しましょう。

 

このようにbinding.pryを使用することで簡単にブレークポイントを作り、値の中身を確認することができます。正直このbinding.pryがRailsのデバッグで最も重要であるといっても過言ではないので、ぜひ理解しておきましょう。

pry-byebug

「pry-rails」の他にも「pry-byebug」と呼ばれるデバッグ用のGemが用意されています。「pry-byebug」をインストールすることで、binding.pryを使用することができるだけでなく、様々な便利コマンドを使用することができます。

頻繁に使用する便利コマンドは以下の5つです。

  • next
  • step
  • finish
  • continue
  • break

「pry-rails」は必要最低限のデバッグ機能があれば問題ない方、「pry-buybug」は様々なデバッグ手法を試したい方向けです。

「pry-byebug」は「pry-rails」の進化版というイメージだね。

 

「pry-buybug」は以下の手順でインストールすることができます。

①Gemfileに「pry-buybug」を記載
gem 'pry-buybug'

②bundle installを実行

 

では「pry-byebug」の便利コマンドを見ていきましょう。

その前に以下のように、ビューファイルの中にbinding.pryを仕込んでおきました。

<tbody>
  <% @articles.each do |article| %>
    <% binding.pry %>
    <tr>

以下省略
    14:   <tbody>
    15:     <% @articles.each do |article| %>
    16:       <% binding.pry %>
 => 17:       <tr>

pry(#<#<Class:0x00007fd8c12d7f48>>)>
「pry-rails」とは異なり「pry-byebug」では、binding.pryの1つ先の行がブレークポイントになります。

next

nextを使用することで次の行に移ることができます。

    14:   <tbody>
    15:     <% @articles.each do |article| %>
    16:       <% binding.pry %>
 => 17:       <tr>

pry(#<#<Class:0x00007fd8c12d7f48>>)> next

    14:   <tbody>
    15:     <% @articles.each do |article| %>
    16:     <% binding.pry %>
    17:       <tr>
 => 18:         <td><%= article.url %></td>

以下省略

step

stepはメソッドの実行もしくは次の行に移動できるコマンドです。nextは単純に次の行に移動、stepはメソッドを実行し、なければ次の行に移動と覚えていただけたらと思います。

    14:   <tbody>
    15:     <% @articles.each do |article| %>
    16:       <% binding.pry %>
 => 17:       <tr>

pry(#<#<Class:0x00007fd8c12d7f48>>)> step

    168: def safe_concat(value)
 => 169:   raise SafeConcatError unless html_safe?
    170:   original_concat(value)
    171: end

finish

finishはメソッドの実行を終了し、次の行に移動できるコマンドです。step→finishという流れで覚えていただけたらと思います。

    14:   <tbody>
    15:     <% @articles.each do |article| %>
    16:       <% binding.pry %>
 => 17:       <tr>

pry(#<#<Class:0x00007fd8c12d7f48>>)> step

    168: def safe_concat(value)
 => 169:   raise SafeConcatError unless html_safe?
    170:   original_concat(value)
    171: end

pry(#<#<Class:0x00007fd8c12d7f48>>)> finish

    14:   <tbody>
    15:     <% @articles.each do |article| %>
    16:     <% binding.pry %>
    17:       <tr>
 => 18:         <td><%= article.url %></td>

continue

continueを実行することで、プログラムの実行を続けつつ、pryモードを終了させることができます。exitでも構いません。

    14:   <tbody>
    15:     <% @articles.each do |article| %>
    16:       <% binding.pry %>
 => 17:       <tr>

pry(#<#<Class:0x00007fd8c12d7f48>>)> continue

Rendered articles/index.html.erb within layouts/application (Duration: 292219.9ms | Allocations: 592597)
[Webpacker] Everything's up-to-date. Nothing to do
Completed 200 OK in 292283ms (Views: 292264.1ms | ActiveRecord: 7.9ms | Allocations: 600860)
繰り返し処理の中に記載した場合はcontinueを繰り返し処理分打たなくてはなりません。その場合は、exit!を入力しましょう。

break

breakはブレークポイントをすることができるコマンドです。「break 行数」を入力することで自在にブレークポイントを移動できます。

    14:   <tbody>
    15:     <% @articles.each do |article| %>
    16:     <% binding.pry %>
 => 17:       <tr>
    18:         <td><%= article.url %></td>
    19:         <td><%= article.post %></td>
    20:         <td><%= link_to 'Show', article %></td>
    21:         <td><%= link_to 'Edit', edit_article_path(article) %></td>
    22:         <td><%= link_to 'Destroy', article, method: :delete, data: { confirm: 'Are you sure?' } %></td>

pry(#<#<Class:0x00007f95e931c3c0>>)> break 20

    18: <td><%= article.url %></td>
    19: <td><%= article.post %></td>
 => 20: <td><%= link_to 'Show', article %></td>
    21: <td><%= link_to 'Edit', edit_article_path(article) %></td>
    22: <td><%= link_to 'Destroy', article, method: :delete, data: { confirm: 'Are you sure?' } %></td>

 

このように「pry-byebug」を使用することで、「pry-rails」の1歩上のデバッグができるようになります。

pry-rails vs pry-byebug

ここまでで似たような2つのGem「pry-rails」と「pry-byebug」を紹介しました。では、どちらを使うのが良いのでしょうか?

どちらも使用してみて、個人的には「pry-byebug」一択かなと思います。

何より、自由にブレークポイントを移動できる点が非常に便利で、「pry-byebug」さえあれば1通りのデバッグは可能です。

Railsの開発ではぜひ「pry-byebug」を導入しておきましょう。

参考

library irb:
https://docs.ruby-lang.org/ja/latest/library/irb.html

pry Github:
https://github.com/pry/pry

pry-buybug Github:
https://github.com/deivid-rodriguez/pry-byebug

TIM Labs:
http://labs.timedia.co.jp/2011/12/rubyist-should-use-pry.html

今更聞けないpryの使い方と便利プラグイン集:
https://qiita.com/k0kubun/items/b118e9ccaef8707c4d9f#cd-ls-tab%E8%A3%9C%E5%AE%8C

 

 

今回は、Railsの開発で活躍するデバッグ手法5選を紹介しました。これらが使いこなせるだけでもアプリケーション開発がグッとしやすくなります。ぜひ理解した上で開発にあたっていただけたらと思います。