2011年7月14日木曜日

移転しました

都合により、以下のサイトにブログを移転しました。

http://monochromeroad.blogspot.com/

2011年4月6日水曜日

GrailsのMailプラグインでエンコード指定(ISO-2022-JP)なメールを送る

Grailsのバージョンは1.3.7
Mailプラグインのバージョンは1.0-SNAPSHOT

結構やってるんだけど、やり方忘れるので備忘録ということで

import org.springframework.mail.javamail.MimeMailMessage
import org.springframework.mail.javamail.MimeMessageHelper

def mailService

/**
 * メールを送信する
 * 
 * @param mailParams メールサービスへ渡すパラメータ
 */
def sendMail(Map mailParams) {
    mailService.sendMail {
        helper = new MimeMessageHelper(
            mailSender.createMimeMessage(), multipart, "ISO-2022-JP")
        message = new MimeMailMessage(helper)
        
        to mailParams.to
        from mailParams.from
        subject mailParams.subject
        body mailParams.body
    }
}

上のコードをサービスクラスとかに入れて呼び出します。
Mailプラグインの設定方法はここに書いてあるままです。

Grails Mail Plugin

最近はUTF-8でもいいんでしたっけ?
でもまあ日本語メールならISO-2022-JPにしといて損はないんじゃないでしょうか。

2011年3月15日火曜日

Gradleでプロパティなどの設定情報を外出しして切り替えて使う

@bluepapa32さんの記事に触発されて書いてみました。

次のようなビルドスクリプトがあるとき、
task hello << {
    println "Hello, $yourname"
}
Gradleでプロパティ「$yourname」の設定を外だしにして、ビルドごとに結果を柔軟に変えたい、といったことを実現する方法ですね。

@bluepapa32さんも書かれているように、Gradleではさまざまな方法でこれを実現できます。
ここでは、主にGradleの機能を使ってこれを実現する方法について書いてみます。

ビルドスクリプトの設定をいろいろ切り替えるには


task hello << {
    println "Hello, $yourname"
}
上記のビルドスクリプトをそのまま「gradle hello」と実行しても、yournameなんてプロパティはないですと怒られて失敗するので、プロパティyournameをビルドスクリプトに設定してやる必要があります。

このとき、プロパティyournameのような設定情報をビルドスクリプトの外で設定し、切り替えられるようにするには、

  • コマンドライン引数で直接設定
  • 環境変数→プロパティ自動変換機能を使う
  • gradle.propertiesをユーザーのホームディレクトリまたはプロジェクトディレクトリに置く
  • 外部ファイルをビルドスクリプトなどの先頭で読み込む
  • 初期化スクリプト(init.gradle)を使う
の方法が考えられます。

詳しくはユーザーガイド(Gradleプロパティとシステムプロパティ)など見てもらうとして、ここでは一つずつ使いどころなど簡単に考えてみたいと思います。

コマンドライン引数で直接設定


コマンドラインオプションを使って、yournameのようなプロパティを設定できます。
gradle -Pyourname=literalice hello

:hello
Hello, literalice

BUILD SUCCESSFUL
設定したいプロパティが少ないときや簡単なデモなどで使えますね。

環境変数→プロパティ自動変換機能を使う


Gradleは、プレフィックス「ORG_GRADLE_PROJECT_」の付いた環境変数を定義しておくと、その値をプロパティに変換してくれる機能があります。
set ORG_GRADLE_PROJECT_yourname=literalice

gradle hello

:hello
Hello, literalice

BUILD SUCCESSFUL
設定するプロパティの数がそう多くない場合で、ビルドするマシンごとにプロパティを切り替えたいとき、jenkinsなどのCIサーバー上で簡単にビルドを設定したいときなどに。

Gradleだと、環境変数を直接ビルドスクリプトで読む(System.getenv) ようなシチュエーションは限られますね。(外部ツールとの連携時とか?)

gradle.propertiesをユーザーのホームディレクトリまたはプロジェクトディレクトリに置く


