autotestのGrowl通知をカスタマイズする
以下の設定ファイルでGrowlを有効にしたautotestは、テスト結果をGrowlメッセージとして教えてくれるのでとても快適なのだが、いくつか気になる点も出てきた。
# autotest設定ファイル: ~/.autotest require 'autotest/growl'
- 現状では、failuresやerrorsが発生した時は毎回Growlメッセージが通知されるが、テストが成功を繰り返す場合は何も通知されない。
- 成功している状態は当然の状況なのだから、この仕様は無駄が無くて良いのだが、テストに不慣れな現状では、毎回テスト結果を通知して欲しい気分だ。
- 通知されるGrowlメッセージは「Test Failed」か「Test Passed」なので、failuresとerrorsの件数はターミナルのテスト結果を見るまで確認できない。
- Growlメッセージにも「1 tests, 1 assertions, 0 failures, 0 errors」のような情報を付加しておきたい。(問題箇所を特定する為には結局ターミナルで確認する必要があるのだが...。)
そんな要望は、Setting up autotest to use Growl - Knowledge Baseを参考にすることで、叶えることができた。試行錯誤しながら、以下のようにやってみた。
作業前の環境
現在の自分のMacBookは、以下の日記の手順を実行済みの環境だ。
module Autotest::Growlを自分で定義する
- 今まではrequire 'autotest/growl'を有効にして、module Autotest::Growlを取り込んで機能拡張していた。
- さらに~/.autotestファイルに直接module Autotest::Growlを定義することで、独自の機能を追加することができるようだ。
- require 'autotest/growl'は不要になるので、コメントアウトしておいた。有効にしたままではGrowlメッセージが二重に通知されてしまう。
- ついでに、前回のgem install redgreenによって、今後はrequire 'autotest/redgreen'も不要と考え、コメントアウトしておいた。
#a -*- ruby -*- # require 'autotest/autoupdate' # require 'autotest/camping' # require 'autotest/cctray' # require 'autotest/emacs' # require 'autotest/fixtures' # require 'autotest/growl' # require 'autotest/heckle' # require 'autotest/html_report' # require 'autotest/kdenotify' # require 'autotest/menu' # require 'autotest/migrate' # require 'autotest/notify' # require 'autotest/pretty' # require 'autotest/redgreen' # require 'autotest/screen' # require 'autotest/shame' # require 'autotest/snarl' # require 'autotest/timestamp' # Autotest::AutoUpdate.sleep_time = 60 # Autotest::AutoUpdate.update_cmd = 'svn up' # Autotest::Emacs.client_cmd = 'emacsclient -e' # Autotest::Heckle.flags << '-t test/**/*.rb' # Autotest::Heckle.klasses << 'MyClass' # Autotest::Shame.chat_app = :adium module Autotest::Growl def self.growl(title, msg, img, pri=0, sticky="") msg += " at #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}" system "growlnotify -n autotest #{title} -m #{msg.inspect} --image #{img} -p #{pri} #{sticky}" end Autotest.add_hook :ran_command do |at| results = [at.results].flatten.join("\n") output = results.slice(/(\d+)\s+tests,\s*(\d+)\s+assertions,\s*(\d+)\s+failures,\s*(\d+)\s+errors/) if output if $~[3].to_i > 0 || $~[4].to_i > 0 growl "Tests Failed", "#{output}", "~/.rails_fail.png", 2, "-s" else growl "Tests Passed", "#{output}", "~/.rails_ok.png" end else growl "Tests Failed", "errors", "~/.rails_fail.png", 2, "-s" end end end
アイコン画像のダウンロード
cd ~ curl http://blog.internautdesign.com/files/rails_fail.png > .rails_fail.png curl http://blog.internautdesign.com/files/rails_ok.png > .rails_ok.png
メッセージの確認
以上の設定で、Growlメッセージは以下のようになった。
何が行われているのか?
growlnotifyコマンドは自在にGrowlメッセージを作る
$ sleep 10; growlnotify -n test "タイトル" -m "メッセージ" --image ~/.rails_fail.png -p 2 -s
- ターミナルから上記コマンドを実行すると、10秒後に以下のメッセージがGrowlから通知される。
- つまり「sleep 10;」の部分を「make;」のように差し替えれば、makeコマンドが完了した時に、Growlメッセージが通知されることになるのだ。便利!(以下は利用例)
$ make; growlnotify "make完了!"
- オプションの意味は以下のようになっている。
オプション文字 | オプション引数 | 引数の書式 | オプションの効果 |
---|---|---|---|
-n | test | アプリケーション名 | システム環境設定 >> Growl >> アプリケーションに登録される名前になる。(省略時はgrowlnotify) |
--mage | rails_fail.png | 画像へのファイルパス | ファイルパスの画像がアイコンとして表示される。 |
-tまたは無し | "タイトル" | タイトル文字列 | メッセージの1行目に太字で大きく表示される。 |
-m | "メッセージ" | メッセージ文字列 | メッセージの2行目からタイトルよりも細字で小さく表示される。 |
-p | 2 | 優先順位を表す数値またはキーワード | 5段階の優先順位がある。(-2 Very Low, -1 Moderate, 0 Normal, 1 High, 2 Emergency、省略時は0) |
-s | 無し | スティッキー設定*1が有効になる。 |
module Autotest::Growlは、growlnotifyコマンドを利用している
- systemメソッドは引数の文字列をシェルコマンドとして実行する。
module Autotest::Growl def self.growl(title, msg, img, pri=0, sticky="") msg += " at #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}" system "growlnotify -n autotest #{title} -m #{msg.inspect} --image #{img} -p #{pri} #{sticky}" end
- 以下のブロックは、テストが実行された時に呼び出されるようだ。
Autotest.add_hook :ran_command do |at| results = [at.results].flatten.join("\n") output = results.slice(/(\d+)\s+tests,\s*(\d+)\s+assertions,\s*(\d+)\s+failures,\s*(\d+)\s+errors/) if output if $~[3].to_i > 0 || $~[4].to_i > 0 growl "Tests Failed", "#{output}", "~/.rails_fail.png", 2, "-s" else growl "Tests Passed", "#{output}", "~/.rails_ok.png" end else growl "Tests Failed", "errors", "~/.rails_fail.png", 2, "-s" end end end
- [at.results]には、シェルコマンドがテストを実行した時に表示される文字列が行ごとの配列として代入されている。
- 上記を一つの文字列に繋げて、(join(\n))
- 正規表現/(\d+)\s+tests,\s*(\d+)\s+assertions,\s*(\d+)\s+failures,\s*(\d+)\s+errors/にマッチする部分だけ取り出している。(results.slice)
- $~[3]は、正規表現の中の()囲まれた3番目の部分を表現している。つまり、3番目の(\d+)とマッチした部分。ここではfailures直前の数字になるはず。
- 同様に$~[4]は、errors直前の数字になるはず。
- よって、failuresやerrorsが0より大きいかどうかで(if $~[3].to_i > 0 || $~[4].to_i > 0)、以下のgrowlnotifyコマンドが実行されることになる。
# 0より大きい場合 $ growlnotify -n autotest "Tests Failed" -m "18 tests, 89 assertions, 1 failures, 0 errors at ..." --image ~/.rails_fail.png -p 2 -s # 0の場合 $ growlnotify -n autotest "Tests Passed" -m "18 tests, 89 assertions, 0 failures, 0 errors at ..." --image ~/.rails_ok.png -p 0
以上の仕組みを応用すれば、自分好みの条件で、シェルコマンドが返す文字列の特定の部分を、Growlに渡して活用することができる!autotest以外でもいろいろな場面で役立ちそうだ!
*1:メッセージが自動的に消えない。クリックすることで消える。