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

Ruby の Object Data Dumper

プリントデバッグが大好きだ!!!何はなくともとりあえず標準出力に吐いてデバグ!!!!!!!!!っていうようなことはみなさんにおかれましても少なからずあるのではないでしょうか。

プリントデバッグを行う場合、単純に変数に何が入っているかというよりも、オブジェクトの状態とかを見たいことが多いかと思う。
そういうときのためのライブラリとして、Ruby だと、p, pp は組み込み、ap はもう定番化しててみんな使ってると思う。
で、ddp ってのもある。http://hisaichi5518.hatenablog.jp/entry/2014/11/05/154949

適当なクラスをでっち上げてそれぞれに食わせてみる

こんなクラスがあったとする。
インスタンスを作って、インスタンス自体、インスタンス変数、インスタンスメソッドをそれぞれのライブラリに食わせてみて、どんな感じに表示されるか見てみませう。

class Ore
end

class Oretachi < Ore
  attr_accessor :data
  def initialize
    @data = [
      { name: '1号', entry_count: 4, bookmark_count: 26 },
      { name: '2号', entry_count: 3, bookmark_count: 7 },
      { name: '3号', entry_count: 3, bookmark_count: 16 },
      { name: '4号', entry_count: 2, bookmark_count: 24 },
      { name: '5号', entry_count: 2, bookmark_count: 21 },
      { name: '6号', entry_count: 2, bookmark_count: 182 },
      { name: '7号', entry_count: 2, bookmark_count: 218 },
    ]
  end

  def [](name)
    @data.select { |ore| ore[:name] == name }.first
  end
end

インスタンス自体

内包するインスタンス変数をキレイに見たい場合だと pp に軍配が上がる感じがします。でもなんかインデントとか微妙だしイマイチ。
で、ddp はインスタンスのクラスの継承関係と、include されているモジュールの一覧まで見られるのでそういう用途においては他のライブラリの出番がなさそう。

require 'pp'
require 'ap'
require 'ddp'

oretachi = Oretachi.new

#### p
p oretachi
#=> #<Oretachi:0x007fef240a13b8 @data=[{:name=>"1号", :entry_count=>4, :bookmark_count=>26}, {:name=>"2号", :entry_count=>3, :bookmark_count=>7}, {:name=>"3号", :entry_count=>3, :bookmark_count=>16}, {:name=>"4号", :entry_count=>2, :bookmark_count=>24}, {:name=>"5号", :entry_count=>2, :bookmark_count=>21}, {:name=>"6号", :entry_count=>2, :bookmark_count=>182}, {:name=>"7号", :entry_count=>2, :bookmark_count=>218}]>

#### pp
pp oretachi
#=> #<Oretachi:0x007fef240a13b8
 @data=
  [{:name=>"1号", :entry_count=>4, :bookmark_count=>26},
   {:name=>"2号", :entry_count=>3, :bookmark_count=>7},
   {:name=>"3号", :entry_count=>3, :bookmark_count=>16},
   {:name=>"4号", :entry_count=>2, :bookmark_count=>24},
   {:name=>"5号", :entry_count=>2, :bookmark_count=>21},
   {:name=>"6号", :entry_count=>2, :bookmark_count=>182},
   {:name=>"7号", :entry_count=>2, :bookmark_count=>218}]>

#### ap
ap oretachi
#=> #<Oretachi:0x007fef240a13b8 @data=[{:name=>"1号", :entry_count=>4, :bookmark_count=>26}, {:name=>"2号", :entry_count=>3, :bookmark_count=>7}, {:name=>"3号", :entry_count=>3, :bookmark_count=>16}, {:name=>"4号", :entry_count=>2, :bookmark_count=>24}, {:name=>"5号", :entry_count=>2, :bookmark_count=>21}, {:name=>"6号", :entry_count=>2, :bookmark_count=>182}, {:name=>"7号", :entry_count=>2, :bookmark_count=>218}]>

#### ddp
Ddp.p oretachi
#=> Oretachi < Ore < Object < BasicObject {
  included_modules:
    PP::ObjectMixin
    Kernel
  inspect:
    #<Oretachi:0x007fef240a13b8 @data=[{:name=>"1号", :entry_count=>4, :bookmark_count=>26}, {:name=>"2号", :entry_count=>3, :bookmark_count=>7}, {:name=>"3号", :entry_count=>3, :bookmark_count=>16}, {:name=>"4号", :entry_count=>2, :bookmark_count=>24}, {:name=>"5号", :entry_count=>2, :bookmark_count=>21}, {:name=>"6号", :entry_count=>2, :bookmark_count=>182}, {:name=>"7号", :entry_count=>2, :bookmark_count=>218}]>
}