gradle.propertiesという名前でプロパティファイルを作成し、プロジェクトディレクトリまたはユーザーのホームディレクトリに置いておくと、読み込んで使ってくれます。

なお、プロジェクトディレクトリ→ユーザーのホームディレクトリの順に読み込まれます。

ユーザーのホームディレクトリのプロパティが優先して使われる(上書きする)ということです。

~/.gradle/gradle.properties
yourname=literalice
実行結果
gradle hello

:hello
Hello, literalice

BUILD SUCCESSFUL

ホームディレクトリのgradle.propertiesは、すべてのビルドスクリプトで使われます。

ツールの場所(grailsHomeとか、javaHomeとか)、ユーザーの(sshやwebページなどへの)ログイン情報などを設定するのに良いかもしれません。

ちなみに、ここまでの設定方法だと、どれ選んだ場合でもビルドスクリプトの方は変更する必要ないんですよね。素晴らしい。

外部ファイルをビルドスクリプトなどの先頭で読み込む


いくつか方法はありますが、

  • Groovyの機能で外部ファイルを読み込んで展開する
  • 外部のスクリプトファイルを読み込む

の二つを紹介しようと思います。

1. Groovyの機能で外部ファイルを読み込んで展開する

この方法は、@bluepapa32さんが解説されています。

GroovyのConfigSlurperを使って、Groovyスクリプト形式の設定ファイルを読み込み、プロパティに展開しています。

既存の設定ファイル(GrailsのDataSource.groovyやBuildConfig.groovyなど)を読み込むときに便利です。

2. 外部のスクリプトファイルを読み込む

たとえば、プロパティのセットをLinux向け、Windows向けなどいくつか定義しておいて、それらを簡単に切り替えできるようにしたい、というときはこの方法が使えます。

build.gradle
apply from:"default.config.gradle"
apply from:"${env}.config.gradle"

task hello << {
    println "Hello, $yourname"
}

設定ファイルをいくつか用意します。
default.config.gradle
yourname = "the name in the default config"
linux.config.gradle
yourname = "the name in the linux config"
windows.config.gradle
yourname = "the name in the windows config"

実行するときは、こんな感じ

gradle -Penv=windows hello

:hello
Hello, the name in the windows config

BUILD SUCCESSFUL

イメージとしては、apply from:"..."で読み込んだファイルが、そのままペーストされる感じで動きます。

読み込んでいる外部スクリプトは、ただのGradleビルドスクリプトなので、上記のようなプロパティの設定だけでなく、タスクの定義やプロジェクト設定の変更など、Gradleでできることはなんでもできます

初期化スクリプト(init.gradle)を使う


Gradleには初期化スクリプトという仕組みがあって、マシン固有の設定をここでも行うことができます。

具体的には、「init.gradle」というファイルをユーザーのホームディレクトリに置いたり、コマンドラインオプションで渡してやったりすると、ビルドが構築される前に「init.gradle」が実行されるというものです。

初期化スクリプト

このスクリプトで、今までのように単純なプロジェクトプロパティをセットするにはかなりめんどくさい方法をとる必要があり現実的ではないです。

ビルドのライフサイクルに対するリスナーや、カスタムのロガーを登録する、外部ツールとの連携に便利らしいのですが、私の使用した範囲だとまだ使いこなせたことはないですね…

まとめ


Gradleのビルドを設定する方法をいくつか見てきましたが、こんな風にいろいろな方法を選ぶことができるのはGradleの魅力の一つだと思います。

場面場面に応じて、適切な方法を選んでいきたいですね。

2010年10月31日日曜日

ビルドシステムをGradleに移行、ユーザーガイドを一部翻訳

おうちで使ってるビルドシステムをGradleに移行

そのとき、Gradleの勉強がてらユーザーガイドを翻訳してました。せっかくだから公開
http://www.monochromeroad.com/artifacts/gradle/userguide/userguide.html
リポジトリ
http://github.com/literalice/gradle-ja/wiki

