倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

「GroovyでBuilder系の定義ファイルの外出しする」の別アプローチを思いついてみた

GroovyでBuilder系の定義ファイルの外出しする」の別アプローチを考えてみました。

ここで見ていく方法は、ちょっと(かなり?)制限がある代わりに、結構簡潔に書けてると思います。 というより、普通によく見かけるような Groovy コードです。

HTML の定義ファイル


まずはパース対象となる Builder 系の定義ファイル。

html{
    header{
        title('倭マン日記')
    }
    body{
        p('「GroovyでBuilder系の定義ファイルの外出しする」の別アプローチを思いついてみた')
    }
}

まぁ、こんなんですよ〜ってだけ。

Builder 系定義ファイルからツリー構造を構築


さて、上記のような定義ファイルからツリー構造を構築するには、以下のようにします:

def script = new GroovyShell().parse(new File('Html.groovy'))

def builder = new groovy.xml.MarkupBuilder()                     // 構築に用いる Builder
script.html = { args -> builder.invokeMethod('html', args) }  // ルートノード html が呼ばれたら、builder に処理を委譲

script.run()

最初の行で 'Html.groovy' の内容から Script オブジェクトを生成し、最後の行でその Script オブジェクトを実行します。 builder の宣言と初期化は構築に用いる Builderの準備です。

で、Script オブジェクトに Closure 型の html プロパティを設定しているのがミソ。 Html.groovy をスクリプトとして実行した場合、ルートノードの html はこの Script オブジェクトのメソッドだと認識されるので、このメソッド html() が呼ばれたらその処理を Builder に委譲するようにします。 これで万事オッケー。

問題としては、ルートノードを決め打ちしてるってとこでしょう。 'Html.groovy' のルートノードは html でなければなりません。 それ以外は扱えません。 html オンリー。 2, 3個、他のルートノードを定義する分には大した手間ではありませんが、ルートノードが任意のノードでも動作するようにさせるにはどうすればいいものやら。 ちなみに、ルートノード以外のノードは何でも構いません。

逆にこの方法のメリットとしては、'Html.groovy' に import 文が書ける!ってことです。 'import' is important!(←コレ書きたかっただけ:-))

クラスとして抽出する


さて、上記のコードは簡潔に完結してますが、何となく散らばってる感が否めないので、これを一個のクラスとしてまとめてみます。 html に決め打ちしてるので、クラス名は HtmlBuilder にしておきましょう。

HtmlBuilder クラス

fromString() / fromFile() メソッドは「GroovyでBuilder系の定義ファイルの外出しする」の模倣。 getProperty() / setProperty() メソッドは便利のために実装してます:

import groovy.xml.MarkupBuilder

class HtmlBuilder{
    
    BuilderSupport builder

    HtmlBuilder(BuilderSupport builder = new MarkupBuilder()){
        this.builder = builder
    }

    def fromString(String expr, var = [:]){
        def script = new GroovyShell(var as Binding).parse(expr)
        script.html = { args -> this.builder.invokeMethod('html', args) }
        script.run()
    }

    def fromFile(File file, var = [:]){
        fromString(file.text, var)
    }

    //********** getProperty() / setProperty() **********
    @Override
    def getProperty(String name){
        name == 'builder' ? this.builder : this.builder.getProperty(name)
    }

    @Override
    void setProperty(String name, value){
        name == 'builder' ? this.builder = value : this.builder.setProperty(name, value)
    }
}

使用例

HtmlBuilder の使用方法は「GroovyでBuilder系の定義ファイルの外出しする」と似てます:

def html = new HtmlBuilder()
html.doubleQuotes = true
html.fromFile(new File('Html.groovy')).println()

まぁ、こんな感じで。

Groovyイン・アクション

Groovyイン・アクション