インスタンス変数

オブジェクトの構造を確認したい場合においては、優勝 ap, 次点 pp という感じでしょうか。
でもなんか ap もインデントとかキレイじゃなくてちょっと微妙な気がしてます。もうすこし階層の深いオブジェクトだとちょい見づらいことがあるのだよなー。

require 'pp'
require 'ap'
require 'ddp'

oretachi = Oretachi.new

#### p
p oretachi.data
#=> [{:name=>"1号", :entry_count=>4, :bookmark_count=>26}, {:name=>"2号", :entry_count=>3, :bookmark_count=>7}, {:name=>"3号", :entry_count=>3, :bookmark_count=>16}, {:name=>"4号", :entry_count=>2, :bookmark_count=>24}, {:name=>"5号", :entry_count=>2, :bookmark_count=>21}, {:name=>"6号", :entry_count=>2, :bookmark_count=>182}, {:name=>"7号", :entry_count=>2, :bookmark_count=>218}]

#### pp
pp oretachi.data
#=> [{:name=>"1号", :entry_count=>4, :bookmark_count=>26},
 {:name=>"2号", :entry_count=>3, :bookmark_count=>7},
 {:name=>"3号", :entry_count=>3, :bookmark_count=>16},
 {:name=>"4号", :entry_count=>2, :bookmark_count=>24},
 {:name=>"5号", :entry_count=>2, :bookmark_count=>21},
 {:name=>"6号", :entry_count=>2, :bookmark_count=>182},
 {:name=>"7号", :entry_count=>2, :bookmark_count=>218}]

#### ap
ap oretachi.data
#=> [
    [0] {
                  :name => "1号",
           :entry_count => 4,
        :bookmark_count => 26
    },
    [1] {
                  :name => "2号",
           :entry_count => 3,
        :bookmark_count => 7
    },
    [2] {
                  :name => "3号",
           :entry_count => 3,
        :bookmark_count => 16
    },
    [3] {
                  :name => "4号",
           :entry_count => 2,
        :bookmark_count => 24
    },
    [4] {
                  :name => "5号",
           :entry_count => 2,
        :bookmark_count => 21
    },
    [5] {
                  :name => "6号",
           :entry_count => 2,
        :bookmark_count => 182
    },
    [6] {
                  :name => "7号",
           :entry_count => 2,
        :bookmark_count => 218
    }
]

#### ddp
Ddp.p oretachi.data
#=> Array < Object < BasicObject {
  included_modules:
    Enumerable
    PP::ObjectMixin
    Kernel
  inspect:
    [{:name=>"1号", :entry_count=>4, :bookmark_count=>26}, {:name=>"2号", :entry_count=>3, :bookmark_count=>7}, {:name=>"3号", :entry_count=>3, :bookmark_count=>16}, {:name=>"4号", :entry_count=>2, :bookmark_count=>24}, {:name=>"5号", :entry_count=>2, :bookmark_count=>21}, {:name=>"6号", :entry_count=>2, :bookmark_count=>182}, {:name=>"7号", :entry_count=>2, :bookmark_count=>218}]
}

インスタンスメソッド

ddp すごい。source_location という項目で、そのメソッドがどこの何行目に定義されているかわかる。これはクソ便利だ。

require 'pp'
require 'ap'
require 'ddp'

oretachi = Oretachi.new

#### p
p oretachi.method(:[])
#=> #<Method: Oretachi#[]>

#### pp
pp oretachi.method(:[])
#=> #<Method: Oretachi#[]>

#### ap
ap oretachi.method(:[])
#=> Oretachi#[](name)

#### ddp
Ddp.p oretachi.method(:[])
#=> Method < Object < BasicObject {
  included_modules:
    PP::ObjectMixin
    Kernel
  inspect:
    #<Method: Oretachi#[]>
  source_location: ["-", 19]
}

まとめとか

構造をいい感じに表示 継承関係がわかる include されているモジュールがわかる メソッドの定義場所がわかる 組み込み require
p × × × × 不要
pp × × ×
ap × × × ×
ddp × ×
  • ddp がすごく便利
  • ddp がオブジェクトの構造をもっといい感じに表示してくれたら最高
  • Ddp.p って書くのダルい

  • ap, ddp は組み込みではないのでインストールが必要

    • bundle exec でどうこうしたい場合には一時的にでも Gemfile に書いておくなどしなければならずダルい
  • ap は Hash とか Array を直接食わせたらいい感じなのに、インスタンス自体を食わせたときには内包するインスタンス変数の内容の表示が残念なのでもうちっとなんとかならんか