Mavenもそんなに深く使い込んでたわけじゃないけど、Gradleはいろいろ分かりやすい感じ

というか、MavenとAntはそんなにかぶってた印象無いけど、GradleとGantは丸かぶりでは?
いままでAntで書いてたところも大体Gradleのほうがやりやすいと思います

2010年7月30日金曜日

Grailsを1.3.3に上げたら、ユニットテストがNullPointerExceptionで壊滅

Grailsのバージョンはもちろん1.3.3

タイトル通り、Grailsを1.3.3に上げたら、ユニットテストがNullPointerExceptionで壊滅しました
原因はGRAILS-6482で1.3.4で直るらしいですが、とりあえず回避方法としては次のコードをユニットテストにいれるといいらしいです。
protected void setUp() {
   super.setUp()
   PluginManagerHolder.pluginManager = [hasGrailsPlugin: { String name -> true }] as GrailsPluginManager
}

protected void tearDown() {
   super.tearDown()
   PluginManagerHolder.pluginManager = null
}

ユニットテストが多くなってくるとめんどいので、_Events.groovyに次のコードを入れました。
import org.codehaus.groovy.grails.plugins.GrailsPluginManager
import org.codehaus.groovy.grails.plugins.PluginManagerHolder

eventTestPhaseStart = { phase ->
    if (phase == 'unit') {
        PluginManagerHolder.pluginManager = [hasGrailsPlugin: { String name -> true }] as GrailsPluginManager
    }
}

eventTestPhaseEnd = { phase ->
    if (phase == 'unit') {
        PluginManagerHolder.pluginManager = null
    }
}

これでいいかどうかは不明ですがとりあえずNullポは消えたので。

2010年7月9日金曜日

hudson+antでgrailsアプリをビルドする その1

Grailsアプリケーションをhudson上でビルドするにはいくつか方法があります。
hudsonにGrailsプラグインがあるので、それを使用してビルドすることもできます。シェルでgrailsコマンドを呼び出すこともできます。
しかし、後々ビルド手順が複雑になったりしたときなどを考えると、やはりantのbuild.xmlにビルド手順をまとめて、hudson上からはbuild.xmlのデフォルトターゲットを呼び出すだけ、くらいにしておいたほうがいいかと。
いや、gantでもpomでもいいんですけどね。

まず、build.xmlを作成するところまで書いてみます。ポイントは、

では↓

1:build.xmlを生成
というか、grailsが作ってくれます。
cd [grails-project]
grails integrate-with --ant
これで、Grailsプロジェクトの中にbuild.xml、ivy.xml、ivysettings.xmlができているはず。
(ちなみにこれらのファイルはgrails upgradeでなぜか更新されないので、grailsを更新したらこれらのファイルを削除してもういちどintegrate-withコマンドを実行)

antコマンドを試してみましょう。基本的にgrailsコマンドに対応したantターゲットができています。
必要な環境変数を設定して、実行
普通にやるとメモリ不足で落ちることが多いので、ヒープサイズも指定しておきます。
set GRAILS_HOME=C:\grails\grails-1.3.2
set ANT_OPTS=-Xmx1024m -XX:MaxPermSize=512m

ant compile

download-ivy:
...(略)...

[ivy:retrieve]          ::::::::::::::::::::::::::::::::::::::::::::::
[ivy:retrieve]          ::          UNRESOLVED DEPENDENCIES         ::
[ivy:retrieve]          ::::::::::::::::::::::::::::::::::::::::::::::
[ivy:retrieve]          :: org.jboss.netty#netty;3.1.5.GA: not found
[ivy:retrieve]          ::::::::::::::::::::::::::::::::::::::::::::::
[ivy:retrieve]
[ivy:retrieve]
[ivy:retrieve] :: USE VERBOSE OR DEBUG MESSAGE LEVEL FOR MORE DETAILS

BUILD FAILED
D:\project\literalice\sandbox\grails-ant-build\build.xml:60: impossible to resolve dependencies:
        resolve failed - see output for details
