No Programming, No Life

プログラミング関連の話題や雑記

GroovyでBuilder系の定義ファイルの外出しする

はじめに

GroovyはほにゃららBuilderが充実していますが、ちょっとだけ不満点を挙げるとしたらそれはコードに直接埋め込まなければいけないことではないだろうか。

new HogeBuilder().aaa {
  bbb (ccc: 'ddd') {
     eee() {
        .
        .
        .
     }
  }
}

こんな感じに。

そこで

このBuilder定義部分*1を別ファイルなどに外出しにできないか?と思って、色々試してみたところできました。以下にソースを載せます。

ソース (動作確認: Groovy Version: 1.7.5 JVM: 1.6.0_20)

サンプルとして前に書いたMarkupBuilderでHTML生成を試してみた - No Programming, No Lifeで利用したソースを改造しました。

markup.groovy
import groovy.xml.MarkupBuilder

MarkupBuilder.metaClass.define {
  fromString = { str, var = [:] ->
    def code = "{-> ${str} }" // a closure
    def received = new GroovyShell([var: var] as Binding).evaluate(code)
    received.delegate = delegate // MarkupBuilderにデリゲート
    received()
  }
  fromFile = { file, var = [:] ->
    delegate.fromString(file.getText('UTF-8'), var)
  }
}


int size = 100
def writer = new File('groovy_htmlbuilder_test.html').newPrintWriter('UTF-8')
def html = new MarkupBuilder(writer)
html.doubleQuotes = true // 属性は ' じゃなくて " で。
html.fromFile(new File('markup.ghtml'), [size: size]) // 引数も渡せる!
writer.close()
markup.ghtml
html {
   head {
      meta('http-equiv': 'Content-Type', content: 'text/html; charset=UTF-8')
      title('MarkupBuilderでHTMLを生成')
      style(type:'text/css') {
         mkp.yieldUnescaped('''
            <!--
            body {
               color : #000000;
               background : #E6DED9;
               font-size : var.size;
            }
            //-->
         ''')
      }
   }
   body() {
      mkp.comment('''
         追記:2010-05-14
         バージョンが上がってyieldUnescapedやyieldや
         ここでも使っているcommentみたいなヘルパーメソッドは
         mkp. を付けることが必要になったみたいです。
      ''')
      mkp.yield('MarkupBuilderのテストです。'); br()
      a(href:'http://d.hatena.ne.jp/fumokmm/', 'No Programming, No Life'); br()
      a(href:'http://d.hatena.ne.jp/fumokmm/20090131/1233428513', 'MarkupBuilderでHTML生成を試してみた'); br()
      mkp.yield('↑当ブログはこちらからどうぞ'); br()
   }
}

解説

例としてMarkupBuilderを使っています。ここの#slurpメソッドを参考にちょっと拡張しています。*2動作としては、ファイルを読み込み、文字列をGroovyScriptとして評価して実行するという流れです。
markup.ghtmlが外出ししたBuilderの定義ファイルです。拡張子は適当にghtmlとかにしています。ここでは直接MarkupBuilderの書き方ができます。

これができると何が嬉しいか

このようにBuilderの定義ファイルを外出しにできることで、変換するスクリプト*3を一つ用意しておけば、いくらでもBuilderファイルを処理できるようになったり、あとは、下の参考のところでも挙げてあるような、GrONのような新しい定義形式を自分で作り出せたりするところですかね。
何はともあれ、これで今後のBuilderライフが楽しくなりそうです。

追記(2010-10-22)

サンプルにmetaタグを追加。(requested by here)
File#newPrintWriter(charset)を利用して文字コードを指定するよう修正しました。

*1:上記例ではaaa{ ... }の部分。

*2:ghtmlの方でvar.変数名という形式で#fromFile時の第2引数で渡したマップの値を参照できるようにしています。

*3:もしくはClassにしてもいいですね。