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

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

このコミットは、Go言語の公式Wikiチュートリアルにおけるテンプレートの扱いと、チュートリアル自体の構造を改善することを目的としています。特に、Goのhtml/templateパッケージの利用方法に関する説明を更新し、テンプレートのキャッシュメカニズムをより効率的な方法に修正しています。また、チュートリアル内でコードスニペットを埋め込むための新しいメカニズムを導入し、チュートリアルのコンテンツを整理しています。

コミット

commit d98507f1c4a4dfdd77400138ff38865813d4327f
Author: Andrew Gerrand <adg@golang.org>
Date:   Tue Mar 27 16:07:46 2012 +1100

    doc: update wiki tutorial templates, and template discussion
    
    Fixes #3384.
    
    R=golang-dev, r
    CC=golang-dev
    https://golang.org/cl/5915044

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

https://github.com/golang/go/commit/d98507f1c4a4dfdd77400138ff38865813d4327f

元コミット内容

doc: update wiki tutorial templates, and template discussion Fixes #3384.

このコミットは、Go Wikiチュートリアルのテンプレートと、テンプレートに関する議論を更新するものです。Issue #3384を修正します。

変更の背景

このコミットの主な背景は、Go Wikiチュートリアル("Writing Web Applications")におけるテンプレートの取り扱いに関する説明の改善と、実際のコード実装の効率化です。

元のチュートリアルでは、html/templateパッケージを使用してHTMLテンプレートを扱う際に、ページがレンダリングされるたびにtemplate.ParseFilesを呼び出していました。これは非効率的であり、特にアクセスが多いアプリケーションではパフォーマンスの問題を引き起こす可能性があります。このコミットは、テンプレートをプログラムの初期化時に一度だけパースし、それをキャッシュして再利用する、より効率的な方法を導入することでこの問題を解決します。

また、チュートリアル自体の構造も改善されています。以前はコードスニペットが<pre>タグ内に直接記述されていましたが、この変更により{{code "path/to/file.go" /regex1/ /regex2/}}のような新しいディレクティブが導入され、外部のGoソースファイルから直接コードを埋め込めるようになりました。これにより、チュートリアルのコードと実際のソースコードの同期が容易になり、メンテナンス性が向上します。

Fixes #3384という記述から、このコミットが特定のバグや改善要求に対応していることがわかります。GoのIssue #3384は「doc/articles/wiki: update tutorial to use ExecuteTemplate」というタイトルで、html/templateExecuteTemplateメソッドを使用するようにチュートリアルを更新することを求めていました。これは、複数のテンプレートを一度にパースし、名前で個別に実行する現代的なGoのテンプレート利用パターンに合わせるための変更です。

前提知識の解説

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

html/templateパッケージは、Go言語でHTML出力を安全に生成するための機能を提供します。クロスサイトスクリプティング(XSS)攻撃を防ぐために、デフォルトでHTMLエスケープを行います。

  • template.ParseFiles(filenames ...string) (*Template, error): 指定されたファイルからテンプレートをパースし、*Templateオブジェクトを返します。複数のファイルを指定した場合、それらはすべて同じテンプレートセットの一部としてパースされます。
  • template.Must(t *Template, err error) *Template: template.ParseFilesなどの結果をラップするために使用されるヘルパー関数です。errnilでない場合、パニックを引き起こします。これは、プログラムの起動時にテンプレートのパースが失敗した場合に、早期にエラーを検出するために便利です。
  • t.Execute(wr io.Writer, data interface{}) error: パースされたテンプレートをwrに書き込み、dataをテンプレートに渡します。このメソッドは、単一のテンプレートを実行する場合に主に使用されます。
  • t.ExecuteTemplate(wr io.Writer, name string, data interface{}) error: 複数のテンプレートがパースされた*Templateセットの中から、指定されたnameのテンプレートを実行します。これにより、複数のテンプレートファイルを一度にパースし、必要に応じて特定のテンプレートを名前で呼び出すことが可能になります。

テンプレートのキャッシュ

Webアプリケーションでは、リクエストごとにテンプレートファイルをディスクから読み込み、パースするのは非常に非効率的です。そのため、アプリケーションの起動時に一度だけテンプレートをパースし、その結果をメモリにキャッシュして再利用するのが一般的なプラクティスです。これにより、各リクエストでのI/Oとパースのオーバーヘッドを削減し、アプリケーションのパフォーマンスを向上させることができます。

Goのドキュメント生成におけるコード埋め込み

Goの公式ドキュメントやチュートリアルでは、ソースコードから直接コードスニペットを埋め込むための特別なディレクティブが使用されることがあります。これにより、ドキュメント内のコード例が常に最新のソースコードと同期されるようになります。このコミットで導入された{{code ...}}ディレクティブは、このメカニズムの一部であり、指定されたファイルから正規表現にマッチするコードブロックを抽出して表示する機能を提供します。

技術的詳細

このコミットの技術的な変更は、主に以下の2点に集約されます。

  1. テンプレートのキャッシュ戦略の変更:

    • 変更前: final.goでは、templatesというmap[string]*template.Templateを作成し、init関数内でedit.htmlview.htmlを個別にパースしてマップに格納していました。renderTemplate関数では、templates[tmpl].Execute(w, p)のように、マップから適切なテンプレートを取得して実行していました。
    • 変更後: final.goでは、templates変数をvar templates = template.Must(template.ParseFiles("edit.html", "view.html"))と定義するように変更されました。これにより、edit.htmlview.htmlの両方が単一の*template.Templateオブジェクトとしてパースされ、templates変数に格納されます。renderTemplate関数はtemplates.ExecuteTemplate(w, tmpl+".html", p)を呼び出すように変更され、パースされたテンプレートセットの中から、tmpl + ".html"という名前で特定のテンプレートを実行するようになりました。
    • 利点: この変更により、テンプレートのパースがアプリケーション起動時に一度だけ行われるようになり、実行時のオーバーヘッドが大幅に削減されます。ExecuteTemplateを使用することで、複数のテンプレートを単一の*Templateインスタンスで管理し、名前で呼び出すという、より効率的で推奨されるパターンが採用されます。
  2. チュートリアルにおけるコードスニペットの埋め込み方法の変更:

    • index.htmlファイルにおいて、GoのWikiチュートリアル内でコードスニペットを表示する方法が変更されました。
    • 変更前: コードスニペットは<pre>タグ内に直接ハードコードされていました。
    • 変更後: {{code "doc/articles/wiki/part1.go" /^type Page/ /}/}}のような新しいディレクティブが導入されました。これは、指定されたファイル(例: doc/articles/wiki/part1.go)から、指定された正規表現(例: /^type Page/から/}/まで)にマッチするコードブロックを自動的に抽出して表示するためのものです。
    • 利点: このメカニズムにより、チュートリアル内のコード例が常に最新のソースコードと同期されることが保証されます。手動でのコピー&ペーストによるエラーや、ソースコードの変更に伴うチュートリアルの更新忘れを防ぎ、ドキュメントの正確性とメンテナンス性を向上させます。また、index.htmlのメタデータに"Template": trueが追加されており、これはこのファイルがテンプレートエンジンによって処理されることを示唆しています。
  3. doc/articles/wiki/wiki.htmlの削除:

    • このファイルは完全に削除されました。これは、wiki.htmlの内容がindex.htmlに統合されたか、あるいはチュートリアルの再構成に伴い不要になったことを示しています。これにより、チュートリアルのコンテンツがより一元化され、管理が容易になります。

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