…失敗。あれ?
これですね。
http://jira.codehaus.org/browse/GRAILS-6410
1.3.4で直るらしい。でもまあたぶん、リポジトリ追加してやればいいんじゃないかな…

2:依存先リポジトリの追加
先ほど生成したivysettings.xmlに、antビルドが使用しているリポジトリがリストされているので、ここにさっき足りない言われたライブラリのリポジトリを追加します。

  
  
  
  
  
  
    
      
      
      
      
      
      
    
  

で、もういちど
ant compile
...(略)...
compile:
[grailsTask] Resolving dependencies...
[grailsTask] Dependencies resolved in 738ms.

BUILD FAILED
D:\project\literalice\sandbox\grails-ant-build\build.xml:104: The following error occurred while executing this line:
D:\project\literalice\sandbox\grails-ant-build\build.xml:48: Unable to start Grails: java.lang.reflect.InvocationTargetException
うは失敗しかもイミフ

結論から言うと、antのバージョンが1.8系だとダメみたい。
antのバージョンを1.7.1にしてもう一回!
ant compile
...(略)...
[grailsTask]   [groovyc] Compiling 7 source files to D:\project\literalice\sandbox\grails-ant-build\target\classes
[grailsTask]

BUILD SUCCESSFUL
Total time: 5 seconds
OK。

3:依存先リポジトリの追加
さて、私としては、ビルド手順は極力build.xmlに集めたいです。
なので、test、warなどのコマンドをターゲットを順に呼び出すターゲットを作成します。
私はbuild.xmlに直接追加しましたが、別のビルドファイルを作成して、そのファイルからbuild.xmlを呼び出すようにしてもいいかもしれません。

これでもいいですが、私の場合war作成時のビルド環境(dev,prod,test,etc...)を明示したかったので、以下のように修正しました。
あと、non-interactiveをコマンド引数に指定しないと、hudsonに持って行ったときにループっぽくなってビルドが進まなくなります。

まず、antのgrailsマクロにビルド環境の引数を追加します。

    
    
    
    
        
            
            
            
        
    


つぎにデフォルトターゲットに指定するビルド用ターゲット

    
    
    
    


これでbuild.xmlができました。あとはhudson上に持って行ってビルドするだけです。
ただし、このままだとgrailsがビルドやテストに使用するディレクトリ(普通は%HOME%/.grails以下)をhudsonジョブ間で共有します。
並行ビルドなど考えると、各ジョブ間の作業ディレクトリはなるべく分割しておきたいところです。

このへんは次回。

2010年7月6日火曜日

GrailsアプリケーションのAntビルドでSSLHandshakeException

grails1.2系で、初めてプロジェクトをビルドするときにantでビルドすると、こんなエラーが起こることがあります。
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException

これが発生するのは、プロジェクトはあるけど、まだ作業ディレクトリ(.grails)がない状態。つまり、
  • Grailsプロジェクトを新マシンにチェックアウトしてantでビルド
  • 作業ディレクトリを~/.grailsから別の場所に変更してantでビルド
などのときに発生するようです。

Tomcatのプラグインを、httpsのプラグインサイト上から落そうとして証明書エラー…javaが知らない認証局を使ってるらしい。
プラグインサイトが使用している中間認証局の証明書をkeytoolでインポートすれば、とりあえずビルドはできます。

証明書: https://www.startssl.com/certs/sub.class2.server.ca.crt

keytool -import -keystore %JAVA_HOME%/jre/lib/security/cacerts -file sub.class2.server.ca.crt -alias sub.class2.start.com
パスワードは変更していなければ「changeit」
証明書インポートについては、HttpClientでSSL通信が詳しいです。

なお、Grails1.3以降だとtomcatプラグインはネットワークからではなくGRAILS_HOMEから持ってくるようで、このエラーは発生しませんでした。

2010年5月13日木曜日

GrailsのIntegrationTestで、ファイルのアップロードをテストする

