やりたいこと
- Rubyの
guard
gemとguard-shell
プラグインを使っている - プロジェクト内のファイルを監視し、更新されたときプロジェクト全体をビルドしたい
- 通常、Guardでは1ファイルの更新につきそのファイルを処理するコマンドが1回走るので、
touch hoge.c moge.c
のように同時に複数のファイルが更新された場合、プロジェクト全体をビルドするコマンドが2回走ってしまう - これを1回だけにしたい
解決法
こんな感じでGuardfile
を書いておく:
$last_exec_time = Time.now
guard :shell do
watch(/.*\.(c|h)$/) do |m|
puts "Changed: #{m[0]}"
# 前回実行から1秒以内なら実行しない
if Time.now - $last_exec_time < 1
puts "Skip"
next
end
ret = `sleep 2; date` # 実験用のコマンド
$last_exec_time = Time.now
ret
end
end
グローバル変数$last_exec_time
を使い、前回実行から1秒以内ならスキップするようにしている。
ビルドコマンドに1秒以上かかってもちゃんとスキップされている。
やりたいことは非常によくあることな割に、ダーティなやり方である。
もっといい方法があったら教えてください。
[追記]
guard-process
やguard-rake
を使えば今回のような問題は生じない。
guard-process
ならGuardfile
内にコマンドを書けるので手軽にguard-shell
の代用にできる。
そもそもguard-shell
は作りがguardの思想から少し外れているような気がする。
watch
のブロックは
When you add a block to the watch expression, you can modify the file name that has been detected before sending it to the plugin for processing:
(中略)
NOTE: Normally, most plugins expect the block to return a path or array of paths
https://github.com/guard/guard/wiki/Guardfile-DSL---Configuring-Guard#watch
と書いてある通り、変更されたファイル名から処理すべきファイル名に変換するのが本来の目的であり、その中でシェル呼び出しすればそりゃ複数回呼ばれるよな。