テクノロジー

2016年12月20日

エラーの原因を突き止めるためにデバッグをする

今回はRubyonRailsに置けるデバッグツールの一つであるpryデバックについての使い方・こつを紹介していきます。デバッグの知識が少しあるだけで開発の速度はかなり上がります。

pryデバッグとは

Ruby on Railsにおけるデバッグツールのことです。

デバッグツールを入れる最大のメリットは、自分でエラーを発見することができることです。
特に初学者の方はエラー画面が1回でも出てしまうとイライラしたり、どこを見たら良いかわからずに立ち止まってしまうケースが多いです。
誰もが一度は通る道なので安心してください。
デバッグツールは最初から入れている方はそんなにいないのですが、私はむしろ最初からデバッグツールを入れて自分で解決する力をつけておいた方が良いと思っています。
デバッグのこつを掴みましょう!

そこで今回はデバッグツールの一つであるpryデバッグについての使い方を紹介していきます。

pryデバッグのインストール

Gemfileに以下のgemを記述します。

group :development, :test do
  gem 'pry-rails'

  gem 'pry-doc'

  gem 'pry-byebug'
end

そしてコンソールにて以下を実行すれば導入完了です。

bundle install

pryデバッグの使い方

では実際にpryデバッグを使ってみましょう。今回はScaffoldでブログ投稿機能を作った上でpryデバッグを試してみます。
使い方としては、以下3点を押さえておけば大丈夫です。

  1. pryデバッグが使用可能なファイル
  2. 書き方
  3. デバッグの仕方

1. デバッグが使用可能なファイル

Railsアプリケーション内の

  • モデル
  • ビュー
  • コントローラ

に使用可能です。

2. 書き方

調べたい変数の中身や処理を止めたい部分に

binding.pry

と記述します。モデルやビューは.rbファイルなので上記の書き方で問題ないです。

一方でビューはhtml.erbファイルなので以下のように<% %>で囲わなければなりません。

<% binding.pry %>

3. デバッグの仕方

例えばblogs_controllerのindexアクション内にbinding.pryを仕込んでおきます。

(app/controllers/blogs_controller.rb)

def index
  @blogs = Blog.all
  binding.pry
end

次にサーバーを立ち上げます。

bin/rails s

そして以下のURLにアクセスしてみてください。処理が止まると思われます。

localhost:3000/blogs

そこでコンソールを見てみると、

From:(省略) BlogsController#index

    6: def index
    7:   @blogs = Blog.all
    8:   binding.pry
 => 9: end

