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

Ruby (Rails) でエンバグしたときのもがきかた

当てずっぽうで手直ししていると収拾がつかなくなるし、後で自分の書いたコード見る人にとってもよくないので、なるべく原因を論理的に究明し、解決していきます。 ということで、適当デバッガから脱すべく幾つか調べてみました。

■ その変数やメソッドが他に使われてる例を探す

プロジェクト内をgit grep

■ 自分のメソッド名を出力して足跡を探る

puts "========== #{self.class.name}##{__method__} =========="

or

Rails.logger.debug "========== #{self.class.name}##{__method__} =========="

もっと派手

Rails.logger.info "\n==========\n========== #{self.class.name}##{__method__}\n==========\n"

これを通ってそうなメソッドに片っ端から入れまくる

■ RubyMineでは、Command+Clickでそのメソッドやクラス、オブジェクトの定義にジャンプできる

超便利

これだけのためだけにRubyMineを常につけているレヴェル

Atomでも、右クリックでGo to Declarationを選択でおk

Vimでもctagsとかでできる筈

■ クラス名を知る

Rails.logger.debug hoge.class

RSpec ログを吐くようにする

spec/rails_helper.rbRSpec.configure do |config|の前に

Rails.logger = Logger.new(STDOUT)
ActiveRecord::Base.logger = Logger.new(STDOUT)

と書くと、アプリケーションログと実行したSQLを吐いてくれる (何故か自分の場合アプリケーションログがうまくだせなかった

set_trace_funcで発火したメソッドをトレースする

http://stackoverflow.com/questions/2219461/how-do-i-log-every-method-thats-called-in-a-ruby-program

log_file = File.open('/Users/your_name/Desktop/log.txt', 'w')
set_trace_func proc { |event, file, line, id, binding, classname|
  log_file.printf("%8s %s:%-2d %10s %8s\n", event, file, line, id, classname) if event == 'call' unless file =~ /eval|vendor|rbenv|uploaders|initializers|support|db|rack/ # ログから外したい名前を並べておく
}

メソッド名とクラスだけでいいならこんな感じで良いと思う↓

log_file = File.open('/Users/your_name/Desktop/hoge.txt', 'a')
set_trace_func proc { |event, file, line, id, binding, classname|
    log_file.printf("%8s#%10s\n", classname, id) if event == 'call' unless file =~ /eval|vendor|rbenv|uploaders|initializers|support|db|rack/ # ログから外したい名前を適当に並べておく
}

set_trace_funcは最後まで実行されるっぽいので、きりのいいところでraise "エラーメッセージ"とでも書いて終了させましょう。

更に↑のhoge.txtをVimで編集していく。
下のコマンドは正規表現で要る/要らない言葉が含まれた行を削除するやつ。

" 削除
:%g/^.*<要らないクラス名とか>.*$/d
" それ以外削除
:%v/^.*<見たいクラス名とか>.*$/d

Rails.logger.info "xxx"ってやれ

コードにRails.logger.info "xxx"のようにログ出力を埋め込んで、処理が実行されたタイミングやメソッドの戻り値、DBの値などを出力してみる

RSpec 失敗した時のlogs/test.logを確認してみる

ログに出力された情報からテストが落ちた原因が推測できるかも

RSpec DatabaseCleanerの設定をdeletionからtruncationにしてテストの度にDBを完全にリセットしてみる

DatabaseCleaner.strategy = :deletion
=> DatabaseCleaner.strategy = :truncation

■ pryのshow-sourceでメソッドの定義を表示する

[1] pry(ClassName)> show-source connection.get

From: /Users/your_name/Codes/hoge/vendor/bundle/ruby/2.3.0/gems/faraday-0.9.2/lib/faraday/connection.rb @ line 139:
Owner: Faraday::Connection
Visibility: public
Number of lines: 6

def #{method}(url = nil, params = nil, headers = nil)
  run_request(:#{method}, url, nil, headers) { |request|
    request.params.update(params) if params
    yield(request) if block_given?
  }
end

■ ググった時、そこに書いてあるケースを自分でも実行してみる

binding.pryしてcallerって打つ