Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 11364] ファイルの概要

このコミットは、Go言語の標準ライブラリである text/template パッケージに、Template 型の使用方法を示す新しい例 (example_test.go) を追加するものです。この例は、テンプレートの定義、データの準備、テンプレートのパース、そしてデータを用いたテンプレートの実行という一連のプロセスを具体的に示しており、text/template パッケージの基本的な使い方を理解するための優れたリファレンスとなります。

コミット

text/template: add example for Template

R=golang-dev, r CC=golang-dev https://golang.org/cl/5564050

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/0bb8ce2984e4d8cabbb10a44912ad4141d532c6d

元コミット内容

text/template: add example for Template

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5564050

変更の背景

Go言語の標準ライブラリは、その機能の豊富さだけでなく、優れたドキュメンテーションと豊富な例によっても知られています。text/template パッケージは、テキストベースの出力を生成するための強力なツールであり、ウェブアプリケーションのHTML生成や、設定ファイルの動的な生成など、多岐にわたる用途で利用されます。

このコミットの背景には、text/template パッケージの Template 型の具体的な使用例を提供し、開発者がこのパッケージをより容易に理解し、活用できるようにするという目的があります。特に、ExampleTemplate のような関数は、Goのテストフレームワークによって自動的に実行され、その出力がドキュメンテーションに組み込まれるため、ライブで動作するコード例として非常に価値があります。これにより、ユーザーはコードを実際に実行することなく、テンプレートの動作を確認できるようになります。

前提知識の解説

Go言語の text/template パッケージ

text/template パッケージは、Go言語でテキストベースの出力を生成するためのデータ駆動型テンプレートエンジンです。これは、HTML、XML、プレーンテキストなど、任意のテキスト形式のドキュメントを生成するのに使用できます。主な特徴は以下の通りです。

  • データ駆動: テンプレートは、Goの構造体、マップ、スライスなどのデータ構造から値を受け取り、それらをテンプレート内のプレースホルダーに挿入します。
  • アクション: テンプレート内では、データにアクセスしたり、条件分岐 (if) や繰り返し (range) を行ったり、他のテンプレートをインクルードしたりするための「アクション」と呼ばれる構文を使用します。
  • パイプライン: 複数のアクションを | で連結して、前の結果を次のアクションの入力として渡すことができます。
  • 関数: 組み込み関数やカスタム関数を定義して、テンプレート内でデータを変換したり操作したりできます。

テンプレートの基本構文

text/template のテンプレートは、プレーンテキストと、二重中括弧 {{...}} で囲まれた「アクション」で構成されます。

  • データフィールドへのアクセス: {{.FieldName}} のように、ドット . の後にフィールド名を続けることで、渡されたデータのフィールドにアクセスできます。ドット . 自体は現在のコンテキストの値を表します。
  • 条件分岐 (if): {{if .Condition}}...{{else}}...{{end}} の形式で、条件に基づいてコンテンツをレンダリングします。
  • 変数 (with): {{with .Data}}...{{end}} は、.Data が存在し、かつ非ゼロ値である場合に、そのスコープ内でドット . のコンテキストを .Data に変更します。これにより、ネストされたデータ構造にアクセスしやすくなります。
  • コメント: {{/* This is a comment */}} のように、テンプレート内にコメントを記述できます。

Goの example_test.go ファイル

Go言語では、_test.go で終わるファイルはテストファイルとして扱われます。その中でも、Example というプレフィックスを持つ関数は特別な意味を持ちます。

  • Example 関数は、go test コマンドによって実行され、その出力が標準出力に書き込まれます。
  • この出力は、関数のコメントブロック内の Output: コメントと比較されます。一致しない場合、テストは失敗します。
  • さらに重要なのは、Example 関数は go doc コマンドによって生成されるドキュメンテーションに自動的に組み込まれる点です。これにより、ユーザーはパッケージのドキュメントを見るだけで、その機能の具体的な使用例と期待される出力を確認できます。

このコミットで追加された ExampleTemplate 関数は、まさにこの目的のために設計されており、text/template パッケージの Template 型の利用方法を明確に示しています。

技術的詳細

