テクノロジー
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点を押さえておけば大丈夫です。
- pryデバッグが使用可能なファイル
- 書き方
- デバッグの仕方
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
次に以下のように新規入力フォームから値を入力して送信ボタンを押します。
すると処理がストップし、コンソールに以下のような記述が表示されます。
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です。
※ここではユーザーとブログが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
これで画面を確認すると正常な画面になりました。
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デバッグを使いこなせるようになって、自分でエラーを解決できるようになりましょう。