grailsのバージョンは1.2.1
spockのバージョンは0.4-SNAPSHOT

import org.springframework.mock.web.MockMultipartHttpServletRequest
import org.springframework.mock.web.MockMultipartFile
import grails.plugin.spock.IntegrationSpec

class FileUploadControllerSpec extends IntegrationSpec {
  
    def controller

    def setup() {
        controller = new FileUploadController()
        controller.request.metaClass.mixin MockMultipartHttpServletRequest
    }

    def "アップロードされたファイルを保存する"() {
        when:
        MockMultipartFile file = new MockMultipartFile("html.attr.name", "originalFileName", "text/plain", "ファイルの内容".bytes)
        controller.request.addFile(file)
        and:
        controller.save()
        then:
        controller.responce.redirectedUrl == "/fileUpload/show/1"
    }
}

とりあえず。

2010年2月16日火曜日

IntegrationTestのときにタイムスタンプの自動設定を切りたい

Grailsのバージョンは1.2.1

lastUpdatedでソートしてるときとか、タイムスタンプの自動設定を切りたいんです。

[DomainClass]が何かのドメインだとして、
DomainClass.mapping.autoTimestamp(false)

まあGroovy的には当然かもですが。

2010年1月10日日曜日

Grailsの統合テスト内で、タグライブラリを呼び出す

Grailsのバージョンは1.2.0

備忘録的なメモ

タグライブラリやコントローラーのIntegration-Test書いてると、テストの中で標準(g)のタグライブラリ呼び出したくなることありませんか?あんまないか。


import org.codehaus.groovy.grails.commons.ApplicationHolder
import org.codehaus.groovy.grails.web.taglib.NamespacedTagDispatcher

class CustomTagLibSpecification extends GroovyPagesSpecification {
  NamespacedTagDispatcher g = ApplicationHolder.application.mainContext.getBean('gspTagLibraryLookup').lookupNamespaceDispatcher('g')

 def "仕様かきかき"() {
  message = g.message(code:"message.properties.message.code")
  ...
 }
}


上のはSpockの仕様ですけど、普通のGroovyPagesTestCase使ったテストケースでもいけるんじゃないかと。まあ、applyTemplate使ってもいいすけど。
コントローラ内だったら、なんにもしなくても message(code:"ほげほげ") でタグライブラリ呼び出せるんだけど…

もっといい方法ありましたら教えてくださいです。

2009年12月23日水曜日

Grailsのtest-appで、コンソールにstacktraceを出す

Grailsのバージョンは1.2.0.RC2

Grailsには、組み込みのテスト実行環境がついています。
Grailsをインストールして、Grailsのプロジェクトを作成して、
コンソールで"test-app"と打ち込むだけでテストが実行されます。

んですが、このテスト、実行してエラーがでても、コンソールにスタックトレースがでないんですよね。

testSample...FAILED

つー、そっけない一行がでるだけ。なんでやねん。

というわけで、Grailsのテストイベントを取ってスタックトレースを出してみます。

1.スタックトレースを出力するためのイベントリスナを作成する。
以下のようなスタックトレース出力クラスを作ります
package testsupport
import grails.build.GrailsBuildListener;
public class StackTraceDumper implements GrailsBuildListener {
    void receiveGrailsBuildEvent(String name, Object[] args) {
        if (name == 'TestFailure') {
            this.doTestFailure(*args)
        }
    }
    protected doTestFailure(String name, failure, boolean isError) {
        failure?.printStackTrace()
    }
}

2.上記イベントリスナをコンパイル
なんか、イベントリスナだけは前もってコンパイルしとかないと失敗しました。
[grails-project]/src/groovyの下に入れて"grails compile"を実行してもいいし、
jarにして[grails-project]/libにいれてもいいと思います。

3._Events.groovyスクリプトの記述
上記イベントリスナを設定します。
[grails-project]/scripts/_Events.groovyに以下を記述
import testsupport.StackTraceDumper
eventListener.addGrailsBuildListener(new StackTraceDumper())