doc/articles/wiki/final.go

--- a/doc/articles/wiki/final.go
+++ b/doc/articles/wiki/final.go
@@ -58,17 +58,10 @@ func saveHandler(w http.ResponseWriter, r *http.Request, title string) {\n 	http.Redirect(w, r, "/view/"+title, http.StatusFound)\n }\n 
-var templates = make(map[string]*template.Template)\n-
-func init() {\n-	for _, tmpl := range []string{"edit", "view"} {\n-		t := template.Must(template.ParseFiles(tmpl + ".html"))\n-		templates[tmpl] = t\n-	}\n-}\n+var templates = template.Must(template.ParseFiles("edit.html", "view.html"))
 
 func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {\n-	err := templates[tmpl].Execute(w, p)\n+	err := templates.ExecuteTemplate(w, tmpl+".html", p)
 	if err != nil {\n 		http.Error(w, err.Error(), http.StatusInternalServerError)\n 	}

doc/articles/wiki/index.html

--- a/doc/articles/wiki/index.html
+++ b/doc/articles/wiki/index.html
@@ -1,5 +1,6 @@
 <!--{
-	"Title": "Writing Web Applications"
+	"Title": "Writing Web Applications",
+	"Template": true
 }-->
 
 <h2>Introduction</h2>
@@ -73,12 +74,7 @@ Here, we define <code>Page</code> as a struct with two fields representing
 the title and body.
 </p>
 
-<pre>
-type Page struct {
-	Title	string
-	Body	[]byte
-}
-</pre>
+{{code "doc/articles/wiki/part1.go" `/^type Page/` `/}/`}}
 
 <p>
 The type <code>[]byte</code> means "a <code>byte</code> slice". 
@@ -95,12 +91,7 @@ But what about persistent storage? We can address that by creating a
 <code>save</code> method on <code>Page</code>:
 </p>
 
-<pre>
-func (p *Page) save() error {
-	filename := p.Title + &#34;.txt&#34;
-	return ioutil.WriteFile(filename, p.Body, 0600)
-}
-</pre>
+{{code "doc/articles/wiki/part1.go" `/^func.*Page.*save/` `/}/`}}
 
 <p>
 This method\'s signature reads: "This is a method named <code>save</code> that
@@ -134,13 +125,7 @@ read-write permissions for the current user only. (See the Unix man page
 We will want to load pages, too:
 </p>
 
-<pre>
-func loadPage(title string) *Page {
-	filename := title + &#34;.txt&#34;
-	body, _ := ioutil.ReadFile(filename)
-	return &amp;Page{Title: title, Body: body}
-}
-</pre>
+{{code "doc/articles/wiki/part1-noerror.go" `/^func loadPage/` `/^}/`}}
 
 <p>
 The function <code>loadPage</code> constructs the file name from
@@ -162,16 +147,7 @@ the file might not exist. We should not ignore such errors.  Let\'s modify the
 function to return <code>*Page</code> and <code>error</code>.
 </p>
 
-<pre>
-func loadPage(title string) (*Page, error) {
-	filename := title + &#34;.txt&#34;
-	body, err := ioutil.ReadFile(filename)
-	if err != nil {\n-		return nil, err
-	}\n-	return &amp;Page{Title: title, Body: body}, nil
-}
-</pre>
+{{code "doc/articles/wiki/part1.go" `/^func loadPage/` `/^}/`}}
 
 <p>
 Callers of this function can now check the second parameter; if it is
@@ -186,14 +162,7 @@ load from a file. Let\'s write a <code>main</code> function to test what we\'ve
 written:
 </p>
 
-<pre>
-func main() {
-	p1 := &amp;Page{Title: &#34;TestPage&#34;, Body: []byte(&#34;This is a sample Page.&#34;)}
-	p1.save()
-	p2, _ := loadPage(&#34;TestPage&#34;)
-	fmt.Println(string(p2.Body))
-}
-</pre>
+{{code "doc/articles/wiki/part1.go" `/^func main/` `/^}/`}}
 
 <p>
 After compiling and executing this code, a file named <code>TestPage.txt</code>
@@ -227,23 +196,7 @@ This is a sample page.
 Here\'s a full working example of a simple web server:
 </p>
 
-<pre>
-package main
-
-import (
-	&#34;fmt&#34;
-	&#34;net/http&#34;
-)
-
-func handler(w http.ResponseWriter, r *http.Request) {
-	fmt.Fprintf(w, &#34;Hi there, I love %s!&#34;, r.URL.Path[1:])
-}
-
-func main() {
-	http.HandleFunc(&#34;/&#34;, handler)
-	http.ListenAndServe(&#34;:8080&#34;, nil)
-}
-</pre>
+{{code "doc/articles/wiki/http-sample.go"}}
 
 <p>
 The <code>main</code> function begins with a call to 
@@ -305,15 +258,9 @@ import (
 Let\'s create a handler to view a wiki page: 
 </p>
 
-<pre>
-const lenPath = len(&#34;/view/&#34;)
+{{code "doc/articles/wiki/part2.go" `/^const lenPath/`}}
 
-func viewHandler(w http.ResponseWriter, r *http.Request) {
-	title := r.URL.Path[lenPath:]
-	p, _ := loadPage(title)
-	fmt.Fprintf(w, &#34;&lt;h1&gt;%s&lt;/h1&gt;&lt;div&gt;%s&lt;/div&gt;&#34;, p.Title, p.Body)
-}
-</pre>
+{{code "doc/articles/wiki/part2.go" `/^func viewHandler/` `/^}/`}}
 
 <p>
 First, this function extracts the page title from <code>r.URL.Path</code>,
@@ -342,12 +289,7 @@ initializes <code>http</code> using the <code>viewHandler</code> to handle
 any requests under the path <code>/view/</code>.
 </p>
 
-<pre>
-func main() {
-	http.HandleFunc(&#34;/view/&#34;, viewHandler)
-	http.ListenAndServe(&#34;:8080&#34;, nil)
-}
-</pre>
+{{code "doc/articles/wiki/part2.go" `/^func main/` `/^}/`}}
 
 <p>
 <a href=\"part2.go\">Click here to view the code we\'ve written so far.</a>
@@ -387,14 +329,7 @@ form.
 First, we add them to <code>main()</code>: 
 </p>
 
-<pre>
-func main() {
-	http.HandleFunc(&#34;/view/&#34;, viewHandler)
-	http.HandleFunc(&#34;/edit/&#34;, editHandler)
-	http.HandleFunc(&#34;/save/&#34;, saveHandler)
-	http.ListenAndServe(&#34;:8080&#34;, nil)
-}
-</pre>
+{{code "doc/articles/wiki/final-noclosure.go" `/^func main/` `/^}/`}}
 
 <p>
 The function <code>editHandler</code> loads the page 
@@ -402,21 +337,7 @@ and displays an HTML form.
 </p>
 
-<pre>
-func editHandler(w http.ResponseWriter, r *http.Request) {
-	title := r.URL.Path[lenPath:]
-	p, err := loadPage(title)
-	if err != nil {
-		p = &amp;Page{Title: title}
-	}
-	fmt.Fprintf(w, &#34;&lt;h1&gt;Editing %s&lt;/h1&gt;&#34;+\n-		&#34;&lt;form action=\\&#34;/save/%s\\&#34; method=\\&#34;POST\\&#34;&gt;&#34;+\n-		&#34;&lt;textarea name=\\&#34;body\\&#34;&gt;%s&lt;/textarea&gt;&lt;br&gt;&#34;+\n-		&#34;&lt;input type=\\&#34;submit\\&#34; value=\\&#34;Save\\&#34;&gt;&#34;+\n-		&#34;&lt;/form&gt;&#34;,\n-		p.Title, p.Title, p.Body)
-}
-</pre>
+{{code "doc/articles/wiki/notemplate.go" `/^func editHandler/` `/^}/`}}
 
 <p>
 This function will work fine, but all that hard-coded HTML is ugly.
@@ -450,31 +371,14 @@ Let\'s create a template file containing the HTML form.
 Open a new file named <code>edit.html</code>, and add the following lines:
 </p>
 
-<pre>
-&lt;h1&gt;Editing {{.Title |html}}&lt;/h1&gt;\n-\n-&lt;form action=&#34;/save/{{.Title |html}}&#34; method=&#34;POST&#34;&gt;\n-&lt;div&gt;&lt;textarea name=&#34;body&#34; rows=&#34;20&#34; cols=&#34;80&#34;&gt;{{printf &#34;%s&#34; .Body |html}}&lt;/textarea&gt;&lt;/div&gt;\n-&lt;div&gt;&lt;input type=&#34;submit&#34; value=&#34;Save&#34;&gt;&lt;/div&gt;\n-&lt;/form&gt;\n-</pre>
+{{code "doc/articles/wiki/edit.html"}}
 
 <p>
 Modify <code>editHandler</code> to use the template, instead of the hard-coded
 HTML:
 </p>
 
-<pre>
-func editHandler(w http.ResponseWriter, r *http.Request) {
-	title := r.URL.Path[lenPath:]
-	p, err := loadPage(title)
-	if err != nil {
-		p = &amp;Page{Title: title}
-	}
-	t, _ := template.ParseFiles(&#34;edit.html&#34;)
-	t.Execute(w, p)
-}
-</pre>
+{{code "doc/articles/wiki/final-noerror.go" `/^func editHandler/` `/^}/`}}
 
 <p>
 The function <code>template.ParseFiles</code> will read the contents of 
@@ -509,26 +413,13 @@ While we\'re working with templates, let\'s create a template for our
 <code>viewHandler</code> called <code>view.html</code>:
 </p>
 
-<pre>
-&lt;h1&gt;{{.Title |html}}&lt;/h1&gt;\n-\n-&lt;p&gt;[&lt;a href=&#34;/edit/{{.Title |html}}&#34;&gt;edit&lt;/a&gt;]&lt;/p&gt;\n-\n-&lt;div&gt;{{printf &#34;%s&#34; .Body |html}}&lt;/div&gt;\n-</pre>
+{{code "doc/articles/wiki/view.html"}}
 
 <p>
 Modify <code>viewHandler</code> accordingly:
 </p>
 
-<pre>
-func viewHandler(w http.ResponseWriter, r *http.Request) {
-	title := r.URL.Path[lenPath:]
-	p, _ := loadPage(title)
-	t, _ := template.ParseFiles(&#34;view.html&#34;)
-	t.Execute(w, p)
-}
-</pre>
+{{code "doc/articles/wiki/final-noerror.go" `/^func viewHandler/` `/^}/`}}
 
 <p>
 Notice that we\'ve used almost exactly the same templating code in both
@@ -536,27 +427,9 @@ handlers. Let\'s remove this duplication by moving the templating code
 to its own function:
 </p>
 
-<pre>
-func viewHandler(w http.ResponseWriter, r *http.Request) {
-	title := r.URL.Path[lenPath:]
-	p, _ := loadPage(title)
-	renderTemplate(w, &#34;view&#34;, p)
-}
-
-func editHandler(w http.ResponseWriter, r *http.Request) {
-	title := r.URL.Path[lenPath:]
-	p, err := loadPage(title)
-	if err != nil {
-		p = &amp;Page{Title: title}
-	}
-	renderTemplate(w, &#34;edit&#34;, p)
-}
-
-func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
-	t, _ := template.ParseFiles(tmpl + &#34;.html&#34;)
-	t.Execute(w, p)
-}
-</pre>
+{{code "doc/articles/wiki/final-template.go" `/^func viewHandler/` `/^}/`}}
+{{code "doc/articles/wiki/final-template.go" `/^func editHandler/` `/^}/`}}
+{{code "doc/articles/wiki/final-template.go" `/^func renderTemplate/` `/^}/`}}
 
 <p>
 The handlers are now shorter and simpler. 
@@ -572,20 +445,7 @@ if the requested Page doesn\'t exist, it should redirect the client to the edit
 Page so the content may be created:
 </p>
 
-<pre>
-func viewHandler(w http.ResponseWriter, r *http.Request) {
-	title, err := getTitle(w, r)
-	if err != nil {
-		return
-	}
-	p, err := loadPage(title)
-	if err != nil {
-		http.Redirect(w, r, &#34;/edit/&#34;+title, http.StatusFound)
-		return
-	}
-	renderTemplate(w, &#34;view&#34;, p)
-}
-</pre>
+{{code "doc/articles/wiki/final-noclosure.go" `/^func viewHandler/` `/^}/`}}
 
 <p>
 The <code>http.Redirect</code> function adds an HTTP status code of 
@@ -599,15 +459,7 @@ header to the HTTP response.
 The function <code>saveHandler</code> will handle the form submission. 
 </p>
 
-<pre>
-func saveHandler(w http.ResponseWriter, r *http.Request) {
-	title := r.URL.Path[lenPath:]
-	body := r.FormValue(&#34;body&#34;)
-	p := &amp;Page{Title: title, Body: []byte(body)}
-	p.save()
-	http.Redirect(w, r, &#34;/view/&#34;+title, http.StatusFound)
-}
-</pre>
+{{code "doc/articles/wiki/final-template.go" `/^func saveHandler/` `/^}/`}}
 
 <p>
 The page title (provided in the URL) and the form\'s only field, 
@@ -637,19 +489,7 @@ function and the user will be notified.
 First, let\'s handle the errors in <code>renderTemplate</code>:
 </p>
 
-<pre>
-func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
-	t, err := template.ParseFiles(tmpl + &#34;.html&#34;)
-	if err != nil {
-		http.Error(w, err.Error(), http.StatusInternalServerError)
-		return
-	}
-	err = t.Execute(w, p)
-	if err != nil {
-		http.Error(w, err.Error(), http.StatusInternalServerError)
-	}\n-}
-</pre>
+{{code "doc/articles/wiki/final-parsetemplate.go" `/^func renderTemplate/` `/^}/`}}
 
 <p>
 The <code>http.Error</code> function sends a specified HTTP response code 
@@ -661,22 +501,7 @@ Already the decision to put this in a separate function is paying off.
 Now let\'s fix up <code>saveHandler</code>:
 </p>
 
-<pre>
-func saveHandler(w http.ResponseWriter, r *http.Request) {
-	title, err := getTitle(w, r)
-	if err != nil {
-		return
-	}
-	body := r.FormValue(&#34;body&#34;)
-	p := &amp;Page{Title: title, Body: []byte(body)}
-	err = p.save()
-	if err != nil {
-		http.Error(w, err.Error(), http.StatusInternalServerError)
-		return
-	}
-	http.Redirect(w, r, &#34;/view/&#34;+title, http.StatusFound)
-}
-</pre>
+{{code "doc/articles/wiki/final-noclosure.go" `/^func saveHandler/` `/^}/`}}
 
 <p>
 Any errors that occur during <code>p.save()</code> will be reported 
@@ -687,40 +512,28 @@ to the user.
 
 <p>
 There is an inefficiency in this code: <code>renderTemplate</code> calls 
-<code>ParseFile</code> every time a page is rendered. 
-A better approach would be to call <code>ParseFile</code> once for each 
-template at program initialization, and store the resultant 
-<code>*Template</code> values in a data structure for later use.\n+<code>ParseFiles</code> every time a page is rendered. 
+A better approach would be to call <code>ParseFiles</code> once at program
+initialization, parsing all templates into a single <code>*Template</code>.
+Then we can use the
+<a href=\"/pkg/html/template/#Template.ExecuteTemplate\"><code>ExecuteTemplate</code></a>
+method to render a specific template.
 </p>
 
 <p>
-First we create a global map named <code>templates</code> in which to store 
-our <code>*Template</code> values, keyed by <code>string</code> 
-(the template name):\n+First we create a global variable named <code>templates</code>, and initialize
+it with <code>ParseFiles</code>.
 </p>
 
-<pre>
-var templates = make(map[string]*template.Template)\n-</pre>
+{{code "doc/articles/wiki/final.go" `/var templates/`}}
 
 <p>
-Then we create an <code>init</code> function, which will be called before
-<code>main</code> at program initialization. The function
-<code>template.Must</code> is a convenience wrapper that panics when passed a
-non-nil <code>error</code> value, and otherwise returns the
+The function <code>template.Must</code> is a convenience wrapper that panics
+when passed a non-nil <code>error</code> value, and otherwise returns the
 <code>*Template</code> unaltered. A panic is appropriate here; if the templates
 can\'t be loaded the only sensible thing to do is exit the program.
 </p>
 
-<pre>
-func init() {
-	for _, tmpl := range []string{&#34;edit&#34;, &#34;view&#34;} {\n-		t := template.Must(template.ParseFiles(tmpl + &#34;.html&#34;))\n-		templates[tmpl] = t
-	}\n-}
-</pre>
-\n <p>
 A <code>for</code> loop is used with a <code>range</code> statement to iterate 
 over an array constant containing the names of the templates we want parsed.
@@ -729,18 +542,17 @@ that array.
 </p>
 
 <p>
-We then modify our <code>renderTemplate</code> function to call 
-the <code>Execute</code> method on the appropriate <code>Template</code> from 
-<code>templates</code>:\n+We then modify the <code>renderTemplate</code> function to call the
+<code>templates.ExecuteTemplate</code> method with the name of the appropriate
+template:
+</p>
 
-<pre>
-func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
-	err := templates[tmpl].Execute(w, p)
-	if err != nil {
-		http.Error(w, err.Error(), http.StatusInternalServerError)
-	}
-}
-</pre>
+{{code "doc/articles/wiki/final.go" `/func renderTemplate/` `/^}/`}}
+\n+<p>
+Note that the template name is the template file name, so we must
+append <code>\".html\"</code> to the <code>tmpl</code> argument.
+</p>
 
 <h2>Validation</h2>
 
@@ -755,9 +567,7 @@ First, add <code>\"regexp\"</code> to the <code>import</code> list.
 Then we can create a global variable to store our validation regexp:
 </p>
 
-<pre>
-var titleValidator = regexp.MustCompile(&#34;^[a-zA-Z0-9]+$&#34;)\n-</pre>
+{{code "doc/articles/wiki/final-noclosure.go" `/^var titleValidator/`}}
 
 <p>
 The function <code>regexp.MustCompile</code> will parse and compile the 
@@ -772,16 +582,7 @@ Now, let\'s write a function that extracts the title string from the request
 URL, and tests it against our <code>TitleValidator</code> expression:
 </p>
 
-<pre>
-func getTitle(w http.ResponseWriter, r *http.Request) (title string, err error) {
-	title = r.URL.Path[lenPath:]
-	if !titleValidator.MatchString(title) {
-		http.NotFound(w, r)
-		err = errors.New(&#34;Invalid Page Title&#34;)
-	}
-	return
-}
-</pre>
+{{code "doc/articles/wiki/final-noclosure.go" `/func getTitle/` `/^}/`}}
 
 <p>
 If the title is valid, it will be returned along with a <code>nil</code>
@@ -794,47 +595,9 @@ handler.
 Let\'s put a call to <code>getTitle</code> in each of the handlers:
 </p>
 
-<pre>
-func viewHandler(w http.ResponseWriter, r *http.Request) {
-	title, err := getTitle(w, r)
-	if err != nil {
-		return
-	}
-	p, err := loadPage(title)
-	if err != nil {
-		http.Redirect(w, r, &#34;/edit/&#34;+title, http.StatusFound)
-		return
-	}
-	renderTemplate(w, &#34;view&#34;, p)
-}
-
-func editHandler(w http.ResponseWriter, r *http.Request) {
-	title, err := getTitle(w, r)
-	if err != nil {
-		return
-	}
-	p, err := loadPage(title)
-	if err != nil {
-		p = &amp;Page{Title: title}
-	}
-	renderTemplate(w, &#34;edit&#34;, p)
-}
-
-func saveHandler(w http.ResponseWriter, r *http.Request) {
-	title, err := getTitle(w, r)
-	if err != nil {
-		return
-	}
-	body := r.FormValue(&#34;body&#34;)
-	p := &amp;Page{Title: title, Body: []byte(body)}
-	err = p.save()
-	if err != nil {
-		http.Error(w, err.Error(), http.StatusInternalServerError)
-		return
-	}
-	http.Redirect(w, r, &#34;/view/&#34;+title, http.StatusFound)
-}
-</pre>
+{{code "doc/articles/wiki/final-noclosure.go" `/^func viewHandler/` `/^}/`}}
+{{code "doc/articles/wiki/final-noclosure.go" `/^func editHandler/` `/^}/`}}
+{{code "doc/articles/wiki/final-noclosure.go" `/^func saveHandler/` `/^}/`}}
 
 <h2>Introducing Function Literals and Closures</h2>
 
@@ -885,18 +648,7 @@ Now we can take the code from <code>getTitle</code> and use it here
 (with some minor modifications):
 </p>
 
-<pre>
-func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
-	return func(w http.ResponseWriter, r *http.Request) {
-		// Here we will extract the page title from the Request,
-		// and call the provided handler &#39;fn&#39;
-	}
-}
-</pre>
+{{code "doc/articles/wiki/final.go" `/func makeHandler/` `/^}/`}}
 
 <p>
 The closure returned by <code>makeHandler</code> is a function that takes
@@ -917,49 +669,16 @@ Now we can wrap the handler functions with <code>makeHandler</code> in
 package:
 </p>
 
-<pre>
-func main() {
-	http.HandleFunc(&#34;/view/&#34;, makeHandler(viewHandler))\n-	http.HandleFunc(&#34;/edit/&#34;, makeHandler(editHandler))\n-	http.HandleFunc(&#34;/save/&#34;, makeHandler(saveHandler))\n-	http.ListenAndServe(&#34;:8080&#34;, nil)
-}
-</pre>
+{{code "doc/articles/wiki/final.go" `/func main/` `/^}/`}}
 
 <p>
 Finally we remove the calls to <code>getTitle</code> from the handler functions,\n making them much simpler:
 </p>
 
-<pre>
-func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
-	p, err := loadPage(title)
-	if err != nil {
-		http.Redirect(w, r, &#34;/edit/&#34;+title, http.StatusFound)
-		return
-	}
-	renderTemplate(w, &#34;view&#34;, p)
-}
-
-func editHandler(w http.ResponseWriter, r *http.Request, title string) {
-	p, err := loadPage(title)
-	if err != nil {
-		p = &amp;Page{Title: title}
-	}
-	renderTemplate(w, &#34;edit&#34;, p)
-}
-
-func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
-	body := r.FormValue(&#34;body&#34;)
-	p := &amp;Page{Title: title, Body: []byte(body)}
-	err := p.save()
-	if err != nil {
-		http.Error(w, err.Error(), http.StatusInternalServerError)
-		return
-	}
-	http.Redirect(w, r, &#34;/view/&#34;+title, http.StatusFound)
-}
-</pre>
+{{code "doc/articles/wiki/final.go" `/^func viewHandler/` `/^}/`}}
+{{code "doc/articles/wiki/final.go" `/^func editHandler/` `/^}/`}}
+{{code "doc/articles/wiki/final.go" `/^func saveHandler/` `/^}/`}}
 
 <h2>Try it out!</h2>
 
diff --git a/doc/articles/wiki/wiki.html b/doc/articles/wiki/wiki.html
deleted file mode 100644
index ef5d902c6c..0000000000
--- a/doc/articles/wiki/wiki.html
+++ /dev/null
@@ -1,779 +0,0 @@
-<!--{
-	"Title": "Writing Web Applications"
-}-->
-
-<h2>Introduction</h2>
-
-<p>
-Covered in this tutorial:
-</p>
-<ul>
-<li>Creating a data structure with load and save methods</li>
-<li>Using the <code>net/http</code> package to build web applications
-<li>Using the <code>html/template</code> package to process HTML templates</li>
-<li>Using the <code>regexp</code> package to validate user input</li>
-<li>Using closures</li>
-</ul>
-
-<p>
-Assumed knowledge:
-</p>
-<ul>
-<li>Programming experience</li>
-<li>Understanding of basic web technologies (HTTP, HTML)</li>
-<li>Some UNIX/DOS command-line knowledge</li>
-</ul>
-
-<h2>Getting Started</h2>
-
-<p>
-At present, you need to have a FreeBSD, Linux, OS X, or Windows machine to run Go.
-We will use <code>$</code> to represent the command prompt.
-</p>
-
-<p>
-Install Go (see the <a href="/doc/install">Installation Instructions</a>).
-</p>
-
-<p>
-Make a new directory for this tutorial inside your <code>GOPATH</code> and cd to it:
-</p>
-
-<pre>
-$ mkdir gowiki
-$ cd gowiki
-</pre>
-
-<p>
-Create a file named <code>wiki.go</code>, open it in your favorite editor, and 
-add the following lines:
-</p>
-
-<pre>
-package main
-
-import (
-	"fmt"
-	"io/ioutil"
-)
-</pre>
-
-<p>
-We import the <code>fmt</code> and <code>ioutil</code> packages from the Go 
-standard library. Later, as we implement additional functionality, we will 
-add more packages to this <code>import</code> declaration.
-</p>
-
-<h2>Data Structures</h2>
-
-<p>
-Let\'s start by defining the data structures. A wiki consists of a series of
-interconnected pages, each of which has a title and a body (the page content).
-Here, we define <code>Page</code> as a struct with two fields representing
-the title and body.
-</p>
-
-<pre>
-!srcextract.bin -src=part1.go -name=Page
-</pre>
-
-<p>
-The type <code>[]byte</code> means "a <code>byte</code> slice". 
-(See <a href="/doc/articles/slices_usage_and_internals.html">Slices: usage and
-internals</a> for more on slices.)
-The <code>Body</code> element is a <code>[]byte</code> rather than
-<code>string</code> because that is the type expected by the <code>io</code>
-libraries we will use, as you\'ll see below.
-</p>
-
-<p>
-The <code>Page</code> struct describes how page data will be stored in memory. 
-But what about persistent storage? We can address that by creating a 
-<code>save</code> method on <code>Page</code>:
-</p>
-
-<pre>
-!srcextract.bin -src=part1.go -name=save
-</pre>
-
-<p>
-This method\'s signature reads: "This is a method named <code>save</code> that
-takes as its receiver <code>p</code>, a pointer to <code>Page</code> . It takes
-no parameters, and returns a value of type <code>error</code>." 
-</p>
-
-<p>
-This method will save the <code>Page</code>\'s <code>Body</code> to a text 
-file. For simplicity, we will use the <code>Title</code> as the file name.
-</p>
-
-<p>
-The <code>save</code> method returns an <code>error</code> value because
-that is the return type of <code>WriteFile</code> (a standard library function
-that writes a byte slice to a file).  The <code>save</code> method returns the
-error value, to let the application handle it should anything go wrong while
-writing the file.  If all goes well, <code>Page.save()</code> will return
-<code>nil</code> (the zero-value for pointers, interfaces, and some other 
-types).
-</p>
-
-<p>
-The octal integer constant <code>0600</code>, passed as the third parameter to
-<code>WriteFile</code>, indicates that the file should be created with
-read-write permissions for the current user only. (See the Unix man page
-<code>open(2)</code> for details.)
-</p>
-
-<p>
-We will want to load pages, too:
-</p>
-
-<pre>
-!srcextract.bin -src=part1-noerror.go -name=loadPage
-</pre>
-
-<p>
-The function <code>loadPage</code> constructs the file name from
-<code>Title</code>, reads the file\'s contents into a new
-<code>Page</code>, and returns a pointer to that new <code>page</code>.
-</p>
-
-<p>
-Functions can return multiple values. The standard library function 
-<code>io.ReadFile</code> returns <code>[]byte</code> and <code>error</code>. 
-In <code>loadPage</code>, error isn\'t being handled yet; the "blank identifier"
-represented by the underscore (<code>_</code>) symbol is used to throw away the
-error return value (in essence, assigning the value to nothing). 
-</p>
-
-<p>
-But what happens if <code>ReadFile</code> encounters an error?  For example,\n-the file might not exist. We should not ignore such errors.  Let\'s modify the
-function to return <code>*Page</code> and <code>error</code>.
-</p>
-
-<pre>
-!srcextract.bin -src=part1.go -name=loadPage
-</pre>
-
-<p>
-Callers of this function can now check the second parameter; if it is
-<code>nil</code> then it has successfully loaded a Page. If not, it will be an
-<code>error</code> that can be handled by the caller (see the 
-<a href="/ref/spec#Errors">language specification</a> for details).\n-</p>
-
-<p>
-At this point we have a simple data structure and the ability to save to and
-load from a file. Let\'s write a <code>main</code> function to test what we\'ve
-written:
-</p>
-
-<pre>
-!srcextract.bin -src=part1.go -name=main
-</pre>
-
-<p>
-After compiling and executing this code, a file named <code>TestPage.txt</code>
-would be created, containing the contents of <code>p1</code>. The file would
-then be read into the struct <code>p2</code>, and its <code>Body</code> element
-printed to the screen.
-</p>
-
-<p>
-You can compile and run the program like this: 
-</p>
-
-<pre>
-$ go build wiki.go
-$ ./wiki
-This is a sample page.
-</pre>
-
-<p>
-(If you\'re using Windows you must type "<code>wiki</code>" without the 
-"<code>./</code>" to run the program.)
-</p>
-
-<p>
-<a href="part1.go">Click here to view the code we\'ve written so far.</a>
-</p>
-
-<h2>Introducing the <code>net/http</code> package (an interlude)</h2>
-
-<p>
-Here\'s a full working example of a simple web server:
-</p>
-
-<pre>
-!htmlify.bin < http-sample.go
-</pre>
-
-<p>
-The <code>main</code> function begins with a call to 
-<code>http.HandleFunc</code>, which tells the <code>http</code> package to 
-handle all requests to the web root (<code>"/"</code>) with 
-<code>handler</code>. 
-</p>
-
-<p>
-It then calls <code>http.ListenAndServe</code>, specifying that it should
-listen on port 8080 on any interface (<code>":8080"</code>). (Don\'t
-worry about its second parameter, <code>nil</code>, for now.)
-This function will block until the program is terminated.
-</p>
-
-<p>
-The function <code>handler</code> is of the type <code>http.HandlerFunc</code>.
-It takes an <code>http.ResponseWriter</code> and an <code>http.Request</code> as
-its arguments.
-</p>
-
-<p>
-An <code>http.ResponseWriter</code> value assembles the HTTP server\'s response; by writing 
-to it, we send data to the HTTP client.
-</p>
-
-<p>
-An <code>http.Request</code> is a data structure that represents the client
-HTTP request.  The string <code>r.URL.Path</code> is the path component
-of the request URL.  The trailing <code>[1:]</code> means
-"create a sub-slice of <code>Path</code> from the 1st character to the end." 
-This drops the leading "/" from the path name.
-</p>
-
-<p>
-If you run this program and access the URL: 
-</p>
-<pre>http://localhost:8080/monkeys</pre>
-<p>
-the program would present a page containing:
-</p>
-<pre>Hi there, I love monkeys!</pre>
-
-<h2>Using <code>net/http</code> to serve wiki pages</h2>
-
-<p>
-To use the <code>net/http</code> package, it must be imported:
-</p>
-
-<pre>
-import (
-	"fmt"
-	<b>"net/http"</b>
-	"io/ioutil"
-)
-</pre>
-
-<p>
-Let\'s create a handler to view a wiki page: 
-</p>
-
-<pre>
-!srcextract.bin -src=part2.go -name=lenPath
-
-!srcextract.bin -src=part2.go -name=viewHandler
-</pre>
-
-<p>
-First, this function extracts the page title from <code>r.URL.Path</code>,
-the path component of the request URL. The global constant 
-<code>lenPath</code> is the length of the leading <code>"/view/"</code>
-component of the request path.
-The <code>Path</code> is re-sliced with <code>[lenPath:]</code> to drop the 
-first 6 characters of the string. This is because the path will invariably 
-begin with <code>"/view/"</code>, which is not part of the page title.
-</p>
-
-<p>
-The function then loads the page data, formats the page with a string of simple 
-HTML, and writes it to <code>w</code>, the <code>http.ResponseWriter</code>. 
-</p>
-
-<p>
-Again, note the use of <code>_</code> to ignore the <code>error</code> 
-return value from <code>loadPage</code>. This is done here for simplicity
-and generally considered bad practice. We will attend to this later.
-</p>
-
-<p>
-To use this handler, we create a <code>main</code> function that
-initializes <code>http</code> using the <code>viewHandler</code> to handle
-any requests under the path <code>/view/</code>.
-</p>
-
-<pre>
-!srcextract.bin -src=part2.go -name=main
-</pre>
-
-<p>
-<a href="part2.go">Click here to view the code we\'ve written so far.</a>
-</p>
-
-<p>
-Let\'s create some page data (as <code>test.txt</code>), compile our code, and
-try serving a wiki page.
-</p>
-
-<p>
-Open <code>test.txt</code> file in your editor, and save the string "Hello world" (without quotes)
-in it.
-</p>
-
-<pre>
-$ go build wiki.go
-$ ./wiki
-</pre>
-
-<p>
-With this web server running, a visit to <code><a
-href="http://localhost:8080/view/test">http://localhost:8080/view/test</a></code>
-should show a page titled "test" containing the words "Hello world".
-</p>
-
-<h2>Editing Pages</h2>
-
-<p>
-A wiki is not a wiki without the ability to edit pages. Let\'s create two new
-handlers: one named <code>editHandler</code> to display an \'edit page\' form,
-and the other named <code>saveHandler</code> to save the data entered via the
-form.
-</p>
-
-<p>
-First, we add them to <code>main()</code>: 
-</p>
-
-<pre>
-!srcextract.bin -src=final-noclosure.go -name=main
-</pre>
-
-<p>
-The function <code>editHandler</code> loads the page 
-(or, if it doesn\'t exist, create an empty <code>Page</code> struct), 
-and displays an HTML form.
-</p>
-
-<pre>
-!srcextract.bin -src=notemplate.go -name=editHandler
-</pre>
-
-<p>
-This function will work fine, but all that hard-coded HTML is ugly.
-Of course, there is a better way.
-</p>
- 
-<h2>The <code>html/template</code> package</h2>
-
-<p>
-The <code>html/template</code> package is part of the Go standard library.
-We can use <code>html/template</code> to keep the HTML in a separate file,
-allowing us to change the layout of our edit page without modifying the
-underlying Go code.
-</p>
-
-<p>
-First, we must add <code>html/template</code> to the list of imports:
-</p>
-
-<pre>
-import (
-	"http"
-	"io/ioutil"
-	"os"
-	<b>"html/template"</b>
-)
-</pre>
-
-<p>
-Let\'s create a template file containing the HTML form. 
-Open a new file named <code>edit.html</code>, and add the following lines:
-</p>
-
-<pre>
-!htmlify.bin < edit.html
-</pre>
-
-<p>
-Modify <code>editHandler</code> to use the template, instead of the hard-coded
-HTML:
-</p>
-
-<pre>
-!srcextract.bin -src=final-noerror.go -name=editHandler
-</pre>
-
-<p>
-The function <code>template.ParseFiles</code> will read the contents of 
-<code>edit.html</code> and return a <code>*template.Template</code>. 
-</p>
-
-<p>
-The method <code>t.Execute</code> executes the template, writing the
-generated HTML to the <code>http.ResponseWriter</code>.
-The <code>.Title</code> and <code>.Body</code> dotted identifiers refer to
-<code>p.Title</code> and <code>p.Body</code>.
-</p>
-
-<p>
-Template directives are enclosed in double curly braces.
-The <code>printf "%s" .Body</code> instruction is a function call
-that outputs <code>.Body</code> as a string instead of a stream of bytes,
-the same as a call to <code>fmt.Printf</code>.
-The <code>|html</code> part of each directive pipes the value through the
-<code>html</code> formatter before outputting it, which escapes HTML
-characters (such as replacing <code>></code> with <code>&gt;</code>),
-preventing user data from corrupting the form HTML. 
-</p>
-
-<p>
-Now that we\'ve removed the <code>fmt.Fprintf</code> statement, we can remove
-<code>"fmt"</code> from the <code>import</code> list.
-</p>
-
-<p>
-While we\'re working with templates, let\'s create a template for our
-<code>viewHandler</code> called <code>view.html</code>:
-</p>
-
-<pre>
-!htmlify.bin < view.html
-</pre>
-
-<p>
-Modify <code>viewHandler</code> accordingly:
-</p>
-
-<pre>
-!srcextract.bin -src=final-noerror.go -name=viewHandler
-</pre>
-
-<p>
-Notice that we\'ve used almost exactly the same templating code in both
-handlers. Let\'s remove this duplication by moving the templating code
-to its own function:
-</p>
-
-<pre>
-!srcextract.bin -src=final-template.go -name=viewHandler
-
-!srcextract.bin -src=final-template.go -name=editHandler
-
-!srcextract.bin -src=final-template.go -name=renderTemplate
-</pre>
-
-<p>
-The handlers are now shorter and simpler. 
-</p>
-
-<h2>Handling non-existent pages</h2>
-
-<p>
-What if you visit <a href="http://localhost:8080/view/APageThatDoesntExist">
-<code>/view/APageThatDoesntExist</code></a>? The program will crash. This is 
-because it ignores the error return value from <code>loadPage</code>. Instead,
-if the requested Page doesn\'t exist, it should redirect the client to the edit
-Page so the content may be created:
-</p>
-
-<pre>
-!srcextract.bin -src=final-noclosure.go -name=viewHandler
-</pre>
-
-<p>
-The <code>http.Redirect</code> function adds an HTTP status code of 
-<code>http.StatusFound</code> (302) and a <code>Location</code>
-header to the HTTP response.
-</p>
-
-<h2>Saving Pages</h2>
-
-<p>
-The function <code>saveHandler</code> will handle the form submission. 
-</p>
-
-<pre>
-!srcextract.bin -src=final-template.go -name=saveHandler
-</pre>
-
-<p>
-The page title (provided in the URL) and the form\'s only field, 
-<code>Body</code>, are stored in a new <code>Page</code>. 
-The <code>save()</code> method is then called to write the data to a file,
-and the client is redirected to the <code>/view/</code> page.
-</p>
-
-<p>
-The value returned by <code>FormValue</code> is of type <code>string</code>.
-We must convert that value to <code>[]byte</code> before it will fit into 
-the <code>Page</code> struct.  We use <code>[]byte(body)</code> to perform
-the conversion.
-</p>
-
-<h2>Error handling</h2>
-
-<p>
-There are several places in our program where errors are being ignored.  This
-is bad practice, not least because when an error does occur the program will
-crash.  A better solution is to handle the errors and return an error message
-to the user. That way if something does go wrong, the server will continue to
-function and the user will be notified.
-</p>
-
-<p>
-First, let\'s handle the errors in <code>renderTemplate</code>:
-</p>
-
-<pre>
-!srcextract.bin -src=final-parsetemplate.go -name=renderTemplate
-</pre>
-
-<p>
-The <code>http.Error</code> function sends a specified HTTP response code 
-(in this case "Internal Server Error") and error message.
-Already the decision to put this in a separate function is paying off.
-</p>
-
-<p>
-Now let\'s fix up <code>saveHandler</code>:
-</p>
-
-<pre>
-!srcextract.bin -src=final-noclosure.go -name=saveHandler
-</pre>
-
-<p>
-Any errors that occur during <code>p.save()</code> will be reported 
-to the user.
-</p>
-
-<h2>Template caching</h2>
-
-<p>
-There is an inefficiency in this code: <code>renderTemplate</code> calls 
-<code>ParseFiles</code> every time a page is rendered. 
-A better approach would be to call <code>ParseFiles</code> once for each 
-template at program initialization, and store the resultant 
-<code>*Template</code> values in a data structure for later use.
-</p>
-
-<p>
-First we create a global map named <code>templates</code> in which to store 
-our <code>*Template</code> values, keyed by <code>string</code> 
-(the template name):
-</p>
-
-<pre>
-!srcextract.bin -src=final.go -name=templates
-</pre>
-
-<p>
-Then we create an <code>init</code> function, which will be called before
-<code>main</code> at program initialization. The function
-<code>template.Must</code> is a convenience wrapper that panics when passed a
-non-nil <code>error</code> value, and otherwise returns the
-<code>*Template</code> unaltered. A panic is appropriate here; if the templates
-can\'t be loaded the only sensible thing to do is exit the program.
-</p>
-
-<pre>
-!srcextract.bin -src=final.go -name=init
-</pre>
-
-<p>
-A <code>for</code> loop is used with a <code>range</code> statement to iterate 
-over an array constant containing the names of the templates we want parsed.
-If we were to add more templates to our program, we would add their names to 
-that array.
-</p>
-
-<p>
-We then modify our <code>renderTemplate</code> function to call 
-the <code>Execute</code> method on the appropriate <code>Template</code> from 
-<code>templates</code>:
-
-<pre>
-!srcextract.bin -src=final.go -name=renderTemplate
-</pre>
-
-<h2>Validation</h2>
-
-<p>
-As you may have observed, this program has a serious security flaw: a user
-can supply an arbitrary path to be read/written on the server. To mitigate
-this, we can write a function to validate the title with a regular expression.
-</p>
-
-<p>
-First, add <code>"regexp"</code> to the <code>import</code> list.
-Then we can create a global variable to store our validation regexp:
-</p>
-
-<pre>
-!srcextract.bin -src=final-noclosure.go -name=titleValidator
-</pre>
-
-<p>
-The function <code>regexp.MustCompile</code> will parse and compile the 
-regular expression, and return a <code>regexp.Regexp</code>. 
-<code>MustCompile</code> is distinct from <code>Compile</code> in that it will
-panic if the expression compilation fails, while <code>Compile</code> returns
-an <code>error</code> as a second parameter. 
-</p>
-
-<p>
-Now, let\'s write a function that extracts the title string from the request 
-URL, and tests it against our <code>TitleValidator</code> expression:
-</p>
-
-<pre>
-!srcextract.bin -src=final-noclosure.go -name=getTitle
-</pre>
-
-<p>
-If the title is valid, it will be returned along with a <code>nil</code>
-error value.  If the title is invalid, the function will write a 
-"404 Not Found" error to the HTTP connection, and return an error to the 
-handler. 
-</p>
-
-<p>
-Let\'s put a call to <code>getTitle</code> in each of the handlers:
-</p>
-
-<pre>
-!srcextract.bin -src=final-noclosure.go -name=viewHandler
-
-!srcextract.bin -src=final-noclosure.go -name=editHandler
-
-!srcextract.bin -src=final-noclosure.go -name=saveHandler
-</pre>
-
-<h2>Introducing Function Literals and Closures</h2>
-
-<p>
-Catching the error condition in each handler introduces a lot of repeated code.
-What if we could wrap each of the handlers in a function that does this 
-validation and error checking? Go\'s 
-<a href="/ref/spec#Function_declarations">function 
-literals</a> provide a powerful means of abstracting functionality 
-that can help us here.
-</p>
-
-<p>
-First, we re-write the function definition of each of the handlers to accept
-a title string:
-</p>
-
-<pre>
-func viewHandler(w http.ResponseWriter, r *http.Request, title string)
-func editHandler(w http.ResponseWriter, r *http.Request, title string)
-func saveHandler(w http.ResponseWriter, r *http.Request, title string)
-</pre>
-
-<p>
-Now let\'s define a wrapper function that <i>takes a function of the above
-type</i>, and returns a function of type <code>http.HandlerFunc</code>
-(suitable to be passed to the function <code>http.HandleFunc</code>):
-</p>
-
-<pre>
-func makeHandler(fn func (http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
-	return func(w http.ResponseWriter, r *http.Request) {
-		// Here we will extract the page title from the Request,
-		// and call the provided handler \'fn\'
-	}\n-}
-</pre>
-
-<p>
-The returned function is called a closure because it encloses values defined
-outside of it. In this case, the variable <code>fn</code> (the single argument
-to <code>makeHandler</code>) is enclosed by the closure. The variable
-<code>fn</code> will be one of our save, edit, or view handlers.
-</p>
-
-<p>
-Now we can take the code from <code>getTitle</code> and use it here
-(with some minor modifications):
-</p>
-
-<pre>
-!srcextract.bin -src=final.go -name=makeHandler
-</pre>
-
-<p>
-The closure returned by <code>makeHandler</code> is a function that takes
-an <code>http.ResponseWriter</code> and <code>http.Request</code> (in other
-words, an <code>http.HandlerFunc</code>). 
-The closure extracts the <code>title</code> from the request path, and
-validates it with the <code>TitleValidator</code> regexp. If the
-<code>title</code> is invalid, an error will be written to the
-<code>ResponseWriter</code> using the <code>http.NotFound</code> function. 
-If the <code>title</code> is valid, the enclosed handler function
-<code>fn</code> will be called with the <code>ResponseWriter</code>,
-<code>Request</code>, and <code>title</code> as arguments.
-</p>
-
-<p>
-Now we can wrap the handler functions with <code>makeHandler</code> in 
-<code>main</code>, before they are registered with the <code>http</code> 
-package:
-</p>
-
-<pre>
-!srcextract.bin -src=final.go -name=main
-</pre>
-
-<p>
-Finally we remove the calls to <code>getTitle</code> from the handler functions,\n-making them much simpler:
-</p>
-
-<pre>
-!srcextract.bin -src=final.go -name=viewHandler
-
-!srcextract.bin -src=final.go -name=editHandler
-
-!srcextract.bin -src=final.go -name=saveHandler
-</pre>
-
-<h2>Try it out!</h2>
-
-<p>
-<a href="final.go">Click here to view the final code listing.</a>
-</p>
-
-<p>
-Recompile the code, and run the app:
-</p>
-
-<pre>
-$ go build wiki.go
-$ ./wiki
-</pre>
-
-<p>
-Visiting <a href="http://localhost:8080/view/ANewPage">http://localhost:8080/view/ANewPage</a>
-should present you with the page edit form. You should then be able to 
-enter some text, click \'Save\', and be redirected to the newly created page.
-</p>
-
-<h2>Other tasks</h2>
-
-<p>
-Here are some simple tasks you might want to tackle on your own:
-</p>
-
-<ul>
-<li>Store templates in <code>tmpl/</code> and page data in <code>data/</code>.\n-<li>Add a handler to make the web root redirect to 
-	<code>/view/FrontPage</code>.</li>
-<li>Spruce up the page templates by making them valid HTML and adding some
-	CSS rules.</li>
-<li>Implement inter-page linking by converting instances of 
-	<code>[PageName]</code> to <br>\n-	<code>&lt;a href="/view/PageName"&gt;PageName&lt;/a&gt;</code>.\n-	(hint: you could use <code>regexp.ReplaceAllFunc</code> to do this)\n-	</li>
-</ul>

doc/articles/wiki/wiki.html

このファイルは削除されました。

コアとなるコードの解説

このコミットの最も重要な変更は、GoのWebアプリケーションにおけるテンプレートの効率的な利用方法を示している点です。

  1. テンプレートの初期化とキャッシュ:

    • 変更前は、init関数内でedit.htmlview.htmlを個別にパースし、templatesマップに格納していました。これは、テンプレートが追加されるたびにinit関数を修正する必要があり、また、Executeメソッドは単一のテンプレートインスタンスに対してのみ機能するため、複数のテンプレートを効率的に管理する上での柔軟性に欠けていました。
    • 変更後は、var templates = template.Must(template.ParseFiles("edit.html", "view.html"))という一行で、すべてのテンプレートファイルを一度にパースし、単一の*template.Templateインスタンスにまとめます。template.Mustは、パース中にエラーが発生した場合にプログラムをパニックさせることで、起動時の設定ミスを早期に検出します。この方法は、アプリケーションの起動時にすべてのテンプレートをロードし、メモリにキャッシュする、Goにおける推奨されるテンプレートキャッシュパターンです。
  2. ExecuteTemplateの利用:

    • renderTemplate関数では、err := templates.ExecuteTemplate(w, tmpl+".html", p)という行に変更されました。これは、キャッシュされたtemplatesインスタンス(複数のテンプレートを含む)から、tmpl + ".html"という名前で特定のテンプレート(例: "edit.html"や"view.html")を選択して実行することを意味します。
    • この変更により、renderTemplate関数内で毎回template.ParseFilesを呼び出す必要がなくなり、テンプレートのレンダリングが非常に高速になります。これは、特に高負荷なWebアプリケーションにおいて、パフォーマンスを大幅に向上させる重要な最適化です。
  3. チュートリアルコンテンツの動的埋め込み:

    • index.htmlにおける{{code ...}}ディレクティブの導入は、Goのドキュメントシステムがどのように機能するかを示す良い例です。これにより、チュートリアルの説明と実際のコード例が密接に連携し、コードの変更が自動的にドキュメントに反映されるようになります。これは、ドキュメントの正確性を保ち、メンテナンスコストを削減するための強力な機能です。

全体として、このコミットは、GoのWebアプリケーション開発におけるベストプラクティス(テンプレートの効率的なキャッシュと利用)を反映し、公式チュートリアルの品質とメンテナンス性を向上させるものです。

関連リンク

参考にした情報源リンク