「enumの抽象メソッドはgroovyでサポートされてない?」「Yes. でもそれだけじゃねーんだぜ」 #jggug #q
enumの抽象メソッドはgroovyでサポートされてない? #jggug #q - 山pの楽しいお勉強生活 へのアンサーエントリ的な。
Long Answer (or Investigation)
以前、自分もこれにやられてenum&メソッドならスッキリかけるところを微妙に技術的負債ライクな迂回コードを書く羽目になりました。
enum EnumSample { A { String hello() { "from A" } }, B { String hello() { "from B" } } abstract String hello() } assert EnumSample.A.hello() == "from A"
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed: /private/tmp/hoge.groovy: 1: Can't have an abstract method in a non-abstract class. The class 'EnumSample' must be declared abstract or the method 'java.lang.String hello()' must be implemented. @ line 1, column 1. enum EnumSample { ^ /private/tmp/hoge.groovy: 8: Can't have an abstract method in a non-abstract class. The class 'EnumSample' must be declared abstract or the method 'java.lang.String hello()' must not be abstract. @ line 8, column 3. abstract String hello() ^ 2 errors
そう、abstractメソッドはGroovyのenumではまだ使えないのです。
これ、2011/01ぐらいにJIRAに報告されてるんですけど、なぜかUnresolvedのまま。
自分もVoteしてあってVoted合計12なのですが、まったく動きがありません。
ソース読んでませんが、技術的に難しいのでしょうかね。
じゃあ、abstractを取ってデフォルト実装をいれておけばいいかというと...
enum EnumSample { A { String hello() { "from A" } }, B { String hello() { "from B" } } String hello() { "DEFAULT" } } assert EnumSample.A.hello() == "from A"
これはOK.
なのですが...
enum EnumSample { A() { String hello() { "from A" } }, B() { String hello() { "from B" } } String hello() { "DEFAULT" } } assert EnumSample.A.hello() == "from A"
と、(コンストラクタはとりあえず未定義ですが)コンストラクタ呼出用の括弧を付けると...
Assertion failed: assert EnumSample.A.hello() == "from A" | | | A DEFAULT false
なんと、メソッドがオーバライドされません。
なので、コンストラクタで各要素ごとの値を設定しようとしても...
enum EnumSample { A(1) { String hello() { "from A" } }, B(2) { String hello() { "from B" } } String hello() { "DEFAULT" } final int number EnumSample(int num) { this.number = num } } assert EnumSample.A.number == 1 assert EnumSample.B.number == 2 assert EnumSample.A.hello() == "from A"
Assertion failed: assert EnumSample.A.hello() == "from A" | | | A DEFAULT false
NGなのです。
JIRAチケットはたぶんコレ。http://jira.codehaus.org/browse/GROOVY-5353
このenumが使いづらいのはホントにどうにかして欲しいですね。
というわけで是非Voteしましょう*1。
参考
*1:パッチ書いてくれるのが一番ありがたい
Grailsインタラクティブモードのstop-appとexitコマンドの違い
〜2.1.2まで
Grails開発者はみんな大好きなインタラクティブモードですが、開発中のWebアプリケーションの起動/停止もインタラクティブモードでやると、2回目以降が早くなって良い感じです。ときどきハマっておかしくなったら、さっくり再起動し直せばOK。Ctrl+Cして"grails"+ ENTERキー(ターンッ が一番早い。
grails> run-app
でWebアプリ起動、
grails> exit
で停止。
実はこのexitコマンド、run-app中かどうかで挙動が変わります。
- run-appしてる -> Webアプリが停止、インタラクティブモードは継続
- run-appしてない -> インタラクティブモードを抜ける
慣れれば別に大丈夫なんですけど、まあ勢いつきすぎて、exit打ち過ぎてWebアプリを停止するだけのつもりがインタラクティブモードを抜けてしまうこともままありました。
で、Grails2.2.0で地味ながらこれが改善されてました。
2.2.0
Webアプリ停止専用のstop-appコマンドが追加されました。
代わりにexitはWebアプリが起動してたら停止しつつインタラクティブモードを抜ける専用コマンドになりました。
クセでうっかりexitコマンドを打つとインタラクティブモードまで抜けるから、慣れるまでしばらくめんどくさい...*1。
2.1.3系
ちなみに2.1.3では単純にexitの別名でしかない模様。
--- a/grails-bootstrap/src/main/groovy/org/codehaus/groovy/grails/cli/interactive/InteractiveMode.groovy +++ b/grails-bootstrap/src/main/groovy/org/codehaus/groovy/grails/cli/interactive/InteractiveMode.groovy @@ -118,7 +118,7 @@ class InteractiveMode { else if ("quit".equals(trimmed)) { goodbye() } - else if ("exit".equals(trimmed)) { + else if (("exit".equals(trimmed)) || ("stop-app".equals(trimmed))) { exit() } else if (scriptName.startsWith("open ")) {
Wow! コマンドの頭数だけ揃えましたという感じですね。
おまけ
InteractiveModeのログみてたら、@kiy0takaのコミットを見つけた。
2012-07-07 00:53 kiy0taka o GRAILS-9250 - fixed open command error.
kiy0takaカコイイ
*1:コマンド打っててなんか違和感を感じて調べたらこうなってたという
第1回JetBrainsユーザグループに行ってきた #jbugj
はじめに
ヒゲのイケメンが「ブログ書くまでが〜です」とか言ってたので、今をときめくミニブログ()のTwitterに報告を書いたら却下されたので、ミニじゃない方のブログで書きます。
— 山本裕介さん (@yusuke) 12月 12, 2012
IDEAはアイディアと読む
IntelliJ IDEA、と書いてるそばからつい「イデア」とココロの中で読んでしまうので、矯正する意味で、この記事の以降ではカタカナで書いておきます。
リッチなGroovyエディタ
日頃、業務的にもGrails/Groovyを書いてるのですが、Grailsサポートは有償のインテリジェイアイディア アルティメット版のみなので、会社で購入してもらって使っています。
以前、GroovyServでOSSライセンスもらってみたんですが、そのときはもらえたことに満足してろくに使っていませんでした。でそのまま期限切れで更新してません。
最近は割と使ってます。インテリジェイアイディアでプロジェクトを開いて起きつつ、VimとかSublimeとかを併用したりしてます。せっかくの有償機能であるGrailsサポートの機能はあまり使いこなしてないです。Grailsというコンテキストにおける補完機能とかは確かに便利なこともあるけどあまり活用できてない。むしろ単なるリッチなGroovyエディタとして使ってます。というか現状でGroovyエディタとしては最強だと思います。
個人的には
- 余計なことをしすぎず適度にうまいことやってくれるコードフォーマッタ
- import文の再構成
- クラスやメソッド間のジャンプ
あたりを重宝している感じ。
特にコードフォーマッタのできは素晴らしいです。Groovyのコードフォーマッタとして現状ではダントツです。Spockのパラメタライズドテストのwhereの縦棒もビシィッと揃えてくれるところなんかすごく痺れますね。
Vim、Sublime「「さすがアイディア!おれたちにできない事を平然とやってのけるッそこに(ry」」
ちなみに、リッチなGroovyエディタとして使う分には無償のコミュニティ版で十分です。
Ctrl+W : Select Word at Caret
あと、Groovy関係ないですがCtrl+Wによる拡張型範囲選択(?)がすごく便利です。
ショートカットは人によって設定が違うと通じないので、きっちり書いておくと Keymapの"Main menu > Edit > Select Word at Caret"です。
例えば
String hoge = "ほげ";
の「ほ」と「げ」の間にカーソルを置いて、Ctrl+Wをすると、まず「ほげ」だけが選択されます。
その後はCtrl+Wをおすたびに
「ほげ」 「"ほげ"」 「hoge = "ほげ"」 「String hoge = "ほげ"」 「String hoge = "ほげ";」 (行全体) (その行が含まれるスコープの内側(境界含まず)) (その行が含まれるスコープの内側(境界含む)) (以下続く)
ということでうまい感じに空気を読んで、選択範囲を広げていってくれます。
ここまでうまいこと自動的にやってくれるエディタは中々ないんじゃないでしょうか*1。
その他
- Eclipseの機能との比較で語るととっつきやすくてよい
- いまいさんのアドベントカレンダ2012がすごい
- ビールがぬるい
- 「GroovyといえばJava7の勉強会での発表が面白くて気になってました」「それって懇親会のLT?」「はい」「それ、オレオレ」「えっ」「えっ」
- 会場の本棚にGrails本とプログラミングGroovyがあった!
- 作者: 関谷和愛,上原潤二,須江信洋,中野靖治
- 出版社/メーカー: 技術評論社
- 発売日: 2011/07/06
- メディア: 単行本(ソフトカバー)
- 購入: 6人 クリック: 378回
- この商品を含むブログ (145件) を見る
おわりに
正直自分もそんなにアイディアを使いこなせてないので、今後も精進していきたいと思います。のでライセンスください。*2
今更ながらGExcelAPIでJava7サポートしました(スナップショット版)
2010年から永らく放置熟成させておいたGExcelAPIですが、ビルドに使っているGroovyのバージョンが古すぎてJava7で動かないことが判明したので対応しました。
機能面では変わっていませんが、ビルド&テスト時の依存ライブラリ等をアップグレードしました。
- Groovy 1.7.5 -> 2.0.5
- POI 3.7 -> 3.8
- Java 6 -> 7
これによりJava6でもJava7でも動作するようになりました。
サンプルコードは、本体部分はひっそりとGExcelAPI v0.2をリリースしました - 豆無日記で紹介したままでOKですが、先頭のGrape指定の所だけ以下のように修正してください。
@GrabConfig(systemClassLoader=true) // for workaround a permgen problem with GroovyServ @GrabResolver(name="kobo-maven-repo", root="https://github.com/kobo/maven-repo/raw/master/snapshot") @Grab("org.jggug.kobo:gexcelapi:0.3-SNAPSHOT") import org.jggug.kobo.gexcelapi.* //......
GrabResolverでsnapshotリポジトリを指定してるところと、gexcelapiのバージョンが0.3-SNAPSHOTなところがポイントです。
v0.3としての正式リリースはもうちょっと様子を見てからやりますので、しばし0.3-SNAPSHOTを使ってお待ちください。
というわけで、ご報告ありがとうございました&ご確認お願いします。> @yamap_55
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のビルド機能を知ったときにこのプラグインのアイデアが降りてきたのでした。これがやりたかった。
GradleでEclipseの設定ファイルを生成するときにlib直下のjarを相対パスにする
GradleでEclipseの設定ファイルを生成するときにクラスパス変数GRADLE_REPOを使う - 豆無日記では、Gradleのローカルリポジトリ(キャッシュ)への絶対パスをGRADLE_REPOのような変数に置換して、Eclipseの設定ファイルを複数人で共有するための方法を考えました。
で、id:Hirohiroさんが書かれてたように、現在の最新Gradle1.0ではそれはすごく簡単になってて
eclipse { pathVariables 'GRADLE_USER_HOME': gradle.gradleUserHomeDir }
でOKです。(どうやらGRADLE_REPOじゃなくてGRADLE_USER_HOMEの方が多数派みたい)
今回のはちょっと違ってて、プロジェクトディレクトリの直下にあるlibに対するクラスパスの話。
gradle eclipseを実行するとlibは以下のjarも全部.classpathに列挙されるんだけど、やっぱりそれも絶対パスになってしまい、せっかく相対パスで書きやすいはずのlib直下なのに残念な感じです。
で、それを回避する方法はどうにも見つからなくて
eclipse { pathVariables '.': projectDir }
とやってみると、パスそのものは相対パスっぽくなるんですが、.classpath上の内部のエントリ種別としてクラスパス変数を利用するタイプのものとして認識されてしまい、Eclipse君がjarを見つけることができませんでした。
結局、以前にやったようにフックを使って書き換えることで回避しました。
eclipse.classpath.file { whenMerged { classpath -> classpath.entries.findAll { entry -> entry.kind == 'lib' }.each { it.path = it.path.replaceFirst("${projectDir.absolutePath}/", '') it.exported = false } } }
以前使ったフックであるeclipseClasspath.whenConfigured はdeprecatedだったのでまだ生きてる別のフックを使ってます。
Grailsのアプリ起動完了やテストの結果をGrowlで通知するEvents.groovyが便利な件
[2013/04/18追記]
Mountain Lion以降で通知センターを使う場合は grails-notifierなるものを作りました - PiyoPiyoDucky を参照のこと。
Grailsスクリプトのイベントフックとは
Grailsではアプリ起動とかテスト実行などをGantスクリプトとして提供しています。で、そのスクリプトではイベントフックの手段が提供されていて、特定のイベントの発生時に介入して独自の追加処理をさせたりすることができます。例えば、
- コンパイルの完了
- テストの起動
- サーバの起動
などのタイミングで独自ファイル処理を入れたり、通知をしたりできるわけです。
フックスクリプトはもちろんGroovyで書きます。$HOME/.grails/scripts/_Events.groovy というパスで保存すればOKです。特定のGrailsプロジェクトでだけフックしたい場合は$PROJECT_HOME/scripts配下にもおけます。書き方などは参考情報のURLを参照してください。
Grailsのドキュメントを読むとファイル名は_Events.groovyが公式のようですが、ソースを読んだら"Events.groovy"でもOKでした。両方のファイルが存在する場合はアンスコ付きが優先です。
つくったもの
Macユーザで通知といえばGrowl。というわけで、Grailsのアプリ起動完了時やテスト失敗時、テスト全件終了時にGrowlで通知するEvents.groovyを書いてみました。
アイデア自体は、Grails徹底入門のコラムにあるので目新しいものじゃないのですが、
- テスト全件終了時に失敗した件数を表示する
- テスト全件終了時にGrowl通知をクリックすると、ブラウザでテストレポートを表示する
- Grailsアプリ起動時にGrowl通知をクリックすると、ブラウザでアプリのURLにアクセスする
などといった感じで実用性を考慮して色々チューニングしてあります。
これからもまだまだ手を入れていく予定ですが、ひとまずそれなりに使えるようになったのでこちらで紹介しておきます。
実際の開発で使っていますが、中々快適です。
必要なもの
使い方
https://gist.github.com/2841103 に公開してある
の2つのファイルを$HOME/.grails/scripts/配下に保存します。
お手元のGrailsプロジェクトで起動やテスト実行をすると...
- Grailsアプリの起動に成功したとき
-
- クリックするとブラウザでアプリのURLにアクセスします。
- すべてのテストがパスしたとき
-
- クリックするとHTMLのテストレポートがブラウザで開きます。
- 1件のテストに失敗したとき
-
- クリックするとHTMLのテストレポートがブラウザで開きます。
- 失敗したテストケースごとに独立して通知が並んだ後に、最後にまとめの通知が流れます。大量のテストが失敗したときは精神的ダメージが大きめです。
といった感じで素敵にGrowlが通知してくれます。
なおこれらのスクリプトの実行によって何らかの不具合が生じても責任は負いかねますのでそこは一つ自己責任でお願いします。
参考情報
- 作者: 山田正樹,山本剛,上原潤二,永井昌子,杉山清美,杉浦孝博,笠原史郎,香月孝太,福岡竜一,伊堂寺北斗
- 出版社/メーカー: 翔泳社
- 発売日: 2008/08/26
- メディア: 大型本
- 購入: 3人 クリック: 42回
- この商品を含むブログ (28件) を見る
- 作者: 関谷和愛,上原潤二,須江信洋,中野靖治
- 出版社/メーカー: 技術評論社
- 発売日: 2011/07/06
- メディア: 単行本(ソフトカバー)
- 購入: 6人 クリック: 392回
- この商品を含むブログ (155件) を見る