[インデックス 10793] ファイルの概要
このコミットは、Go言語の標準ライブラリである html/template パッケージに対する変更です。具体的には、以下の2つのファイルが変更されています。
src/pkg/html/template/escape_test.go:html/templateパッケージのテストファイル。src/pkg/html/template/template.go:html/templateパッケージの主要な実装ファイル。
変更の目的は、html/template パッケージの公開APIが text/template パッケージに直接依存しないようにすることです。
コミット
commit 1402d1a68615d037365158578b0c4861e0fb4157
Author: Rob Pike <r@golang.org>
Date: Wed Dec 14 11:22:17 2011 -0800
html/template: define the FuncMap type locally
This redefinition means that the public signature of html/template
does not refer to text/template.
Fixes #2546.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5487083
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1402d1a68615d037365158578b0c4861e0fb4157
元コミット内容
html/template: define the FuncMap type locally
This redefinition means that the public signature of html/template
does not refer to text/template.
Fixes #2546.
変更の背景
Go言語の html/template パッケージは、HTML出力の自動エスケープ機能を提供するテンプレートエンジンです。これは、基本的なテンプレート機能を提供する text/template パッケージの上に構築されています。
このコミットが行われる前は、html/template パッケージの Funcs メソッドが text/template.FuncMap 型を直接引数として受け取っていました。これは、html/template の公開APIが text/template パッケージの型に直接依存していることを意味します。
ソフトウェア設計において、モジュール間の結合度(Coupling)は低い方が望ましいとされています。結合度が高いと、一方のモジュールが変更された際に、それに依存する他のモジュールも変更が必要になる可能性が高まります。この場合、html/template が text/template.FuncMap に直接依存しているため、text/template の FuncMap 型の定義が変更された場合、html/template もそれに合わせて変更する必要が生じる可能性があります。
このコミットの背景にあるのは、このような不必要な依存関係を解消し、html/template パッケージの独立性を高めることです。コミットメッセージにある「public signature of html/template does not refer to text/template」という記述は、この目的を明確に示しています。これにより、html/template は text/template の内部実装の詳細からより疎結合になり、将来的な変更に対する堅牢性が向上します。
Fixes #2546 という記述がありますが、このIssueはGoの公開Issueトラッカーでは直接見つかりませんでした。これは、内部的なIssueトラッカーの番号であるか、非常に古いIssueである可能性があります。しかし、コミットメッセージとコードの変更内容から、その目的は明確です。
前提知識の解説
Go言語の text/template パッケージ
text/template パッケージは、Go言語におけるテキストベースのテンプレートエンジンを提供します。これは、プレースホルダーを含むテキストを解析し、データ構造から提供された値でそれらを置き換えることができます。主な機能には以下があります。
- テンプレートの解析と実行: テンプレート文字列を解析し、指定されたデータ構造を基に最終的なテキストを生成します。
- アクション:
{{.FieldName}}のような構文でデータ構造のフィールドにアクセスしたり、{{range .Slice}}...{{end}}でループ処理を行ったり、{{if .Condition}}...{{end}}で条件分岐を行ったりできます。 - 関数: テンプレート内でカスタム関数を呼び出すことができます。これらの関数は
FuncMapを介してテンプレートに登録されます。
Go言語の html/template パッケージ
html/template パッケージは、text/template パッケージを基盤としていますが、HTML出力に特化しています。最も重要な機能は、コンテキストに応じた自動エスケープです。これにより、クロスサイトスクリプティング(XSS)などのセキュリティ脆弱性を防ぐことができます。例えば、ユーザーが入力した文字列をHTMLページに表示する際に、悪意のあるスクリプトが埋め込まれていても、html/template が自動的にエスケープ処理を行うため、スクリプトが実行されるのを防ぎます。
FuncMap とは
FuncMap は、テンプレート内で使用できるカスタム関数を登録するためのマップ型です。キーは関数の名前(文字列)、値はGoの関数(interface{} 型)です。これらの関数は、テンプレートの実行時に呼び出され、動的なコンテンツ生成に利用されます。
text/template パッケージには text/template.FuncMap という型が定義されており、これは map[string]interface{} のエイリアスです。
型のエイリアスと依存関係
Go言語では、type NewType OldType のようにして既存の型に新しい名前(エイリアス)を付けることができます。このコミットでは、html/template パッケージ内で FuncMap という新しい型を定義していますが、その基底型は map[string]interface{} であり、これは text/template.FuncMap と同じ基底型です。
重要なのは、html/template が text/template.FuncMap を直接参照する代わりに、独自の FuncMap 型を定義することで、html/template の公開APIから text/template への直接的な型依存を排除している点です。これにより、html/template を利用する側は、text/template パッケージをインポートすることなく、html/template の Funcs メソッドを利用できるようになります。
技術的詳細
このコミットの核心は、html/template パッケージが text/template パッケージの FuncMap 型に直接依存するのをやめ、独自の FuncMap 型を定義した点にあります。
変更前は、html/template の Funcs メソッドのシグネチャは以下のようになっていました。
func (t *Template) Funcs(funcMap template.FuncMap) *Template
ここで template.FuncMap は text/template パッケージの FuncMap を指します。このため、html/template を利用するユーザーは、Funcs メソッドを呼び出す際に text/template をインポートし、その FuncMap 型を使用する必要がありました。
このコミットでは、html/template パッケージ内に FuncMap という新しい型を定義しました。
type FuncMap map[string]interface{}
この新しい FuncMap 型は、text/template.FuncMap と同じ基底型 map[string]interface{} を持ちます。これにより、html/template の Funcs メソッドのシグネチャは以下のように変更されました。
func (t *Template) Funcs(funcMap FuncMap) *Template
この変更により、html/template の公開APIから text/template への直接的な型依存がなくなりました。html/template を利用する側は、html/template パッケージが提供する FuncMap 型を使用すればよくなり、text/template を明示的にインポートする必要がなくなります。
しかし、html/template は内部的には text/template を利用しています。そのため、html/template の Funcs メソッドの実装では、受け取った html/template.FuncMap 型の値を text/template.FuncMap 型に型変換して、内部の text/template.Template オブジェクトの Funcs メソッドに渡す必要があります。これは、t.text.Funcs(template.FuncMap(funcMap)) という行で行われています。ここで template.FuncMap(funcMap) は、html/template の FuncMap 型の値を text/template の FuncMap 型に変換しています。Go言語では、基底型が同じであれば、このように型変換を行うことができます。
この変更は、html/template パッケージのAPI設計をよりクリーンにし、ユーザーが html/template を使う際に text/template の詳細を意識する必要をなくすという点で重要です。これは、Goの標準ライブラリがモジュール性と独立性を重視していることの一例と言えます。
コアとなるコードの変更箇所
src/pkg/html/template/escape_test.go
--- a/src/pkg/html/template/escape_test.go
+++ b/src/pkg/html/template/escape_test.go
@@ -654,7 +654,7 @@ func TestEscape(t *testing.T) {
for _, test := range tests {
tmpl := New(test.name)
// TODO: Move noescape into template/func.go
- tmpl.Funcs(template.FuncMap{
+ tmpl.Funcs(FuncMap{
"noescape": func(a ...interface{}) string {
return fmt.Sprint(a...)
},
@@ -792,7 +792,7 @@ func TestEscapeSet(t *testing.T) {
// pred is a template function that returns the predecessor of a
// natural number for testing recursive templates.
- fns := template.FuncMap{"pred": func(a ...interface{}) (interface{}, error) {
+ fns := FuncMap{"pred": func(a ...interface{}) (interface{}, error) {
if len(a) == 1 {
if i, _ := a[0].(int); i > 0 {
return i - 1, nil
このテストファイルでは、tmpl.Funcs() および fns の初期化において、template.FuncMap の代わりに新しく定義された FuncMap 型が使用されるように変更されています。これは、APIの変更に合わせてテストコードも更新されたことを示しています。
src/pkg/html/template/template.go
--- a/src/pkg/html/template/template.go
+++ b/src/pkg/html/template/template.go
@@ -154,12 +154,20 @@ func (t *Template) Name() string {
return t.text.Name()
}
+// FuncMap is the type of the map defining the mapping from names to
+// functions. Each function must have either a single return value, or two
+// return values of which the second has type error. In that case, if the
+// second (error) argument evaluates to non-nil during execution, execution
+// terminates and Execute returns that error. FuncMap has the same base type
+// as template.FuncMap, copied here so clients need not import "text/template".
+type FuncMap map[string]interface{}
+
// Funcs adds the elements of the argument map to the template's function map.
// It panics if a value in the map is not a function with appropriate return
// type. However, it is legal to overwrite elements of the map. The return
// value is the template, so calls can be chained.
-func (t *Template) Funcs(funcMap template.FuncMap) *Template {
- t.text.Funcs(funcMap)
+func (t *Template) Funcs(funcMap FuncMap) *Template {
+ t.text.Funcs(template.FuncMap(funcMap))
return t
}
このファイルが変更の主要な部分です。
-
FuncMap型の定義追加:type FuncMap map[string]interface{}という行が追加され、html/templateパッケージ内で独自のFuncMap型が定義されました。この定義には、その目的とtext/template.FuncMapとの関係を説明するコメントも追加されています。 -
Funcsメソッドのシグネチャ変更:func (t *Template) Funcs(funcMap template.FuncMap) *Templateがfunc (t *Template) Funcs(funcMap FuncMap) *Templateに変更されました。これにより、Funcsメソッドがhtml/template独自のFuncMap型を引数として受け取るようになりました。 -
Funcsメソッドの実装変更:t.text.Funcs(funcMap)がt.text.Funcs(template.FuncMap(funcMap))に変更されました。これは、html/templateのFuncsメソッドが受け取ったhtml/template.FuncMap型の値を、内部で利用しているtext/templateのFuncsメソッドに渡す前に、text/template.FuncMap型に明示的に型変換していることを示しています。
コアとなるコードの解説
このコミットのコアとなる変更は、html/template/template.go における FuncMap 型のローカル定義と、それに伴う Funcs メソッドのシグネチャおよび実装の変更です。
-
type FuncMap map[string]interface{}の追加: この行は、html/templateパッケージ内にFuncMapという新しい型を定義しています。この型はmap[string]interface{}のエイリアスであり、text/template.FuncMapと同じ基底型を持ちます。これにより、html/templateはtext/templateのFuncMapを直接参照することなく、独自のFuncMap型を公開APIとして提供できるようになります。追加されたコメントは、この型の目的と、クライアントがtext/templateをインポートする必要がないことを明確に説明しています。 -
func (t *Template) Funcs(funcMap FuncMap) *Templateへのシグネチャ変更:Template型のFuncsメソッドが、text/template.FuncMapではなく、新しく定義されたhtml/template.FuncMapを引数として受け取るように変更されました。これは、html/templateの公開APIがtext/templateから独立したことを意味します。これにより、html/templateを利用する開発者は、text/templateパッケージを意識することなく、html/templateの機能を利用できるようになります。 -
t.text.Funcs(template.FuncMap(funcMap))への実装変更:html/templateのTemplateオブジェクトは、内部にtext/template.Templateオブジェクト (t.text) を持っています。html/templateのFuncsメソッドが呼び出された際、その機能は最終的に内部のtext/template.TemplateオブジェクトのFuncsメソッドに委譲されます。 この変更では、html/template.FuncMap型のfuncMap引数を、text/template.FuncMap(funcMap)という形でtext/template.FuncMap型に明示的に型変換しています。Go言語では、基底型が同じであれば、このように型変換を行うことができます。この変換により、html/templateは自身の公開APIの独立性を保ちつつ、内部的にはtext/templateの機能を引き続き利用することができます。
これらの変更により、html/template パッケージは、その公開APIにおいて text/template パッケージへの直接的な依存を解消し、よりクリーンで独立した設計を実現しています。これは、Go言語の標準ライブラリが、パッケージ間の適切な分離と疎結合を重視していることを示す良い例です。
関連リンク
- Go言語の公式ドキュメント:
text/templateパッケージとhtml/templateパッケージに関する詳細な情報が提供されています。 - Go言語のIssueトラッカー: コミットメッセージに
Fixes #2546とありますが、このIssueは公開されているGoのIssueトラッカーでは直接見つかりませんでした。これは、内部的なIssueであるか、非常に古いIssueである可能性があります。
参考にした情報源リンク
- コミット情報:
/home/orange/Project/comemo/commit_data/10793.txt - GitHubコミットページ: https://github.com/golang/go/commit/1402d1a68615d037365158578b0c4861e0fb4157
- Go言語のドキュメント (一般的なパッケージ設計と型システムに関する知識)
- Go言語のソースコード (変更されたファイルの内容)