TL;DR
- コンソール (irb や pry) で例外が発生しても、バックトレースが表示されないので原因がわからないことがある。
- コンソールで例外時のバックトレースを表示するには
バージョン情報
$ ruby -v
ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-darwin18]
$ bin/rails -v
Rails 6.0.1
実例
以下のモデルを作成した。
class Hunter < ApplicationRecord
validates name, presence: true
end
早速 bin/rails c
(irb) で動作を確認してみると謎のエラーが発生した。
irb(main):008:0> Hunter.create!(name: 'クラピカ')
Traceback (most recent call last):
1: from (irb):8
NoMethodError (undefined method `Hunter' for #<Hunter:0x00007f8a322f52d8>)
Hunter#Hunter というメソッドがないって言われる……。なんだこの HUNTER×HUNTER みたいなへんちくりんなエラーは 🤔
それにしても、他に情報が何も出力されないのでエラーの原因がさっぱりわからない。こういうときはメソッドの呼び出し状況であるバックトレースを出力する。Ruby では $@ という特殊変数でバックトレースを参照することができる。
irb(main):002:0> puts $@
/Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/attribute_methods.rb:431:in `method_missing'
/Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:150:in `block in validate'
/Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `each'
/Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `validate'
# 略
/Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/persistence.rb:55:in `create!'
(irb):8:in `irb_binding'
# 略
-e:1:in `<main>'
=> nil
バックトレースに validate
というキーワードがあったのでバリデーション周りを疑ってみる。すると本来シンボルであるはずの validates の引数がシンボルになっていなかったのが原因だと判明した!
class Hunter < ApplicationRecord
# name が self.name すなわち Hunter.name と解釈されて "Hunter" を返していた。
# name を :name に修正した。
validates :name, presence: true
end
しかしこの方法はちょっと不便だ。もう一度 $@
を参照しようとすると空になってしまう。
irb(main):002:0> puts $@
/Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/attribute_methods.rb:431:in `method_missing'
/Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:150:in `block in validate'
/Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `each'
/Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `validate'
# 略
/Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/persistence.rb:55:in `create!'
(irb):8:in `irb_binding'
# 略
-e:1:in `<main>'
=> nil
irb(main):003:0> puts $@
=> nil
そこで irb をより強力にしたツールである pry を使う。Gemfile に pry-rails
を追加して bundle install しておく。
group :development, :test do
gem 'pry-rails'
end
bin/rails c
で動作を確認してみる。
[1] pry(main)> Hunter.create!(name: 'クラピカ')
NoMethodError: undefined method `Hunter' for #<Hunter:0x00007fe654afd088>
from /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/attribute_methods.rb:431:in `method_missing'
wtf
と入力すると、例外のバックトレースが表示される。すべてではなく先頭から数行が表示される。
[2] pry(main)> wtf
Exception: NoMethodError: undefined method `Hunter' for #<Hunter:0x00007fe654afd088>
--
0: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/attribute_methods.rb:431:in `method_missing'
1: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:150:in `block in validate'
2: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `each'
3: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `validate'
4: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/callbacks.rb:429:in `block in make_lambda'
もう一度入力してもバックトレースが表示される。おもしろいことに wtf!
や wtf!!?
のように wtf
の後に !
や ?
をつけると、つけた量に比例して表示されるバックトレースの行数が増える。
[3] pry(main)> wtf!
Exception: NoMethodError: undefined method `Hunter' for #<Hunter:0x00007fe654afd088>
--
0: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/attribute_methods.rb:431:in `method_missing'
1: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:150:in `block in validate'
2: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `each'
3: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `validate'
4: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/callbacks.rb:429:in `block in make_lambda'
5: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/callbacks.rb:201:in `block (2 levels) in halting'
6: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/callbacks.rb:607:in `block (2 levels) in default_terminator'
7: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/callbacks.rb:606:in `catch'
8: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/callbacks.rb:606:in `block in default_terminator'
9: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/callbacks.rb:202:in `block in halting'
バックトレースを最後まで表示したい場合は wtf -v
と入力する。
[4] pry(main)> wtf -v
Exception: NoMethodError: undefined method `Hunter' for #<Hunter:0x00007fe654afd088>
--
0: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/attribute_methods.rb:431:in `method_missing'
1: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:150:in `block in validate'
2: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `each'
3: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `validate'
# 略
73: /Users/killua/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
74: /Users/killua/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
75: -e:1:in `<main>'