4.test-appコマンドを、-echoErrオプション付きで実行する
Grails1.2.0から、echoErrオプションで標準エラーを出力できるようになりました。
"test-app -echoErr"と激しく打ち込み。
テストクラスを限定したり、ユニットテストだけ実行したりできます
"test-app [TEST CLASS] -echoErr"
"test-app unit: -echoErr"

で、スタックトレースがでるはず…


ちなみに、spockプラグインを使うと、powerassertsっぽいかっこいいスタックトレースになります。
こんな↓


あ、[grails-project]/scripts/_Events.groovyの仕様とかよく分かってないんで、突っ込みどころあったら突っ込んでくらはい

2009年12月8日火曜日

[Grails] GSP部分テンプレート内のitがときどき消滅する件

grailsのバージョンは1.2-M4

grailsで部分テンプレート「/share/_book.gsp」を表示するとき、



とかやると思います。
このとき、部分テンプレート「/share/_book.gsp」内の変数「it」にbookが自動で設定されるわけですが、
この「it」にアクセスできないことがあるようです。

具体的には、部分テンプレート「/share/_book.gsp」内で

${it.title.encodeAsHTML()}

というふうに、linkタグを使っているときにlinkタグテキスト部内のit(it.title.encodeAsHTML())にアクセスできないみたいです。この場合、次のようにdelegateからアクセスする必要があります。

${delegate.it.title.encodeAsHTML()}


テキスト部は、クロージャになってタグライブラリの引数に渡されるので、delegateの差し替えなどしていない場合上のようになる…のかな?

2009年11月6日金曜日

Grails + easyb でイミフなエラー(MultipleCompilationErrorsException)が出る件

Grailsのバージョンは1.2-M3
EasyB + Grails Testingプラグイン(grails-easyb)のバージョンは1.1

ちょっと前、GrailsでRSpec的なBDDができたらいいなぁ、的なこと言ってたんですが、どうやら"easyb" というBDDフレームワークがあって、Grailsでも使用できる模様。([Java][Groovy][BDD] Grails におけるドメインクラスのユニットテスト - Grails標準テストと Easyb プラグイン使用)

人によるかもですが、私の場合xUnit系のテストケースよりはBDDのスペックの方が断然書きやすいので、早速飛びついてみました。
easyb公式ページなどを参考に、簡単なシナリオを書いてみる…

で、タイトル通り怒られました。このメッセージがよく分からない…
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed, Script1.groovy: 7: unexpected token: } @ line 7, column 5.
グーグル先生とかに相談してみたんですがさっぱり。
適当に試行錯誤してみました。まずコレ。
scenario "TestScenario", {
    given "TestGiven", {
    }
    when "TestWhen", {
    }
    then "TestThen", {
        "test".shouldBe("test")
    }
}
このコードなら通してくれました。
だから何?って感じの超テストコードなんですが、ここから段々デコレーションしてみます。

といいますか、どうやら日本語が含まれてるとだめっぽい。コメントすら無理
でもシナリオ名に日本語使ったとき、たまに通ることもある…

あーもうだめだ、再現性ないとかお試しで頑張るには手強すぎる。
シナリオの書き方が違ってるのか?コメントはどこどこに入れてはいけないとか…そんなバ
サービスのインジェクションとかどうするのかなー、とか他にもいろいろ調べたかったんですけど、とりあえず様子見ということで。

しばらく標準のテストで進めるか…

2009年11月3日火曜日

[Grails] Integration Test のテストケースは、GrailsUnitTestCaseでなくGroovyTestCase を継承するらしい

Grailsのバージョンは1.2-M3

情報元はここ
http://agileice.blogspot.com/2009/09/grails-integration-testing.html

なんか、GrailsのIntegrationTestで、Config.groovyに設定した定義情報が使えないなあ、って思ってネットサーフィンしてたら、上記のページを発見。

