PryでTempfileオブジェクトを評価するとFileと表示される
PryでTempfileオブジェクトを評価するとFileと表示される
TL;DR
- Pryで
Tempfileオブジェクトを評価すると、Fileと表示される - これはPryのデフォルトインスペクタがppであり、
Tempfileクラスがprint_prettyの処理をFileクラスに移譲しているから
現象
PryでTempfileオブジェクトを作って評価すると、Fileと表示される。
[1] pry(main)> f = Tempfile.new
=> #<File:/var/folders/5x/jy4d20b97jgcsgf4mt7dwwqr0000gn/T/20180808-1401-kvolc2>
もちろんTempfileクラスはFileクラスではなく、inspectするとちゃんとTempfileと表示される
[2] pry(main)> f.inspect
=> "#<Tempfile:/var/folders/5x/jy4d20b97jgcsgf4mt7dwwqr0000gn/T/20180808-1401-kvolc2>"
そもそもTempfileクラスはFileクラスとis aの関係にない。
[3] pry(main)> f.is_a?(File)
=> false
irbではこのようなことは起きない。
irb(main):001:0> require "tempfile"
=> true
irb(main):002:0> f = Tempfile.new
=> #<Tempfile:/var/folders/5x/jy4d20b97jgcsgf4mt7dwwqr0000gn/T/20180808-1422-in132m>
原因
TL;DRにも書いたが、原因はPryのデフォルトインスペクタがppであり、ppがオブジェクトのpretty_printを要求するが、TempfileがFileのDelegateClassになっており、inspectは自前で持っているが、pretty_printは持っていないので、その処理をFileに移譲してしまうから。
従って、irbでもデフォルトインスペクタをppにすると同じことが起きる。
$ irb --inspect pp
irb(main):001:0> require "tempfile"
=> true
irb(main):002:0> f = Tempfile.new
=> #<File:/var/folders/5x/jy4d20b97jgcsgf4mt7dwwqr0000gn/T/20180808-1493-fvvhrn>
irb(main):003:0>
逆に、Tempfileにpretty_printを実装すればPryでもこの問題は起きない。
[1] pry(main)> f = Tempfile.new # Fileと表示される
=> #<File:/var/folders/5x/jy4d20b97jgcsgf4mt7dwwqr0000gn/T/20180808-1501-1z0rdk5>
[2] pry(main)> class Tempfile
[2] pry(main)* def pretty_print(q) # Tempfileクラスにpretty_printを追加
[2] pry(main)* q.text inspect
[2] pry(main)* end
[2] pry(main)* end
=> :pretty_print
[3] pry(main)> f # Tempfileと表示される
=> #<Tempfile:/var/folders/5x/jy4d20b97jgcsgf4mt7dwwqr0000gn/T/20180808-1501-1z0rdk5>
[4] pry(main)>
また、Pryのデフォルトインスペクタをsimpleにしてもこの問題は起きない。
[1] pry(main)> f = Tempfile.new
=> #<File:/var/folders/5x/jy4d20b97jgcsgf4mt7dwwqr0000gn/T/20180808-1569-1sbfpbu>
[2] pry(main)> f # デフォルトではFileと評価される
=> #<File:/var/folders/5x/jy4d20b97jgcsgf4mt7dwwqr0000gn/T/20180808-1569-1sbfpbu>
[3] pry(main)> change-inspector simple
Switched to the 'simple' inspector!
[4] pry(main)> f # simpleインスペクタではTempfileと評価される
#<Tempfile:/var/folders/5x/jy4d20b97jgcsgf4mt7dwwqr0000gn/T/20180808-1569-1sbfpbu>
まとめ
ちょっとTempfileについて調べてたらPryの変な挙動を見つけたのでまとめてみた。Tempfileだけでなく、inpsectは実装しているがpretty_printは実装されていないDelegateClass全般で同じ問題がおきるはず。一番まっとうな解決策はrequire "pp"されたときに、Tempfileにpretty_printメソッドを追加することなんだろうけど、特に実害も無いし、issue立てたりプルリクを作るほどでも無い気がする・・・?
その他
Pryのインスペクタにはもう一つ、clippedがある。これでTempfileオブジェクトのinspectを評価するとStringになってしまう。
[1] pry(main)> f = Tempfile.new
=> #<File:/var/folders/5x/jy4d20b97jgcsgf4mt7dwwqr0000gn/T/20180808-1591-1uujsu6>
[2] pry(main)> f.inspect # デフォルトインスペクタではTempfileと表示される
=> "#<Tempfile:/var/folders/5x/jy4d20b97jgcsgf4mt7dwwqr0000gn/T/20180808-1591-1uujsu6>"
[3] pry(main)> change-inspector clipped
Switched to the 'clipped' inspector!
[4] pry(main)> f.inspect # clippedインスペクタではStringと表示されてしまう。
#<String:0x7fe23d102710>
[5] pry(main)>
これは、Pryのclippedインスペクタが、60文字を超えるとオブジェクトのクラス名+IDを表示する仕様だから。
[1] pry(main)> change-inspector clipped
Switched to the 'clipped' inspector!
[2] pry(main)> "X"*58
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
[3] pry(main)> "X"*59
#<String:0x7ff6a60a5c18>
[4] pry(main)>
そういう仕様だと言われたらそうなんだけど、Tempfileオブジェクトをinspectした結果が「String」とか言われたらちょっとびっくりしない?
A Robot’s Sigh