このコミットで追加された example_test.go ファイルは、text/template パッケージの基本的なワークフローを具体的に示しています。

  1. テンプレートの定義: const letter = ``...``` で、バッククォート ( ) を使って複数行の文字列としてテンプレートが定義されています。このテンプレートには、以下の text/template のアクションが含まれています。

    • {{.Name}}: Name フィールドの値を挿入します。
    • {{if .Attended}}...{{else}}...{{end}}: Attended フィールドが真偽値に基づいて異なるテキストをレンダリングします。
    • {{with .Gift}}...{{end}}: Gift フィールドが存在し、かつ空でない場合に、その値を . のコンテキストとして利用し、Thank you for the lovely {{.}}. をレンダリングします。Gift が空文字列の場合、このブロックはスキップされます。
  2. データの準備: Recipient という構造体が定義され、Name (文字列), Gift (文字列), Attended (真偽値) の3つのフィールドを持っています。この構造体のスライス recipients が作成され、テンプレートに渡すデータとして使用されます。各 Recipient インスタンスは、手紙の受取人一人ひとりの情報を表します。

  3. テンプレートの作成とパース: t := template.Must(template.New("letter").Parse(letter)) の行で、テンプレートが初期化され、パースされます。

    • template.New("letter"): 新しいテンプレートインスタンスを作成し、名前を "letter" とします。テンプレートの名前は、複数のテンプレートを扱う際に識別するために使用されます。
    • .Parse(letter): 定義されたテンプレート文字列 letter をパースします。このステップでテンプレートの構文が解析され、内部的な表現に変換されます。
    • template.Must(...): Parse メソッドはエラーを返す可能性があるため、template.Must ヘルパー関数が使用されています。Must は、エラーが発生した場合にパニックを引き起こします。これは、プログラムの起動時にテンプレートのパースが失敗した場合に、すぐにクラッシュさせることで、開発者が問題を早期に発見できるようにするための一般的なGoのイディオムです。
  4. テンプレートの実行: for _, r := range recipients { ... } ループ内で、各 Recipient データに対してテンプレートが実行されます。

    • err := t.Execute(os.Stdout, r): Execute メソッドは、パースされたテンプレートとデータを結合し、結果を io.Writer (この場合は os.Stdout、つまり標準出力) に書き込みます。r は、現在のループで処理されている Recipient 構造体のインスタンスであり、これがテンプレートに渡されるデータとなります。
    • エラーハンドリング: if err != nil { log.Println("executing template:", err) } は、テンプレートの実行中にエラーが発生した場合に、そのエラーをログに出力します。

この例は、text/template を使って動的にテキストを生成する際の、データ構造の設計、テンプレートの記述、そしてGoコードからのテンプレートの操作という、一連のプロセスを簡潔かつ効果的に示しています。

コアとなるコードの変更箇所

このコミットでは、src/pkg/text/template/example_test.go という新しいファイルが追加されています。

diff --git a/src/pkg/text/template/example_test.go b/src/pkg/text/template/example_test.go
new file mode 100644
index 0000000000..b7701ea265
--- /dev/null
+++ b/src/pkg/text/template/example_test.go
@@ -0,0 +1,69 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package template_test
+
+import (
+	"log"
+	"os"
+	"text/template"
+)
+
+// Dear Aunt Mildred,
+// 
+// It was a pleasure to see you at the wedding.
+// Thank you for the lovely bone china tea set.
+// 
+// Best wishes,
+// Josie
+// 
+// Dear Uncle John,
+// 
+// It is a shame you couldn't make it to the wedding.
+// Thank you for the lovely moleskin pants.
+// 
+// Best wishes,
+// Josie
+// 
+// Dear Cousin Rodney,
+// 
+// It is a shame you couldn't make it to the wedding.
+// 
+// Best wishes,
+// Josie
+func ExampleTemplate() {
+	// Define a template.
+	const letter = `
+Dear {{.Name}},
+{{if .Attended}}
+It was a pleasure to see you at the wedding.{{else}}
+It is a shame you couldn't make it to the wedding.{{end}}
+{{with .Gift}}Thank you for the lovely {{.}}.` +
+`
+{{end}}
+Best wishes,
+Josie
+`
+
+	// Prepare some data to insert into the template.
+	type Recipient struct {
+		Name, Gift string
+		Attended   bool
+	}
+	var recipients = []Recipient{
+		{"Aunt Mildred", "bone china tea set", true},
+		{"Uncle John", "moleskin pants", false},
+		{"Cousin Rodney", "", false},
+	}
+
+	// Create a new template and parse the letter into it.
+	t := template.Must(template.New("letter").Parse(letter))
+
+	// Execute the template for each recipient.
+	for _, r := range recipients {
+		err := t.Execute(os.Stdout, r)
+		if err != nil {
+			log.Println("executing template:", err)
+		}
+	}
+}

コアとなるコードの解説

追加された example_test.go ファイルの ExampleTemplate 関数は、text/template パッケージの Template 型の具体的な使用方法をデモンストレーションしています。

  1. パッケージ宣言とインポート:

    package template_test
    
    import (
    	"log"
    	"os"
    	"text/template"
    )
    

    package template_test は、このファイルが text/template パッケージの外部テストであることを示します。これにより、テスト対象のパッケージの内部実装に依存せず、公開されたAPIのみを使用してテストを行うことができます。必要な標準ライブラリとして log (エラーログ用)、os (標準出力アクセス用)、そしてもちろん text/template がインポートされています。

  2. ExampleTemplate 関数の定義と出力コメント:

    // Dear Aunt Mildred,
    // ... (期待される出力) ...
    func ExampleTemplate() {
    

    この関数は Example プレフィックスを持つため、go test 実行時に自動的に実行され、その出力が関数のコメントブロックに記述された期待される出力と比較されます。コメントブロックには、テンプレートが実行された結果として標準出力に表示されるであろうテキストが正確に記述されています。これはGoのドキュメンテーションシステムと連携し、ライブで動作するコード例として機能します。

  3. テンプレート文字列 letter の定義:

    	const letter = `
    Dear {{.Name}},
    {{if .Attended}}
    It was a pleasure to see you at the wedding.{{else}}
    It is a shame you couldn't make it to the wedding.{{end}}
    {{with .Gift}}Thank you for the lovely {{.}}.` +
    `
    {{end}}
    Best wishes,
    Josie
    `
    

    letter 定数は、バッククォートで囲まれた生文字列リテラルとして定義されており、複数行にわたるテンプレートの内容を含んでいます。

    • Dear {{.Name}},: 渡されるデータの Name フィールドの値がここに挿入されます。
    • {{if .Attended}}...{{else}}...{{end}}: Attended フィールドが true なら「It was a pleasure...」、false なら「It is a shame...」が出力されます。
    • {{with .Gift}}...{{end}}: Gift フィールドが空文字列でなければ、その値({{.}} でアクセス)を使って「Thank you for the lovely ...」という行が出力されます。Gift が空文字列の場合、このブロック全体はスキップされます。
  4. データ構造 Recipient とデータの準備:

    	type Recipient struct {
    		Name, Gift string
    		Attended   bool
    	}
    	var recipients = []Recipient{
    		{"Aunt Mildred", "bone china tea set", true},
    		{"Uncle John", "moleskin pants", false},
    		{"Cousin Rodney", "", false},
    	}
    

    テンプレートに渡すデータの型として Recipient 構造体が定義されています。この構造体は、受取人の名前、贈られた贈り物、結婚式に出席したかどうかを保持します。そして、この Recipient 型のスライス recipients が初期化され、3人の異なる受取人のデータが用意されています。

  5. テンプレートのパース:

    	t := template.Must(template.New("letter").Parse(letter))
    

    template.New("letter") で新しいテンプレートインスタンスを作成し、Parse(letter) でテンプレート文字列を解析します。template.Must は、パース中にエラーが発生した場合にパニックを引き起こすヘルパー関数です。これにより、テンプレートの構文エラーが実行時ではなく、プログラムの初期化段階で検出されるようになります。

  6. テンプレートの実行:

    	for _, r := range recipients {
    		err := t.Execute(os.Stdout, r)
    		if err != nil {
    			log.Println("executing template:", err)
    		}
    	}
    

    recipients スライスをループし、各 Recipient データに対してテンプレートを実行します。t.Execute(os.Stdout, r) は、テンプレート t をデータ r で実行し、結果を標準出力 (os.Stdout) に書き込みます。エラーが発生した場合は、log.Println でログに出力されます。

このコードは、text/template パッケージの基本的な機能(テンプレートの定義、データのバインディング、条件分岐、オプションのコンテンツ表示)を明確かつ簡潔に示しており、Goのテンプレートシステムを学ぶ上で非常に役立つ例となっています。

関連リンク

参考にした情報源リンク