テストケースの継承元を間違うと、テストケースのtearDownでConfig.groovyの設定情報がnullになってしまうらしいです。
  • GrailsUnitTestCaseとか、それ継承してるControllerUnitTestCaseとか
    Unitテストのみに使用
  • GroovyTestCase
    Integration Test で使用
ポイントは、Integration Testのテストケース全部にGroovyTestCase を使うことですかね。
一個でもGrailsUnitTestCaseのが混じってると、その後のテストケースで設定情報が消えます。

最近のGrailsだと、コントローラーやサービス作成時にUnit Testを生成するから、そのファイルをIntegration Testの方に移動したりしているうちに段々ごっちゃになってそう。

2009年10月29日木曜日

Grails+Acegi プログラム上でログイン・ログアウトさせる

Grailsのバージョンは1.2-M3
Acegi Security は 0.5.2

忘れないうちにメモ

import org.springframework.security.providers.UsernamePasswordAuthenticationToken as AuthToken
import org.springframework.security.context.SecurityContextHolder as SCH

class NantokaService {
    def daoAuthenticationProvider

    // ログイン処理
    void systemLogin(String username, String password) {
        def auth = new AuthToken(username, password)
        def authtoken = daoAuthenticationProvider.authenticate(auth)
        SCH.context.authentication = authtoken
    }
    // ログアウト処理
    void systemLogout() {
        SCH.context.authentication = null
    }
}

こんな感じ

2009年10月28日水曜日

7 〜モールモースの騎兵隊〜

をいまさらプレイしてます。

いまさらすぎるタイトルですが、ヴィーナス&ブレイブス買いに行ったら500円で売っていたので。
事前にプレイしとくとちょっぴり嬉しい、とどこかで聞いたような気が…

戦闘システムにランダム要素ほとんどないんで、考えがいあって楽しいです。
一問一問出題されるクイズみたいな戦闘だし、飽きないで遊べてます。
これでランダムエンカウント、とかだったら途中で投げそう。

しかし、この騎士団の連中、プライドだけ高い無能集団みたいな描かれ方してますけど、
構成メンバー全員、職種「騎士」なんだよなぁ。
それ最強集団じゃないスか?
適当にローテーションしてるだけで大抵殲滅できそう。

2009年10月27日火曜日

GrailsのUrlMappingsから直接viewに飛ばすと、localeの読み込みがおかしくなる

Grailsのバージョンは1.2-M3

ここで四時間くらい詰まった…

開発中アプリのトップページ「localhost/sampleapp/」に、"messages_ja.properties"から読み込んだメッセージを表示させていたんです。
で、URLの末尾に「?lang=de」とかつけるとそのメッセージがドイツ語(messages_de.properties)に変わるはずじゃないですか。

でも、変わらなかったんですよね。ブラウザの設定から日本語を消しても、クッキーを消しても、ずっと日本語表示のまま。

こういうときはGoogleいったあと、JIRAでしょうか。
http://jira.codehaus.org/browse/GRAILS-3871
に原因が書いてました。

Grailsのデフォルトだと、UrlMappingsに
"/"(view:"/index")

って書いてて、トップーページをviewに直接遷移させてるんですけど、
この場合、そのviewでlocaleの変更ができなくなるみたい。

いちど
"/"(controller:"foo")
でコントローラーを呼び出して、その中でindex.gspをrenderするように修正することで直りました。


だいぶ有名な事象みたいだけど、これ、直るのかな…
JIRAみると、必殺技「仕様です」みたいにも読めるけど。

GrailsのTagLibテストケースの記述

Grailsのバージョンは1.2-M3

最近のフレームワークは、開発者がテストにハマるようにいろいろ趣向こらしていて助かりますよね。
RSpec on RailsとZenTestはもうすごいよかったなぁ…
Groovyでも似たようなのないですかね、RSpec on Grails みたいなヤツ。あとZenTestみたいなヤツ。

さて、Grailsも最近のフレームワークなので、テストは結構楽しくできます。
ですが、Grailsの1.2(1.1も?)だと、ドメインモデルだのコントローラーだのは、テストケースの中でnewしてもうまくいかないらしいです。(ダイナミックメソッドが注入されずエラーになるみたい 参考:Grails1.1.xのUnitテスト)

