パーフェクト Ruby のサンプルコードにバグを見っけた。

パーフェクト Ruby の初版、p.486 にバグを見っけた。
公式の正誤表は見当たらなかったので記事にしておく。

capistrano の capture 関数のサンプルとしてリスト 14.14 にこんな記述がある。

task :log_size_watcher do
  log_file = "#{deploy_to}/shared/log/development.log"
  ls_result = capture("ls -l #{log_file}")
  file_size = ls_result.squeeze.split(/ /)[4].to_i

  p "file size alert" if file_size > 100
end

リモートホストで ls -l を実行して、ログファイルが 100 バイトより大きくなっていたら
アラートを表示しようとしています。

ですが、このサンプルはうまく動きません。

String#squeeze しちゃダメ

サンプルコードでは ls -l の結果からファイルサイズを抜き出すときに
squeeze して split してファイル列を取り出しています。

String#squeeze はある文字列の中の連続する文字をひとつにまとめます*1

"yellow moon".squeeze                  #=> "yelow mon"
"  now   is  the".squeeze(" ")         #=> " now is the"
"putters shoot balls".squeeze("m-z")   #=> "puters shot balls"

おそらく split をする前に連続するスペースをまとめるために squeeze を呼んでいるのだと思いますが、
このとき、ファイルサイズに連続した数字が並ぶと桁が変わってしまいます。
例えば、次のようなファイルがあるとどうなるでしょうか。

$ ls -l devel
-rw-r--r--  1 tkomiya tkomiya 1000000  524 20:34 development.log

なんと、ログファイルは 100バイト以上にも関わらずアラートが出力されません。

というわけで、この部分は squeeze を使わずに

  file_size = ls_result.split(/ +/)[4].to_i

としてスペース複数文字で split するのがよさそうです。

そもそも。

capture 関数は先頭のサーバしか見に行ってくれないので、
複数のサーバをデプロイ対象にしていても、二番目以降のホストのログファイルチェックはしてくれません。

正しくファイルサイズをチェックするにはどうするといいんですかね。
capistrano を使って実現する場合、僕は parallel を使う方法しか思いつきませんでした。

*1:めったに使わないメソッドなので、最初に読んだ時、サンプルコードをぱっと理解できませんでした orz