LoginSignup
2
2

More than 5 years have passed since last update.

GroovyのGStringTemplateEngineでクロージャをbindして使う

Last updated at Posted at 2013-03-23

GroovyのGStringTemplateEngineでクロージャをbindして使ってみる。

http://www.glamenv-septzen.net/view/1168 より転載

検証環境:

  • Win7 Pro 64bit
  • JDK 1.7 64bit
  • Groovy 1.8.9

問題点

GStringTemplateEngineでは、テンプレートテキストは groovy.tmp.templates.GStringTemplateScript[N] ([N]にはインクリメントされる数字が入る)のclosure内で処理されます。テンプレート内で

<%= dump() %>
<%= inspect() %>

すると以下のように出力されます。

↓↓"<%= dump() %>"の出力
<groovy.tmp.templates.GStringTemplateScript9$_getTemplate_closure1@73b05494 \
    delegate=groovy.lang.Binding@3d80fbe2 \
    owner=groovy.tmp.templates.GStringTemplateScript9@5f18d1f9 \
    thisObject=groovy.tmp.templates.GStringTemplateScript9@5f18d1f9 ...

↓↓"<%= inspect() %>"の出力
groovy.tmp.templates.GStringTemplateScript9$_getTemplate_closure1@73b05494

ということで、

def binding = [
    title : "...",
    c : { s -> ... },

とした場合、テンプレート中で

<%= c(title) %>

としても、「groovy.tmp.templates.GStringTemplateScript[N]にはc()なんてメソッドありません」とエラーになります。

方法その1:"call()"で明示的に呼ぶ

以下のように"call()"メソッドで明示的にclosureを呼び出します。

<%= c.call(title) %>

例:

@Grapes([
@Grab(group='org.apache.commons', module='commons-lang3', version='3.1'),
])

import org.apache.commons.lang3.*

String input_s = "<hello, 'bonjour' & \"evening\">"
def binding = [
    title : input_s,
    h1 : { s -> StringEscapeUtils.escapeHtml4(s).replace("'", '&#39;') },
    h2 : { s ->
        s.toString().replace('&', '&amp;').replace('<', '&lt;')
        .replace('>', '&gt;').replace('"', '&quot;').replace("'", '&#39;')
        },
]
println binding.h1(input_s)
println binding.h2(input_s)

def engine = new groovy.text.GStringTemplateEngine()
def text = '''\
Raw Title : [<%= title %>]
HTML Escaped Title(1) : [<%= h1.call(title) %>]
HTML Escaped Title(2) : [<%= h2.call(title) %>]
'''
def template = engine.createTemplate(text).make(binding)
println template.toString()

実行結果:

&lt;hello, &#39;bonjour&#39; &amp; &quot;evening&quot;&gt;
&lt;hello, &#39;bonjour&#39; &amp; &quot;evening&quot;&gt;
Raw Title : [<hello, 'bonjour' & "evening">]
HTML Escaped Title(1) : [&lt;hello, &#39;bonjour&#39; &amp; &quot;evening&quot;&gt;]
HTML Escaped Title(2) : [&lt;hello, &#39;bonjour&#39; &amp; &quot;evening&quot;&gt;]

方法その2:Object.mixinでグローバルに呼べるようにする

"Object"クラスにテンプレートから使いたいメソッドをmixinしてしまいます。一種のグローバル関数として使えるようになりますが、他のライブラリやモジュールとの衝突を考慮する必要があったり、パフォーマンスを気にする場合は検証する必要があったりと、一見強力で魅力的に見える反面、どこで問題が発生するか予測が難しくなるデメリットもそれなりに大きくなると思われます。

例:

@Grapes([
@Grab(group='org.apache.commons', module='commons-lang3', version='3.1'),
])

import org.apache.commons.lang3.*

String input_s = "<hello, 'bonjour' & \"evening\">"

class Helper {
    String h1(String s) { StringEscapeUtils.escapeHtml4(s).replace("'", '&#39;') }
    String h2(String s) {
        s.toString().replace('&', '&amp;').replace('<', '&lt;')
            .replace('>', '&gt;').replace('"', '&quot;').replace("'", '&#39;')
    }
}
Object.mixin(Helper)

def binding = [
    title : input_s,
]
println h1(input_s)
println h2(input_s)

def engine = new groovy.text.GStringTemplateEngine()
def text = '''\
Raw Title : [<%= title %>]
HTML Escaped Title(1) : [<%= h1(title) %>]
HTML Escaped Title(2) : [<%= h2(title) %>]
'''
def template = engine.createTemplate(text).make(binding)
println template.toString()

実行結果:

&lt;hello, &#39;bonjour&#39; &amp; &quot;evening&quot;&gt;
&lt;hello, &#39;bonjour&#39; &amp; &quot;evening&quot;&gt;
Raw Title : [<hello, 'bonjour' & "evening">]
HTML Escaped Title(1) : [&lt;hello, &#39;bonjour&#39; &amp; &quot;evening&quot;&gt;]
HTML Escaped Title(2) : [&lt;hello, &#39;bonjour&#39; &amp; &quot;evening&quot;&gt;]
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2