Grails プラグインの作り方 - シンプルな easyb 実行プラグインを作成

以下のような環境を使って、easyb を実行するだけの簡単な Grails プラグインを作ってみる。

  • JavaSE 6 (1.6.0_06)
  • groovy 1.5.6
  • grails 1.0.3
  • easyb 0.8

Grails でのプラグイン作成手順は以下の通り。

  1. プラグイン作成プロジェクトの生成
  2. プラグインの情報を設定
  3. プラグインを構成するファイルを追加
  4. プラグインをパッケージ化

プラグイン作成プロジェクトの生成

以下のようなコマンド実行で、プラグインの作成プロジェクトが生成される。

grails create-plugin [プラグイン名]

今回は、easySample という名称のプラグインを作成する事にする。

>grails create-plugin easySample

カレントディレクトリにプラグイン名のディレクトリが生成され、その中にプラグイン作成用のプロジェクトファイルが生成される。

プラグインの情報を設定

create-plugin によって生成されたディレクトリに直下に <プラグイン名>GrailsPlugin.groovy ファイルが生成されているので、以下のような情報を入力する。

  • バージョン情報 version
  • 作者 author
  • タイトル title
  • 説明 description

今回は以下のように編集した。

EasybSampleGrailsPlugin.groovy
class EasybSampleGrailsPlugin {
    def version = 0.1
    def dependsOn = [:]

    // TODO Fill in these fields
    def author = "fits"
    def authorEmail = ""
    def title = "Easyb Sample"
    def description = '''\
Easyb runner plugin.
'''
・・・
}

なお、今のところ description などに日本語を使用すると、プラグインを作成する環境によってはインストール時に問題が発生するプラグインを作成してしまう事があるようなので注意が必要。

具体的には、GrailsPlugin.groovy 自体の文字コードUTF-8 にすれば、パッケージングまでは日本語を適切に処理できるのだが、その際に生成される plugin.xml が実行環境のデフォルト文字コードで生成されるので(java.io.FileWriter を使って plugin.xml を作成している)、Windows 環境で普通に作成すると plugin.xmlUTF-8 にならずにこれがプラグインのインストール時に問題となる模様。

プラグインを構成するファイルを追加

プラグインの実装として、grails easyb のようなコマンド実行で spec ディレクトリにある easyb のスペックファイルを実行するようなスクリプトを作成する。

ちなみに、Grails は scripts ディレクトリ内に配置した Gant スクリプトを grails コマンドのターゲットとして実行できるようになっている。(例 scripts/Sample.groovy を grails sample で実行できる)


今回は、以下のようなファイルを作成して scripts ディレクトリに配置した。なお、スクリプトの作成は grails create-script コマンドが使える。

scripts/Easyb.groovy
Ant.property(environment: "env")
grailsHome = Ant.antProject.properties."env.GRAILS_HOME"

baseDir = System.getProperty("base.dir")
specDir = "${baseDir}/spec"
srcDir = "${baseDir}/grails-app"
destDir = "${specDir}/classes"

target("default": null) {
    clean()
    runEasyb()
}

target(initClasspath: "init classpath") {
    path (id: "easyb.classpath") {
        pathelement(path: destDir)

        fileset(dir: "${baseDir}") {
            include(name: "**/*.jar")
        }

        fileset(dir: "${grailsHome}") {
            include(name: "**/*.jar")
        }
    }
}

target(compile: "Compile Target Script") {
    depends(initClasspath)

    Ant.taskdef(name: "groovyc", classname: "org.codehaus.groovy.grails.compiler.GrailsCompiler") {
        classpath(refid: "easyb.classpath")
    }

    Ant.mkdir(dir: destDir)

    Ant.groovyc(srcdir: srcDir, destdir: destDir, encoding:"UTF-8") {
        classpath(refid: "easyb.classpath")
    }
}

target(runEasyb: "Execute Easyb") {
    depends(compile)

    Ant.taskdef(name: "easyb", classname: "org.disco.easyb.ant.BehaviorRunnerTask") {
        classpath(refid: "easyb.classpath")
    }

    Ant.easyb {
        classpath(refid: "easyb.classpath")
        behaviors(dir: "${specDir}") {
            include(name: "**/*Story.groovy")
            include(name: "**/*.story")
            include(name: "**/*Specification.groovy")
            include(name: "**/*.specification")
        }
    }
}

target(clean: "clean") {
    Ant.delete(dir: destDir)
}

処理としては、grails-app 内の .groovy ファイルをコンパイルした後に、spec ディレクトリ内のシナリオ・スペックファイルを実行するようになっている。


次に、easyb を処理できるように lib ディレクトリに以下の jar ファイルを配置しておく。(jar ファイルは easyb-0.8 のディレクトリ内にあるものを使用)

  • easyb-0.8.jar
  • commons-cli-1.1.jar

プラグインのインストール時に spec ディレクトリを自動的に作成する処理を実装するため、scripts/_Install.groovy に以下の処理を追加する。

Ant.property(environment:"env")
grailsHome = Ant.antProject.properties."env.GRAILS_HOME"

baseDir = System.getProperty("base.dir")

Ant.mkdir(dir: "${baseDir}/spec")

なお、scripts/_Install.groovy にはプラグインのインストール時(grais install-plugin 実行時)の処理を記述し、scripts/_Upgrade.groovy にはプラグインのアップグレード時(grails upgrade 実行時)の処理を記述するようになっている。

プラグインをパッケージ化

以下のコマンドを実行するとカレントディレクトリにプラグインをパッケージ化した zip ファイルが生成される。

>grails package-plugin

今回は、grails-easyb-sample-0.1.zip ファイルが生成された。

プラグインのインストールと動作確認

上記作業で作成したプラグインを新規作成した Grails アプリケーションプロジェクトにインストールする。

>grails create-app plugin-test
>cd plugin-test
>grails install-plugin ..\easySample\grails-easyb-sample-0.1.zip

動作確認のために、スペックの検証対象とするドメインクラスを作成。

>grails create-domain-class customer

生成された Customer クラスに name フィールドを追加

grails-app/domain/Customer.groovy
class Customer {
    String name
}

以下のようなスペックファイルを作成して spec ディレクトリに配置。

spec/CustomerScenario.story
scenario "Customer の name が未設定", {

    when "name を設定せずにインスタンス化", {
        newCustomerWithEmptyName = {
            new Customer()
        }
    }

    then "name は空の文字列", {
        customer = newCustomerWithEmptyName()
        customer.name.shouldBe ""
    }
}

grails easyb で実行して、失敗することを確認。

>grails easyb
・・・
[easyb] easyb is preparing to process 1 file(s)
[easyb] Running customer scenario story (CustomerScenario.story)
[easyb] FAILURE Scenarios run: 1, Failures: 1, Pending: 0, Time Elapsed: 0.501 sec
[easyb] 1 behavior run with 1 failure
[easyb] easyb execution FAILED

ドメインクラスの実装を変更して、再度 grails easyb を実行し成功することを確認。

grails-app/domain/Customer.groovy
class Customer {
    String name = ""
}
>grails easyb
・・・
[easyb] easyb is preparing to process 1 file(s)
[easyb] Running customer scenario story (CustomerScenario.story)
[easyb] Scenarios run: 1, Failures: 0, Pending: 0, Time Elapsed: 0.481 sec
[easyb] 1 behavior run with no failures
[easyb] easyb execution passed