パッケージJava製品開発担当の大です。こんにちは。

前回に引き続き、FreeMarker Template Language(以下 FTL)の解説をします。

今回は、マクロに関するお話です。

マクロとは

FreeMarkerにおけるマクロとは、「変数に束縛されたテンプレートのフラグメント(断片)」です。簡単な例をあげましょう:

<#macro greet>
  こんにちは!
</#macro>

ここで定義された変数greetは、ユーザ定義のディレクティブとして呼び出すことができます。通常のディレクティブと違い、ユーザ定義のディレクティブは先頭に「@」をつけて呼び出します:

<@greet />

実行すると、こんな風に表示されます:

  こんにちは!

マクロは、引数をとることもできます:

<#macro greet user>
  こんにちは、${user}さん!
</#macro>

呼び出しはこんな風になります:

<@greet user="大"/>

実行結果:

 こんにちは、大さん!

関数との違い

マクロは、前回出てきた関数とは異なるものです。
関数は値を返さなければなりませんが、マクロは、値を返すことができません。
マクロの本体は、前述のとおり「テンプレートのフラグメント」なので、テキストはそのままテキストとして出力されますが、関数の本体ではテキスト出力があっても無視されます。
関数呼び出しは式を書ける場所ならどこでもできますが、マクロ呼び出しはFTLタグを書ける場所でしかできません。

たとえば、上で定義したgreetマクロぐらいの内容なら、関数として定義、使用することも可能でしょう。

<#function greet user>
  <#return "こんにちは、${user}さん!">
</#function>
${greet("大")}

しかし、以下のようににちょっと複雑な出力をするなら、マクロでやるほうが関数でやるより便利です(関数でもやれないことはありませんが。。)。

<#macro table cols rows>
  <table>
    <#list 1..rows as row>
      <tr>
        <#list 1..cols as col>
          <td>${row}, ${col}</td>
        </#list>  
      </tr>
    </#list>
  </table>
</#macro>
<@table cols=3 rows=2 />
  <table>
      <tr>
          <td>1, 1</td>
          <td>1, 2</td>
          <td>1, 3</td>
      </tr>
      <tr>
          <td>2, 1</td>
          <td>2, 2</td>
          <td>2, 3</td>
      </tr>
  </table>

returnディレクティブ

マクロ中でもreturnディレクティブを使用できます。が、前述のとおり値を返すことはできません。処理をそこで終了するというだけです。

<#macro hoge>
  ここは表示されます。
  <#return>
  ここは表示されません。
</#macro>

nestedディレクティブ

nestedディレクティブは、マクロ呼び出し時に渡されたテンプレートのフラグメントをマクロ中から呼び出します。Rubyをご存知でしたら、yieldみたいなものと考えればわかりやすいでしょう。nestedを利用すれば、マクロをより汎用的に定義することができます。たとえば、リストを引数にとり、その要素をnested呼び出しに使用するマクロを考えます。

<#macro lprint lst>
  <#list lst as item>
  ・${item}<#nested item />
  </#list>
</#macro>

このマクロを、

<@lprint 1..3; x>^2 = ${x * x}</@lprint>

のように呼び出せば、

  ・1^2 = 1
  ・2^2 = 4
  ・3^2 = 9

このように表示されます。また、

<@lprint 1..3; x>^3 = ${x * x * x}</@lprint>

と違うフラグメントを渡せば、

  ・1^3 = 1
  ・2^3 = 8
  ・3^3 = 27

違う出力が得られます。

<@lprint ["Let's go", "to the", "land of Medetai"] />

nested呼び出しするものがなければ、何も行われません。

  ・Let's go
  ・to the
  ・land of Medetai