[1] pry(#<BlogsController>)>

こんな記述があるので、以下のように実行します。

[1] pry(#<BlogsController>)> @blogs

Railsコンソールで上記を実行するとnilが返ってきますが、pryデバックでは以下のようにブログのすべてのデータが表示されます。

Blog Load (8.9ms)  SELECT "blogs".* FROM "blogs"
=> [#<Blog:0x007ff4f9ac5080
  id: 3,
  title: "hoge",
  content: "hogehoge",
  created_at: Sun, 02 Oct 2016 11:08:40 UTC +00:00,
  updated_at: Sun, 02 Oct 2016 11:08:40 UTC +00:00>,
 #<Blog:0x007ff4f9ac4f40
  id: 4,
  title: "vdasdvasv",
  content: "dvsavdsavsav",
  created_at: Sun, 02 Oct 2016 11:58:35 UTC +00:00,
  updated_at: Sun, 02 Oct 2016 11:58:35 UTC +00:00>]

pryデバッグは処理を流しながら行うデバッグなので自分がコントローラやモデルに定義した変数の中身やメソッドを簡単に確認することができます。
これがRailsコンソールとの大きな違いです。

つまりpryデバッグは「リアルタイム形式のRailsコンソール」と呼べるでしょう。

ここまでできたら処理をスタートさせます。何もしないと処理がずっと止まりっぱなしになってしまいます。
処理のスタートはRailsコンソール同様、exitでpryデバッグから抜けることで可能です。

[2] pry(#<BlogsController>)> exit

さらに定義したbinding.pryを消すのを忘れないように気をつけましょう。
消すのを忘れるとlocalhost:3000/blogsにアクセスした際にまた処理が止まってしまいます。

(app/controllers/blogs_controller.rb)

def index
  @blogs = Blog.all
  binding.pry #←消し忘れに気をつけよう。
end

pryデバッグを使いこなす!

デバッグの仕方がわかったところで以下3つの問題について解決していきましょう。

paramsの正体

Railsアプリケーションでは、ブラウザから送信された情報はparamsという乗り物に乗ってコントローラまでたどり着きます。
それを実際に確認していきましょう。

例えばblogsコントローラのcreateアクションに、以下のようにbinding.pryを仕込みます。

(app/controllers/blogs_controller.rb)

def create
  @blog = Blog.new(blog_params)
  binding.pry
  if @blog.save
    redirect_to @blog, notice: 'Blog was successfully created.'
  else
    render :new
  end
end

次に以下のように新規入力フォームから値を入力して送信ボタンを押します。

https://diveintocode.gyazo.com/6f7446c11fec21840f98c957691c46ae

すると処理がストップし、コンソールに以下のような記述が表示されます。

    26: def create
    27:   @blog = Blog.new(blog_params)
    28:   binding.pry
 => 29:   if @blog.save
    30:     redirect_to @blog, notice: 'Blog was successfully created.'
    31:   else
    32:     render :new
    33:   end
    34: end

表示されたところで、paramsがcreateアクションに届いているか、その中身はどうなっているかを確認しましょう。

[1] pry(#<BlogsController>)> params

するとこんなレスポンスが返ってきます。

=> {"utf8"=>"✓", "authenticity_token"=>"yrqU5uZjb04hpVwRzZdu1Re/+71JYJbT0O4irskeqFa/Gkl4kfE0GsZuej0IQrvzIk6NCCot7ezb/7p0OR3slA==", "blog"=> {"title"=>"aaaa", "content"=>"bbbb"}, "commit"=>"Create Blog", "controller"=>"blogs", "action"=>"create"}

paramsの中身は入力したtitleとcontentの値だけではなく、リクエスト強要を防止する「authenticity_token」やアクセス先コントローラ名、アクション名などの情報も乗っています。
続けて以下を実行しましょう。

[2] pry(#<BlogsController>)> blog_params

するとtitleとcontentの値だけが絞り込まれた状態のハッシュが表示されます。

=> {"title"=>"aaaa", "content"=>"bbbb"}

このことから、blog_paramsはストロングパラメータを行っているメソッドということがわかります。
最後に@blogの中身を確認してみましょう。

[6] pry(#<BlogsController>)> @blog
=> #<Blog:0x007ffed9052258 id: nil, title: "aaaa", content: "bbbb", created_at: nil, updated_at: nil>

このようにストロングパラメータを介した値をブログのインスタンスに格納したことで、titleとcontentだけ値が入ってそれ以外はnilという不自然な形のものが変数に入っているのです。

NoMethodErrorの解決

Railsアプリケーションを開発していると必ずこのエラーに遭遇しますね。NoMethodErrorです。

https://diveintocode.gyazo.com/33aa67bd56eb2fbd124db3d4be192ff8

※ここではユーザーとブログが1対多の関係になっています。さらにblogsコントローラのindexアクションでblogsテーブルからすべてのデータを引っ張ってきて、ビューでeachメソッドを使ってタイトルや内容、投稿者を表示しています。

こんな時こそpryデバッグを使ってエラーを解決していきましょう。

まず考えなければいけないのが「変数の中身」です。
ビューの中に登場している変数は「@blogs」と「blog」です。ただ、「blog」という変数はeachメソッドで展開するための変数なのでNoMethodErrorには関係なさそうです。
なので「@blogs」の中身が何かおかしいのではないか?ということになります。

そこでpryデバッグの登場です。「@blogs」はコントローラで値を取得しているのでblogsコントローラに以下のように仕込みます。

def index
  @blogs = Blog.all
  binding.pry
end

次にコンソールにてサーバを立ち上げます。

bin/rails s

そしてURLに以下を貼り付けてアクセスした後、コンソールに戻ってください。

localhost:3000/blogs

すると処理が止まるのが確認できます。

    5: def index
    6:   @blogs = Blog.all
    7:   binding.pry
 => 8: end

そこで変数の中身を見てみましょう。

 [1] pry(#<BlogsController>)> @blogs
  Blog Load (2.1ms)  SELECT "blogs".* FROM "blogs"
=> [#<Blog:0x007fa3e9c2d870
  id: 1,
  title: "dvsavasv",
  content: "vdsavsa",
  user_id: nil,
  created_at: Sun, 21 Aug 2016 08:44:50 UTC +00:00,
  updated_at: Fri, 30 Sep 2016 06:21:08 UTC +00:00>,
 #<Blog:0x007fa3e9c2d690
  id: 2,
  title: "vdasv",
  content: "vdsavd",
  user_id: 2,
  created_at: Fri, 30 Sep 2016 06:21:56 UTC +00:00,
  updated_at: Fri, 30 Sep 2016 06:23:32 UTC +00:00>]

この部分に注目します。

user_id: nil

外部キーがnilになっていまね。これが原因でblog.userを実行するとnilが返り、nilに対してnameというメソッドを実行していたのでエラーが起きていたのです。
なので存在するユーザのidを入れてあげれば正常な動作が実現できます。

pryデバッグが動いているコンソールにて以下を実行してください。

[2] pry(#<BlogsController>)> @blog = Blog.find(1)
[3] pry(#<BlogsController>)> @blog.user_id = 1
[4] pry(#<BlogsController>)> @blog.save
[5] pry(#<BlogsController>)> exit

これで画面を確認すると正常な画面になりました。

https://diveintocode.gyazo.com/e6d1abdf0d0786e5e3551f09581be5a5

rubyでもデバッグをする

今回はrailsのプロジェクトにおいてのデバッグでしたがrubyの勉強をしているときにでももちろんデバッグは有効です。
pryをインストールします。

gem install pry

そうしてrubyのファイルに記述します。

test.rbファイル
require 'pry'
a = 2 + 3
binding.pry

実行します

ruby test.rb

===ここから実行のログ===
require 'pry'
a = 2 + 3
 => 16: binding.pry

[1] pry(main)> a
=> 5

このようにrailsに限らずともデバッグは可能です。アルゴリズムの勉強をするときなどある変数に思った通りの値が入っているか確認するときに使ったりしています。他の言語を学習する際もあらかじめデバッグツールについて調べておくと良いでしょう。

いかがでしたでしょうか?自分でデバッグしてエラーを解決することはとても重要なことです。
皆さんもpryデバッグを使いこなせるようになって、自分でエラーを解決できるようになりましょう。

ダイビックのことをもっと知ってみませんか?