Golang Cafe #6 まとめ text/templateパッケージを試す

2013/12/01に開催された「Golang Cafe #6」についてのまとめです。

他言語では外部ライブラリを利用してテンプレート処理を行うことが多いですが、Go言語ではデフォルトのパッケージに組み込まれています。

Go言語でテンプレートを使用するにはtext/templateパッケージとhtml/templateパッケージの2つがあるのですが、
今回はtext/templateパッケージのほうを可能な限り試しました。

ソースコード中にテンプレートを定義する

ソースコード中にテンプレートを定義して使用します。
template.Newメソッドでテンプレート名を指定し、template.Parseメソッドでテンプレートの内容を読み込ませます。
template.Mustメソッドでは指定するテンプレートの正当性をチェックしてくれます。不正なテンプレートの場合はpanicになります。
また、ソースコード中でテンプレートを定義する場合は`(バックククォート)を使用するほうが良さそうです。
template.Executeメソッドに出力先と値を指定します。

package main

import (
    "fmt"
    "os"
    "text/template"
)

type Member struct {
    Id   int
    Name string
    Tech string
}

func main() {

    const template_text = `初めてのテンプレート。名前は {{.Name}} です。
`

    tpl := template.Must(template.New("mytemplate").Parse(template_text))

    member := Member{1, "ほげほげ", "Go"}

    if err := tpl.Execute(os.Stdout, member); err != nil {
        fmt.Println(err)
    }

}

実行結果は、

初めてのテンプレート。名前は ほげほげ です。

ファイルにテンプレートを定義する

別ファイルに定義したテンプレートを読み込んで使用することができます。template.ParseFilesメソッドで読み込むテンプレートファイルを指定します。拡張子は何でもかまいません。
テンプレート名は読み込んだファイルのファイル名(フルパスからディレクトリを除いたもの)になります。

package main

import (
    "fmt"
    "os"
    "text/template"
)

type Member struct {
    Id   int
    Name string
    Tech string
}

func main() {

    tpl := template.Must(template.ParseFiles("sample02.tpl"))

    member := Member{1, "ほげほげ", "Go"}

    if err := tpl.Execute(os.Stdout, member); err != nil {
        fmt.Println(err)
    }

}

sample02.tpl

初めてのテンプレート。名前は {{.Name}} です。

実行結果は、

初めてのテンプレート。名前は ほげほげ です。

アクションや関数を使ってみる

テンプレート内では用意されているアクションや関数を使用することができます。
また、テンプレートに渡す値はネストさせることができます。

package main

import (
    "fmt"
    "os"
    "text/template"
)

type Member struct {
    Id     int
    Name   string
    Groups []Group
}

type Group struct {
    Code   string
    Name   string
    Leader bool
}

func (g Group) Display() string {
    return fmt.Sprintf("***%s***", g.Name)
}

func main() {

    tpl := template.Must(template.ParseFiles("sample03.tpl"))

    member := Member{1, "ほげほげ",
        []Group{
            Group{"01", "GDGChugoku", true},
            Group{"02", "OITEC", false},
        },
    }

    if err := tpl.Execute(os.Stdout, member); err != nil {
        fmt.Println(err)
    }

}

sample03.tpl

初めてのテンプレート。名前は {{.Name}} です。
所属しているグループは、{{len .Groups}} 件です。
{{range $index, $group := .Groups}}{{$index}} {{$group.Code}} {{$group.Name}} {{if $group.Leader}}リーダー{{end}} {{$group.Display}}
{{end}}

実行結果は、

初めてのテンプレート。名前は ほげほげ です。
所属しているグループは、2 件です。
0 01 GDGChugoku リーダー ***GDGChugoku***
1 02 OITEC  ***OITEC***

複数テンプレートの読み込み

複数のテンプレートを読み込み指定したテンプレートを使用することができます。
template.Lookupメソッドで使用するテンプレートを取得してtemplate.Executeメソッドで実行するか、template.ExecuteTemplateメソッドでテンプレート名を指定して実行します。
template.Lookupメソッドで読み込んでいないテンプレート名を指定した場合はpanicになります。
複数のテンプレートを読み込んで、template.Executeメソッドを使用した場合(template.ExecuteTemplateメソッドでテンプレート名を指定しない場合)はtemplate.ParseFilesメソッドで最初に指定したテンプレートが使用されます。

package main

import (
    "fmt"
    "os"
    "text/template"
)

type Member struct {
    Id   int
    Name string
    Tech string
}

func main() {

    tpl := template.Must(template.ParseFiles("sample04_1.tpl", "sample04_2.tpl"))

    tpl1 := tpl.Lookup("sample04_2.tpl")

    member := Member{1, "ほげほげ", "Go"}

    if err := tpl1.Execute(os.Stdout, member); err != nil {
        fmt.Println(err)
    }

    if err := tpl.ExecuteTemplate(os.Stdout, "sample04_2.tpl", member); err != nil {
        fmt.Println(err)
    }    

}

sample04_1.tpl

初めてのテンプレート。名前は {{.Name}} です。

sample04_2.tpl

初めてのテンプレート。技術は {{.Tech}} です。

実行結果は、

初めてのテンプレート。技術は Go です。
初めてのテンプレート。技術は Go です。

テンプレートから別のテンプレートを読み込む

テンプレート内で別のテンプレートを読み込むことができます。その際に値を渡すこともできます。
ただし、読み込むすべてのテンプレートをtemplate.ParseFilesで指定しておかなくてはいけないようです。

package main

import (
    "fmt"
    "os"
    "text/template"
)

type Member struct {
    Id   int
    Name string
    Tech string
}

func main() {

    tpl := template.Must(template.ParseFiles("sample05_1.tpl", "sample05_2.tpl"))

    member := Member{1, "ほげほげ", "Go"}

    if err := tpl.Execute(os.Stdout, member); err != nil {
        fmt.Println(err)
    }

}

sample05_1.tpl

初めてのテンプレート。名前は {{.Name}} です。

{{template "sample05_2.tpl" .Tech}}

sample05_2.tpl

読み込まれたテンプレート。技術は {{.}} です。

実行結果は、

初めてのテンプレート。名前は ほげほげ です。

読み込まれたテンプレート。技術は Go です。

まとめ

Go言語のテンプレートを使用すれば、JSPと同じようなことが簡単にできそうです。
最近はページの生成をすることは少なくなってきたような気もしますが、メールの文面に使用したり、ソースコードのジェネレータに使用したり、使い道はまだありそうです。