Grails Improx プラグイン 0.1 (コードネーム GrailsServ) リリースしました!
G*アドベントカレンダ2012の7日目、12/7担当のnobeansです。
さて、本日、Improx(インプロックス)というGrailsプラグインをリリースしましたので、ちょっとここで長文自慢しておきつつ、アドベントカレンダの記事に代えさせていただきます。
Grailsのインタラクティブモードはすっごく便利
Grailsのインタラクティブモードって知っていますか?
Grailsはコマンドを使って、テンプレートから各種クラスを生成したり、アプリを起動したり、テストを実行したりする、Webアプリケーションフレームワークです。コマンド実行の度にGrailsをイチから起動すると、それはつまりJVMを起動して、数多くの依存jarをロードし、共通初期化処理を行い、やっとコマンド本来の処理を行うという流れで大変時間がかかります*1。
そこで編み出されたのがインタラクティブモード。
$ grails
とだけコマンドを実行すると、対話型シェルになってそこで連続してコマンドが実行できます。
しかも、同一種類のコマンドを繰り返し実行する場合、2度目以降はロード時間が劇的に短縮できるので簡単なユニットテストとかは瞬殺できます*2。まさに、TDDerにはなくてはならない機能なわけです。更に、Grails2.0からJLineが採用され、コマンドヒストリや補完など大変便利になってきています。素晴らしい。
まあ、昨今そんな珍しいわけでもないですけど、繰り返しテスト実行するためには大変便利なわけです。
(参考)コマンド実行時間@MBPR15
インタラクティブモード? | コマンド | 実行時間 |
no | help | 5 sec |
yes | help | 1 sec |
no | test-app unit: SampleTests | 14 sec |
yes | test-app unit: SampleTests (1回目) | 10 sec |
yes | test-app unit: SampleTests (2回目) | 0.4 sec |
※適当に計測したのであくまで参考ということで
話は変わって、IDE/エディタの話
IntelliJ IDEA。Groovy/GrailsをサポートするIDEとしてはやはり現状で最強です。自分も使ってますが、Spockのパラメタライズドテストのwhere句の縦棒をビシィッとフォーマットしてくれる様は感動的ですらあります。
大変便利ではあるのですが、だがしかしテストの実行が重い。
Grailsのテストというのは、たとえソレがunitテストであろうとそれなりにGrailsの機構に縛られているので、Grailsプラットフォーム上で実行しなければならないわけです*3。ということは、テストの度にイチからGrailsを起動しなければならないわけです。
これはIntelliJ IDEAのせいではなくて、EclipseだろうとSublime Text 2だろうと同じです。Grailsだからしょうがない。
IDE/エディタとインタラクティブモードなコンソールをいったりきたり
インタラクティブモードなコンソールを直接使って、テストを実行すればサクサク実行できます。
なので、これまでのベストプラクティスとしては、インタラクティブモードなコンソールを用意しておいて、IDE/エディタで編集しつつ、ウィンドウを切り替えてインタラクティブモードでテストを実行する、という作業の繰り返しになります。
実際ここしばらくはこの方法でやってきました。慣れればそれなりに素早く実行はできるのですが、ウィンドウの切り替えを何度も何度も何度も繰り返すのは怠惰なプログラマとしては気になります。
あと、編集しているテストだけを実行するためには、テストクラス名を引数に渡す必要があるので、クラス名をコピペするために更にウィンドウをいったりきたりしたりするのもダルい*4。
IDE/エディタから直接インタラクティブモードにコマンド実行をdelegateできたらいいなぁ。
そこでimproxプラグインですよ!
improx = Interactive Mode Proxy、です。
名前の通り、インタラクティブモードのプロキシとして働きます。
improxを使えば、外部コマンド実行に対応したIDEやエディタから、編集中のテストをインタラクティブモード上で直接実行することができます。
improxプラグインはすごくシンプルなプロキシなので、テストの実行上、特殊な制約などは一切発生しません。単にインタラクティブモードにコマンド実行を委譲して、結果を返すだけの土管です。
インタラクティブモードでできることは全部できるし、できないことはできません*5。
インストールと準備
本家のプラグインリポジトリに登録済みなので、簡単にインストールして利用できます。
- BuildConfig.groovyにのpluginsに以下を追加します。
build ":improx:0.1"
- grails refresh-dependenciesを実行するなどして、実際にプラグインをインストールします。
- grails improx-install-resourcesを実行して、クライアントスクリプトをプロジェクト直下に展開します。
improx-resources/ └── scripts ├── improxClient.groovy ├── improxClient.sh ├── improxSmartInvoker.groovy └── improxSmartInvoker.sh
- シェルスクリプトが使える環境であれば、実行権限を与えておきます。
$ chmod +x improx-resources/scripts/*.sh
クライアントスクリプトは、開発マシン上に1回インストールすれば、複数のGrailsアプリで共通でつかえるので、improx-resourcesディレクトリごとどこか対象プロジェクトとは関係ないディレクトリに移動しておくとよいです。ちなみに自分は ~/bin 配下にスクリプト自体を直接放り込んで使っています。
とりあえず使ってみよう
improxサーバを起動
インタラクティブモード上でimprox-startコマンドを実行すると、8096ポートのlistenを開始します*6。
$ grails grails> improx-start Interactive mode proxy server has started on 8096 port.
止めたいときはimprox-stopコマンドでポートを閉じるか、exitコマンドまたはCtrl+Cで素直にインタラクティブモードを抜けてください。
クライアントから任意のコマンドを実行してみる
HTTP/Webブラウザ?!
需要がどれだけあるかは別として、HTTPプロトコルに対応してるので、ブラウザから
にアクセスするだけで簡単に試せます。
URLエンコードされていればいいので、コマンド引数を指定したい場合は
などとします。ブラウザによってはアドレスバーに空白込みで指定すると勝手にURLエンコードしてくれますね。
シェルスクリプト: improxClient.sh
シェルスクリプトが使えるOSなら、improxClient.shがお勧めです*7。
$ improxClient.sh help $ improxClient.sh test-app unit:
IDE/エディタへの外部コマンド登録
IDE/エディタ連携に便利なimproxSmartInvoker
前述の通りコマンド実行は簡単にできますが、IDE/エディタからファイルを指定して実行させたい場合、そのファイルがunitテストなのかintegrationテストなのか、実行するべきコマンドが異なります。また、現状ではfunctionalテストはインタラクティブモードではまともに動かないので、その場合は従来通り新規にgrailsプロセスを起動するしかありません。
などといったことを、自動的に判別してくれる便利なスクリプトも提供しています。IDE/エディタに外部ツールとして登録するのは、このスクリプトにしておけばOKです。
$ improxSmartInvoker.sh /path/to/yourApp/test/unit/sample/SampleUnitTests.groovy --[1] $ improxSmartInvoker.sh /path/to/yourApp/test/integration/sample/SampleIntegTests.groovy --[2] $ improxSmartInvoker.sh /path/to/yourApp/test/functional/sample/SampleFuncTests.groovy --[3] $ improxSmartInvoker.sh /path/to/scriptDir/myTribialScript.groovy --[4]
- [1]は、インタラクティブモード上で'test-app unit: sample.SampleUnitTests'を実行します。
- [2]は、インタラクティブモード上で'test-app integration: sample.SampleIntegTests'を実行します。
- [3]は、'grails test-app functional: sample.SampleFuncTests'として独立したGrailsプロセスを起動します。
- [4]は、'groovy /path/to/scriptDir/myTribialScript.groovy'として単に普通のGroovyスクリプトとして実行します。GroovyServのgroovyclientがインストール済みの場合はそっちが使われます。
groovyスクリプト版も使い方は同じです。
コードネームGrailsServ?
GroovyServを知ってる人なら何となく分かってもらえると思いますが、
- サーバ・クライアント構成で、軽量クライアントからサーバに処理を委譲する構造
- 素早いフィードバックサイクルを促進してサクサク開発をサポートする
といったあたりがGroovyServと似てるので、最初はGrailsServにしようかなと思ったのですが、GrailsはそもそもがサーバなのでGrailsServだとなんというか頭痛が痛い的というか名前があまりよろしくない。ということで素直に構造を表すImprox (Interactive Mode PROXy)にしたのでした。
リンク
- Improx Plugin User Guide (英語): http://kobo.github.com/grails-improx/
- Grailsプラグインサイト: http://grails.org/plugin/improx/
- Github: http://github.com/kobo/grails-improx/
*1:我が最新のMBPR15でgrails helpを実行すると5秒だった
*2:同じくMBPR15でとあるunitテストを実行すると、1回目:10sec、2回目:0.4sec
*3:integrationテストのようにガッツり内部的にサーバを起動するのか、unitテストのように関連するクラスをロードしてモック機構を有効にする程度なのか、という違いはありますが結局Grailsプロセスは必要になります
*4:一度実行したら後はコマンド履歴で実行できるとは言え、ダルい
*5:Gebでのfunctionalテストとかは元々インタラクティブモードでうまく動かないので、improx経由でもエラーになります。あと、コマンド実行途中でユーザ入力を求めるようなコマンドは現状非対応です
*6:-Dimprox.port=9999のようにシステムプロパティで変更可能。ただしポートを変更するとクライアントスクリプトを実行するときも併せて独自ポートを指定する必要があります。詳しくはhttp://kobo.github.com/grails-improx/を参照のこと
*8:元々Sublime Text 2のビルド機能を知ったときにこのプラグインのアイデアが降りてきたのでした。これがやりたかった。