TagLibのテストでも、同じようにnewして実行すると、メソッドやプロパティが見つからないと言われて落ちることがあります。
TagLibのユニットテストを記述するTagLibUnitTestCaseには、tagLibプロパティがあって、対象となるTagLibがセット済みなのでそれを使えばいいみたいです。

Grails をマスターするのカスタムタグを例にすると、

×
class DateTagLibTests extends GroovyTestCase {
  def dateTagLib

  void setUp(){
    dateTagLib = new DateTagLib()
  }

  void testThisYear() {
    String expected = Calendar.getInstance().get(Calendar.YEAR)
    assertEquals("the years don't match", expected, dateTagLib.thisYear())
  }
}

class DateTagLibTests extends GroovyTestCase {
  void testThisYear() {
    String expected = Calendar.getInstance().get(Calendar.YEAR)
    assertEquals("the years don't match",expected, tagLib.thisYear())
  }
}

上だと、
groovy.lang.MissingPropertyException:No such property: out for class
とか言われて落ちました。

あ、でも各テストケースの中(setUpじゃなくて)でTagLibクラスをnewすると普通にテスト通りましたね。
よくわからんです。

WebFlowってRESTfulじゃないよね…

すくなくとも、Grails(てかSpring?)の実装だと。
まったく同じ画面なのに、リロードするとどんどんURLが変化していく…
あれ、複数のURLが同じリソースを指すのはいいんだっけ。でもWebFlowのURLって特に記述的な意味とかないし。
POSTのリクエストボディに制御パラメータ入れられないかな。

Grailsが最近取り入れてるRESTfulなURLマッピングと組み合わせようとすると、訳分からんことに。
なにかうまい方法ないでしょうか。よくわからない…
とりあえず、WebFlowについてよく理解できるまでは使わないようにします。

Grailsでのメール認証つきユーザー登録

Grails バージョンは1.2-M3

ユーザー登録画面

「仮登録メールを送信しました、本登録にはこのリンクにアクセスして云々」

本登録が完了しました

という、よくあるやつです。


次のふたつのプラグインで実現
  • 定番らしいユーザー認証プラグイン「Spring(Acegi) Security 0.5.2」(公式)
  • メールアドレス確認用プラグイン「Email Confirmation 1.0.3」(公式)
まあ、「Email Confirmation」のチュートリアルをやってみたら、なんとなく感じがつかめそうですが、実装はこんな感じで。

1.「Email Confirmation」にメールアドレス、入力されたユーザー情報(ログインIDとか)を渡す
[PersonController#save]
emailConfirmationService.sendConfirmation("sendto@sample.com", "メール本文", [from:"from@sample.com", view:"/path/to/mail-body-gsp"], "${loginid}\t${encordedPassword}")
emailConfirmationServiceはプラグインが提供しているServiceです。
こうすると "sendto@sample.com"にメールアドレス確認用のリンクがかかれたメールが送られます。最後に"${loginid}\t${encordedPassword}"でユーザー情報を登録してます。

2.「Email Confirmation」のアドレス確認完了イベントでユーザー登録
さて、メールに送信されたリンクを押すと、emailConfirmationServiceのonConfirmationイベントが発生します。
このイベントにはリスナとしてクロージャを登録するのですが、このクロージャの中でさっき登録した"${loginid}\t${encordedPassword}"がとれるので、分解してユーザーを構築してDBに格納して終了です。

Grailsのユーザー認証、認可用プラグインには今回使用したSpringSecurityの他にも、JSecurity(もう非推奨、今後はApache Shiro(何?)プラグインを使えといわれた)、Authentication Pluginなどいろいろあるんですが、どれ使っても同じ用に実装すればできるのかな? (ほとんど触ってないので分からないですが。)
というか、この辺のプラグインだとEmail Confirmation使わずにできたりしないかな…