1
0
mirror of https://github.com/golang/go synced 2024-11-22 02:24:41 -07:00

doc/codelab/wiki: update to work with template changes, add to run.bash

Fixes #1444.

R=rsc, r
CC=golang-dev
https://golang.org/cl/3979045
This commit is contained in:
Andrew Gerrand 2011-01-26 14:56:52 +10:00
parent b7949035d6
commit add4e167e3
17 changed files with 194 additions and 190 deletions

View File

@ -1,6 +1,6 @@
<h1>Editing {title}</h1> <h1>Editing {Title}</h1>
<form action="/save/{title}" method="POST"> <form action="/save/{Title}" method="POST">
<div><textarea name="body" rows="20" cols="80">{body|html}</textarea></div> <div><textarea name="Body" rows="20" cols="80">{Body|html}</textarea></div>
<div><input type="submit" value="Save"></div> <div><input type="submit" value="Save"></div>
</form> </form>

View File

@ -8,23 +8,23 @@ import (
"template" "template"
) )
type page struct { type Page struct {
title string Title string
body []byte Body []byte
} }
func (p *page) save() os.Error { func (p *Page) save() os.Error {
filename := p.title + ".txt" filename := p.Title + ".txt"
return ioutil.WriteFile(filename, p.body, 0600) return ioutil.WriteFile(filename, p.Body, 0600)
} }
func loadPage(title string) (*page, os.Error) { func loadPage(title string) (*Page, os.Error) {
filename := title + ".txt" filename := title + ".txt"
body, err := ioutil.ReadFile(filename) body, err := ioutil.ReadFile(filename)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &page{title: title, body: body}, nil return &Page{Title: title, Body: body}, nil
} }
func viewHandler(w http.ResponseWriter, r *http.Request) { func viewHandler(w http.ResponseWriter, r *http.Request) {
@ -47,7 +47,7 @@ func editHandler(w http.ResponseWriter, r *http.Request) {
} }
p, err := loadPage(title) p, err := loadPage(title)
if err != nil { if err != nil {
p = &page{title: title} p = &Page{Title: title}
} }
renderTemplate(w, "edit", p) renderTemplate(w, "edit", p)
} }
@ -58,7 +58,7 @@ func saveHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
body := r.FormValue("body") body := r.FormValue("body")
p := &page{title: title, body: []byte(body)} p := &Page{Title: title, Body: []byte(body)}
err = p.save() err = p.save()
if err != nil { if err != nil {
http.Error(w, err.String(), http.StatusInternalServerError) http.Error(w, err.String(), http.StatusInternalServerError)
@ -67,7 +67,7 @@ func saveHandler(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/view/"+title, http.StatusFound) http.Redirect(w, r, "/view/"+title, http.StatusFound)
} }
func renderTemplate(w http.ResponseWriter, tmpl string, p *page) { func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
t, err := template.ParseFile(tmpl+".html", nil) t, err := template.ParseFile(tmpl+".html", nil)
if err != nil { if err != nil {
http.Error(w, err.String(), http.StatusInternalServerError) http.Error(w, err.String(), http.StatusInternalServerError)

View File

@ -7,23 +7,23 @@ import (
"template" "template"
) )
type page struct { type Page struct {
title string Title string
body []byte Body []byte
} }
func (p *page) save() os.Error { func (p *Page) save() os.Error {
filename := p.title + ".txt" filename := p.Title + ".txt"
return ioutil.WriteFile(filename, p.body, 0600) return ioutil.WriteFile(filename, p.Body, 0600)
} }
func loadPage(title string) (*page, os.Error) { func loadPage(title string) (*Page, os.Error) {
filename := title + ".txt" filename := title + ".txt"
body, err := ioutil.ReadFile(filename) body, err := ioutil.ReadFile(filename)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &page{title: title, body: body}, nil return &Page{Title: title, Body: body}, nil
} }
const lenPath = len("/view/") const lenPath = len("/view/")
@ -32,7 +32,7 @@ func editHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[lenPath:] title := r.URL.Path[lenPath:]
p, err := loadPage(title) p, err := loadPage(title)
if err != nil { if err != nil {
p = &page{title: title} p = &Page{Title: title}
} }
t, _ := template.ParseFile("edit.html", nil) t, _ := template.ParseFile("edit.html", nil)
t.Execute(p, w) t.Execute(p, w)

View File

@ -8,23 +8,23 @@ import (
"template" "template"
) )
type page struct { type Page struct {
title string Title string
body []byte Body []byte
} }
func (p *page) save() os.Error { func (p *Page) save() os.Error {
filename := p.title + ".txt" filename := p.Title + ".txt"
return ioutil.WriteFile(filename, p.body, 0600) return ioutil.WriteFile(filename, p.Body, 0600)
} }
func loadPage(title string) (*page, os.Error) { func loadPage(title string) (*Page, os.Error) {
filename := title + ".txt" filename := title + ".txt"
body, err := ioutil.ReadFile(filename) body, err := ioutil.ReadFile(filename)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &page{title: title, body: body}, nil return &Page{Title: title, Body: body}, nil
} }
func viewHandler(w http.ResponseWriter, r *http.Request, title string) { func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
@ -39,14 +39,14 @@ func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
func editHandler(w http.ResponseWriter, r *http.Request, title string) { func editHandler(w http.ResponseWriter, r *http.Request, title string) {
p, err := loadPage(title) p, err := loadPage(title)
if err != nil { if err != nil {
p = &page{title: title} p = &Page{Title: title}
} }
renderTemplate(w, "edit", p) renderTemplate(w, "edit", p)
} }
func saveHandler(w http.ResponseWriter, r *http.Request, title string) { func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
body := r.FormValue("body") body := r.FormValue("body")
p := &page{title: title, body: []byte(body)} p := &Page{Title: title, Body: []byte(body)}
err := p.save() err := p.save()
if err != nil { if err != nil {
http.Error(w, err.String(), http.StatusInternalServerError) http.Error(w, err.String(), http.StatusInternalServerError)
@ -55,7 +55,7 @@ func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
http.Redirect(w, r, "/view/"+title, http.StatusFound) http.Redirect(w, r, "/view/"+title, http.StatusFound)
} }
func renderTemplate(w http.ResponseWriter, tmpl string, p *page) { func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
t, err := template.ParseFile(tmpl+".html", nil) t, err := template.ParseFile(tmpl+".html", nil)
if err != nil { if err != nil {
http.Error(w, err.String(), http.StatusInternalServerError) http.Error(w, err.String(), http.StatusInternalServerError)

View File

@ -7,23 +7,23 @@ import (
"template" "template"
) )
type page struct { type Page struct {
title string Title string
body []byte Body []byte
} }
func (p *page) save() os.Error { func (p *Page) save() os.Error {
filename := p.title + ".txt" filename := p.Title + ".txt"
return ioutil.WriteFile(filename, p.body, 0600) return ioutil.WriteFile(filename, p.Body, 0600)
} }
func loadPage(title string) (*page, os.Error) { func loadPage(title string) (*Page, os.Error) {
filename := title + ".txt" filename := title + ".txt"
body, err := ioutil.ReadFile(filename) body, err := ioutil.ReadFile(filename)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &page{title: title, body: body}, nil return &Page{Title: title, Body: body}, nil
} }
const lenPath = len("/view/") const lenPath = len("/view/")
@ -32,7 +32,7 @@ func editHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[lenPath:] title := r.URL.Path[lenPath:]
p, err := loadPage(title) p, err := loadPage(title)
if err != nil { if err != nil {
p = &page{title: title} p = &Page{Title: title}
} }
renderTemplate(w, "edit", p) renderTemplate(w, "edit", p)
} }
@ -46,12 +46,12 @@ func viewHandler(w http.ResponseWriter, r *http.Request) {
func saveHandler(w http.ResponseWriter, r *http.Request) { func saveHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[lenPath:] title := r.URL.Path[lenPath:]
body := r.FormValue("body") body := r.FormValue("body")
p := &page{title: title, body: []byte(body)} p := &Page{Title: title, Body: []byte(body)}
p.save() p.save()
http.Redirect(w, r, "/view/"+title, http.StatusFound) http.Redirect(w, r, "/view/"+title, http.StatusFound)
} }
func renderTemplate(w http.ResponseWriter, tmpl string, p *page) { func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
t, _ := template.ParseFile(tmpl+".html", nil) t, _ := template.ParseFile(tmpl+".html", nil)
t.Execute(p, w) t.Execute(p, w)
} }

View File

@ -8,23 +8,23 @@ import (
"template" "template"
) )
type page struct { type Page struct {
title string Title string
body []byte Body []byte
} }
func (p *page) save() os.Error { func (p *Page) save() os.Error {
filename := p.title + ".txt" filename := p.Title + ".txt"
return ioutil.WriteFile(filename, p.body, 0600) return ioutil.WriteFile(filename, p.Body, 0600)
} }
func loadPage(title string) (*page, os.Error) { func loadPage(title string) (*Page, os.Error) {
filename := title + ".txt" filename := title + ".txt"
body, err := ioutil.ReadFile(filename) body, err := ioutil.ReadFile(filename)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &page{title: title, body: body}, nil return &Page{Title: title, Body: body}, nil
} }
func viewHandler(w http.ResponseWriter, r *http.Request, title string) { func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
@ -39,14 +39,14 @@ func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
func editHandler(w http.ResponseWriter, r *http.Request, title string) { func editHandler(w http.ResponseWriter, r *http.Request, title string) {
p, err := loadPage(title) p, err := loadPage(title)
if err != nil { if err != nil {
p = &page{title: title} p = &Page{Title: title}
} }
renderTemplate(w, "edit", p) renderTemplate(w, "edit", p)
} }
func saveHandler(w http.ResponseWriter, r *http.Request, title string) { func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
body := r.FormValue("body") body := r.FormValue("body")
p := &page{title: title, body: []byte(body)} p := &Page{Title: title, Body: []byte(body)}
err := p.save() err := p.save()
if err != nil { if err != nil {
http.Error(w, err.String(), http.StatusInternalServerError) http.Error(w, err.String(), http.StatusInternalServerError)
@ -63,7 +63,7 @@ func init() {
} }
} }
func renderTemplate(w http.ResponseWriter, tmpl string, p *page) { func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
err := templates[tmpl].Execute(p, w) err := templates[tmpl].Execute(p, w)
if err != nil { if err != nil {
http.Error(w, err.String(), http.StatusInternalServerError) http.Error(w, err.String(), http.StatusInternalServerError)

View File

@ -8,5 +8,5 @@ import (
func main() { func main() {
b, _ := ioutil.ReadAll(os.Stdin) b, _ := ioutil.ReadAll(os.Stdin)
template.HTMLFormatter(os.Stdout, b, "") template.HTMLFormatter(os.Stdout, "", b)
} }

View File

@ -71,14 +71,14 @@ declaration.
<p> <p>
Let's start by defining the data structures. A wiki consists of a series of 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). 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 Here, we define <code>Page</code> as a struct with two fields representing
the title and body. the title and body.
</p> </p>
<pre> <pre>
type page struct { type Page struct {
title string Title string
body []byte Body []byte
} }
</pre> </pre>
@ -86,33 +86,33 @@ type page struct {
The type <code>[]byte</code> means "a <code>byte</code> slice". The type <code>[]byte</code> means "a <code>byte</code> slice".
(See <a href="http://golang.org/doc/effective_go.html#slices">Effective Go</a> (See <a href="http://golang.org/doc/effective_go.html#slices">Effective Go</a>
for more on slices.) for more on slices.)
The <code>body</code> element is a <code>[]byte</code> rather than 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> <code>string</code> because that is the type expected by the <code>io</code>
libraries we will use, as you'll see below. libraries we will use, as you'll see below.
</p> </p>
<p> <p>
The <code>page</code> struct describes how page data will be stored in memory. 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 But what about persistent storage? We can address that by creating a
<code>save</code> method on <code>page</code>: <code>save</code> method on <code>Page</code>:
</p> </p>
<pre> <pre>
func (p *page) save() os.Error { func (p *Page) save() os.Error {
filename := p.title + &#34;.txt&#34; filename := p.Title + &#34;.txt&#34;
return ioutil.WriteFile(filename, p.body, 0600) return ioutil.WriteFile(filename, p.Body, 0600)
} }
</pre> </pre>
<p> <p>
This method's signature reads: "This is a method named <code>save</code> that 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 takes as its receiver <code>p</code>, a pointer to <code>Page</code> . It takes
no parameters, and returns a value of type <code>os.Error</code>." no parameters, and returns a value of type <code>os.Error</code>."
</p> </p>
<p> <p>
This method will save the <code>page</code>'s <code>body</code> to a text 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. file. For simplicity, we will use the <code>Title</code> as the file name.
</p> </p>
<p> <p>
@ -120,7 +120,7 @@ The <code>save</code> method returns an <code>os.Error</code> value because
that is the return type of <code>WriteFile</code> (a standard library function 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 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 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 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 <code>nil</code> (the zero-value for pointers, interfaces, and some other
types). types).
</p> </p>
@ -137,17 +137,17 @@ We will want to load pages, too:
</p> </p>
<pre> <pre>
func loadPage(title string) *page { func loadPage(title string) *Page {
filename := title + &#34;.txt&#34; filename := title + &#34;.txt&#34;
body, _ := ioutil.ReadFile(filename) body, _ := ioutil.ReadFile(filename)
return &amp;page{title: title, body: body} return &amp;Page{Title: title, Body: body}
} }
</pre> </pre>
<p> <p>
The function <code>loadPage</code> constructs the file name from The function <code>loadPage</code> constructs the file name from
<code>title</code>, reads the file's contents into a new <code>Title</code>, reads the file's contents into a new
<code>page</code>, and returns a pointer to that new <code>page</code>. <code>Page</code>, and returns a pointer to that new <code>page</code>.
</p> </p>
<p> <p>
@ -161,23 +161,23 @@ error return value (in essence, assigning the value to nothing).
<p> <p>
But what happens if <code>ReadFile</code> encounters an error? For example, But what happens if <code>ReadFile</code> encounters an error? For example,
the file might not exist. We should not ignore such errors. Let's modify the the file might not exist. We should not ignore such errors. Let's modify the
function to return <code>*page</code> and <code>os.Error</code>. function to return <code>*Page</code> and <code>os.Error</code>.
</p> </p>
<pre> <pre>
func loadPage(title string) (*page, os.Error) { func loadPage(title string) (*Page, os.Error) {
filename := title + &#34;.txt&#34; filename := title + &#34;.txt&#34;
body, err := ioutil.ReadFile(filename) body, err := ioutil.ReadFile(filename)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &amp;page{title: title, body: body}, nil return &amp;Page{Title: title, Body: body}, nil
} }
</pre> </pre>
<p> <p>
Callers of this function can now check the second parameter; if it is 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>nil</code> then it has successfully loaded a Page. If not, it will be an
<code>os.Error</code> that can be handled by the caller (see the <a <code>os.Error</code> that can be handled by the caller (see the <a
href="http://golang.org/pkg/os/#Error">os package documentation</a> for href="http://golang.org/pkg/os/#Error">os package documentation</a> for
details). details).
@ -191,17 +191,17 @@ written:
<pre> <pre>
func main() { func main() {
p1 := &amp;page{title: &#34;TestPage&#34;, body: []byte(&#34;This is a sample page.&#34;)} p1 := &amp;Page{Title: &#34;TestPage&#34;, Body: []byte(&#34;This is a sample Page.&#34;)}
p1.save() p1.save()
p2, _ := loadPage(&#34;TestPage&#34;) p2, _ := loadPage(&#34;TestPage&#34;)
fmt.Println(string(p2.body)) fmt.Println(string(p2.Body))
} }
</pre> </pre>
<p> <p>
After compiling and executing this code, a file named <code>TestPage.txt</code> 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 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 then be read into the struct <code>p2</code>, and its <code>Body</code> element
printed to the screen. printed to the screen.
</p> </p>
@ -317,7 +317,7 @@ const lenPath = len(&#34;/view/&#34;)
func viewHandler(w http.ResponseWriter, r *http.Request) { func viewHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[lenPath:] title := r.URL.Path[lenPath:]
p, _ := loadPage(title) 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) fmt.Fprintf(w, &#34;&lt;h1&gt;%s&lt;/h1&gt;&lt;div&gt;%s&lt;/div&gt;&#34;, p.Title, p.Body)
} }
</pre> </pre>
@ -377,7 +377,7 @@ href="http://localhost:8080/view/test">http://localhost:8080/view/test</a></code
should show a page titled "test" containing the words "Hello world". should show a page titled "test" containing the words "Hello world".
</p> </p>
<h2>Editing pages</h2> <h2>Editing Pages</h2>
<p> <p>
A wiki is not a wiki without the ability to edit pages. Let's create two new A wiki is not a wiki without the ability to edit pages. Let's create two new
@ -401,7 +401,7 @@ func main() {
<p> <p>
The function <code>editHandler</code> loads the page The function <code>editHandler</code> loads the page
(or, if it doesn't exist, create an empty <code>page</code> struct), (or, if it doesn't exist, create an empty <code>Page</code> struct),
and displays an HTML form. and displays an HTML form.
</p> </p>
@ -410,14 +410,14 @@ func editHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[lenPath:] title := r.URL.Path[lenPath:]
p, err := loadPage(title) p, err := loadPage(title)
if err != nil { if err != nil {
p = &amp;page{title: title} p = &amp;Page{Title: title}
} }
fmt.Fprintf(w, &#34;&lt;h1&gt;Editing %s&lt;/h1&gt;&#34;+ fmt.Fprintf(w, &#34;&lt;h1&gt;Editing %s&lt;/h1&gt;&#34;+
&#34;&lt;form action=\&#34;/save/%s\&#34; method=\&#34;POST\&#34;&gt;&#34;+ &#34;&lt;form action=\&#34;/save/%s\&#34; method=\&#34;POST\&#34;&gt;&#34;+
&#34;&lt;textarea name=\&#34;body\&#34;&gt;%s&lt;/textarea&gt;&lt;br&gt;&#34;+ &#34;&lt;textarea name=\&#34;body\&#34;&gt;%s&lt;/textarea&gt;&lt;br&gt;&#34;+
&#34;&lt;input type=\&#34;submit\&#34; value=\&#34;Save\&#34;&gt;&#34;+ &#34;&lt;input type=\&#34;submit\&#34; value=\&#34;Save\&#34;&gt;&#34;+
&#34;&lt;/form&gt;&#34;, &#34;&lt;/form&gt;&#34;,
p.title, p.title, p.body) p.Title, p.Title, p.Body)
} }
</pre> </pre>
@ -454,10 +454,10 @@ Open a new file named <code>edit.html</code>, and add the following lines:
</p> </p>
<pre> <pre>
&lt;h1&gt;Editing {title}&lt;/h1&gt; &lt;h1&gt;Editing {Title}&lt;/h1&gt;
&lt;form action=&#34;/save/{title}&#34; method=&#34;POST&#34;&gt; &lt;form action=&#34;/save/{Title}&#34; method=&#34;POST&#34;&gt;
&lt;div&gt;&lt;textarea name=&#34;body&#34; rows=&#34;20&#34; cols=&#34;80&#34;&gt;{body|html}&lt;/textarea&gt;&lt;/div&gt; &lt;div&gt;&lt;textarea name=&#34;Body&#34; rows=&#34;20&#34; cols=&#34;80&#34;&gt;{Body|html}&lt;/textarea&gt;&lt;/div&gt;
&lt;div&gt;&lt;input type=&#34;submit&#34; value=&#34;Save&#34;&gt;&lt;/div&gt; &lt;div&gt;&lt;input type=&#34;submit&#34; value=&#34;Save&#34;&gt;&lt;/div&gt;
&lt;/form&gt; &lt;/form&gt;
</pre> </pre>
@ -472,7 +472,7 @@ func editHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[lenPath:] title := r.URL.Path[lenPath:]
p, err := loadPage(title) p, err := loadPage(title)
if err != nil { if err != nil {
p = &amp;page{title: title} p = &amp;Page{Title: title}
} }
t, _ := template.ParseFile(&#34;edit.html&#34;, nil) t, _ := template.ParseFile(&#34;edit.html&#34;, nil)
t.Execute(p, w) t.Execute(p, w)
@ -486,15 +486,15 @@ The function <code>template.ParseFile</code> will read the contents of
<p> <p>
The method <code>t.Execute</code> replaces all occurrences of The method <code>t.Execute</code> replaces all occurrences of
<code>{title}</code> and <code>{body}</code> with the values of <code>{Title}</code> and <code>{Body}</code> with the values of
<code>p.title</code> and <code>p.body</code>, and writes the resultant <code>p.Title</code> and <code>p.Body</code>, and writes the resultant
HTML to the <code>http.ResponseWriter</code>. HTML to the <code>http.ResponseWriter</code>.
</p> </p>
<p> <p>
Note that we've used <code>{body|html}</code> in the above template. Note that we've used <code>{Body|html}</code> in the above template.
The <code>|html</code> part asks the template engine to pass the value The <code>|html</code> part asks the template engine to pass the value
<code>body</code> through the <code>html</code> formatter before outputting it, <code>Body</code> through the <code>html</code> formatter before outputting it,
which escapes HTML characters (such as replacing <code>&gt;</code> with which escapes HTML characters (such as replacing <code>&gt;</code> with
<code>&amp;gt;</code>). <code>&amp;gt;</code>).
This will prevent user data from corrupting the form HTML. This will prevent user data from corrupting the form HTML.
@ -511,11 +511,11 @@ While we're working with templates, let's create a template for our
</p> </p>
<pre> <pre>
&lt;h1&gt;{title}&lt;/h1&gt; &lt;h1&gt;{Title}&lt;/h1&gt;
&lt;p&gt;[&lt;a href=&#34;/edit/{title}&#34;&gt;edit&lt;/a&gt;]&lt;/p&gt; &lt;p&gt;[&lt;a href=&#34;/edit/{Title}&#34;&gt;edit&lt;/a&gt;]&lt;/p&gt;
&lt;div&gt;{body}&lt;/div&gt; &lt;div&gt;{Body}&lt;/div&gt;
</pre> </pre>
<p> <p>
@ -548,12 +548,12 @@ func editHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[lenPath:] title := r.URL.Path[lenPath:]
p, err := loadPage(title) p, err := loadPage(title)
if err != nil { if err != nil {
p = &amp;page{title: title} p = &amp;Page{Title: title}
} }
renderTemplate(w, &#34;edit&#34;, p) renderTemplate(w, &#34;edit&#34;, p)
} }
func renderTemplate(w http.ResponseWriter, tmpl string, p *page) { func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
t, _ := template.ParseFile(tmpl+&#34;.html&#34;, nil) t, _ := template.ParseFile(tmpl+&#34;.html&#34;, nil)
t.Execute(p, w) t.Execute(p, w)
} }
@ -568,8 +568,8 @@ The handlers are now shorter and simpler.
<p> <p>
What if you visit <code>/view/APageThatDoesntExist</code>? The program will What if you visit <code>/view/APageThatDoesntExist</code>? The program will
crash. This is because it ignores the error return value from crash. This is because it ignores the error return value from
<code>loadPage</code>. Instead, if the requested page doesn't exist, it should <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: redirect the client to the edit Page so the content may be created:
</p> </p>
<pre> <pre>
@ -589,7 +589,7 @@ The <code>http.Redirect</code> function adds an HTTP status code of
header to the HTTP response. header to the HTTP response.
</p> </p>
<h2>Saving pages</h2> <h2>Saving Pages</h2>
<p> <p>
The function <code>saveHandler</code> will handle the form submission. The function <code>saveHandler</code> will handle the form submission.
@ -599,7 +599,7 @@ The function <code>saveHandler</code> will handle the form submission.
func saveHandler(w http.ResponseWriter, r *http.Request) { func saveHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[lenPath:] title := r.URL.Path[lenPath:]
body := r.FormValue(&#34;body&#34;) body := r.FormValue(&#34;body&#34;)
p := &amp;page{title: title, body: []byte(body)} p := &amp;Page{Title: title, Body: []byte(body)}
p.save() p.save()
http.Redirect(w, r, &#34;/view/&#34;+title, http.StatusFound) http.Redirect(w, r, &#34;/view/&#34;+title, http.StatusFound)
} }
@ -607,7 +607,7 @@ func saveHandler(w http.ResponseWriter, r *http.Request) {
<p> <p>
The page title (provided in the URL) and the form's only field, The page title (provided in the URL) and the form's only field,
<code>body</code>, are stored in a new <code>page</code>. <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, 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. and the client is redirected to the <code>/view/</code> page.
</p> </p>
@ -615,7 +615,7 @@ 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>. 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 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 <code>Page</code> struct. We use <code>[]byte(body)</code> to perform
the conversion. the conversion.
</p> </p>
@ -634,7 +634,7 @@ First, let's handle the errors in <code>renderTemplate</code>:
</p> </p>
<pre> <pre>
func renderTemplate(w http.ResponseWriter, tmpl string, p *page) { func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
t, err := template.ParseFile(tmpl+&#34;.html&#34;, nil) t, err := template.ParseFile(tmpl+&#34;.html&#34;, nil)
if err != nil { if err != nil {
http.Error(w, err.String(), http.StatusInternalServerError) http.Error(w, err.String(), http.StatusInternalServerError)
@ -660,7 +660,7 @@ Now let's fix up <code>saveHandler</code>:
<pre> <pre>
func saveHandler(w http.ResponseWriter, r *http.Request, title string) { func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
body := r.FormValue(&#34;body&#34;) body := r.FormValue(&#34;body&#34;)
p := &amp;page{title: title, body: []byte(body)} p := &amp;Page{Title: title, Body: []byte(body)}
err := p.save() err := p.save()
if err != nil { if err != nil {
http.Error(w, err.String(), http.StatusInternalServerError) http.Error(w, err.String(), http.StatusInternalServerError)
@ -725,7 +725,7 @@ the <code>Execute</code> method on the appropriate <code>Template</code> from
<code>templates</code>: <code>templates</code>:
<pre> <pre>
func renderTemplate(w http.ResponseWriter, tmpl string, p *page) { func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
err := templates[tmpl].Execute(p, w) err := templates[tmpl].Execute(p, w)
if err != nil { if err != nil {
http.Error(w, err.String(), http.StatusInternalServerError) http.Error(w, err.String(), http.StatusInternalServerError)
@ -747,7 +747,6 @@ Then we can create a global variable to store our validation regexp:
</p> </p>
<pre> <pre>
var titleValidator = regexp.MustCompile(&#34;^[a-zA-Z0-9]+$&#34;)
</pre> </pre>
<p> <p>
@ -761,7 +760,7 @@ the expression compilation fails, while <code>Compile</code> returns an
<p> <p>
Now, let's write a function that extracts the title string from the request Now, let's write a function that extracts the title string from the request
URL, and tests it against our <code>titleValidator</code> expression: URL, and tests it against our <code>TitleValidator</code> expression:
</p> </p>
<pre> <pre>
@ -807,7 +806,7 @@ func editHandler(w http.ResponseWriter, r *http.Request) {
} }
p, err := loadPage(title) p, err := loadPage(title)
if err != nil { if err != nil {
p = &amp;page{title: title} p = &amp;Page{Title: title}
} }
renderTemplate(w, &#34;edit&#34;, p) renderTemplate(w, &#34;edit&#34;, p)
} }
@ -818,7 +817,7 @@ func saveHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
body := r.FormValue(&#34;body&#34;) body := r.FormValue(&#34;body&#34;)
p := &amp;page{title: title, body: []byte(body)} p := &amp;Page{Title: title, Body: []byte(body)}
err = p.save() err = p.save()
if err != nil { if err != nil {
http.Error(w, err.String(), http.StatusInternalServerError) http.Error(w, err.String(), http.StatusInternalServerError)
@ -895,7 +894,7 @@ The closure returned by <code>makeHandler</code> is a function that takes
an <code>http.ResponseWriter</code> and <code>http.Request</code> (in other an <code>http.ResponseWriter</code> and <code>http.Request</code> (in other
words, an <code>http.HandlerFunc</code>). words, an <code>http.HandlerFunc</code>).
The closure extracts the <code>title</code> from the request path, and The closure extracts the <code>title</code> from the request path, and
validates it with the <code>titleValidator</code> regexp. If the validates it with the <code>TitleValidator</code> regexp. If the
<code>title</code> is invalid, an error will be written to the <code>title</code> is invalid, an error will be written to the
<code>ResponseWriter</code> using the <code>http.NotFound</code> function. <code>ResponseWriter</code> using the <code>http.NotFound</code> function.
If the <code>title</code> is valid, the enclosed handler function If the <code>title</code> is valid, the enclosed handler function
@ -936,14 +935,14 @@ func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
func editHandler(w http.ResponseWriter, r *http.Request, title string) { func editHandler(w http.ResponseWriter, r *http.Request, title string) {
p, err := loadPage(title) p, err := loadPage(title)
if err != nil { if err != nil {
p = &amp;page{title: title} p = &amp;Page{Title: title}
} }
renderTemplate(w, &#34;edit&#34;, p) renderTemplate(w, &#34;edit&#34;, p)
} }
func saveHandler(w http.ResponseWriter, r *http.Request, title string) { func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
body := r.FormValue(&#34;body&#34;) body := r.FormValue(&#34;body&#34;)
p := &amp;page{title: title, body: []byte(body)} p := &amp;Page{Title: title, Body: []byte(body)}
err := p.save() err := p.save()
if err != nil { if err != nil {
http.Error(w, err.String(), http.StatusInternalServerError) http.Error(w, err.String(), http.StatusInternalServerError)

View File

@ -7,23 +7,23 @@ import (
"os" "os"
) )
type page struct { type Page struct {
title string Title string
body []byte Body []byte
} }
func (p *page) save() os.Error { func (p *Page) save() os.Error {
filename := p.title + ".txt" filename := p.Title + ".txt"
return ioutil.WriteFile(filename, p.body, 0600) return ioutil.WriteFile(filename, p.Body, 0600)
} }
func loadPage(title string) (*page, os.Error) { func loadPage(title string) (*Page, os.Error) {
filename := title + ".txt" filename := title + ".txt"
body, err := ioutil.ReadFile(filename) body, err := ioutil.ReadFile(filename)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &page{title: title, body: body}, nil return &Page{Title: title, Body: body}, nil
} }
const lenPath = len("/view/") const lenPath = len("/view/")
@ -31,21 +31,21 @@ const lenPath = len("/view/")
func viewHandler(w http.ResponseWriter, r *http.Request) { func viewHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[lenPath:] title := r.URL.Path[lenPath:]
p, _ := loadPage(title) p, _ := loadPage(title)
fmt.Fprintf(w, "<h1>%s</h1><div>%s</div>", p.title, p.body) fmt.Fprintf(w, "<h1>%s</h1><div>%s</div>", p.Title, p.Body)
} }
func editHandler(w http.ResponseWriter, r *http.Request) { func editHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[lenPath:] title := r.URL.Path[lenPath:]
p, err := loadPage(title) p, err := loadPage(title)
if err != nil { if err != nil {
p = &page{title: title} p = &Page{Title: title}
} }
fmt.Fprintf(w, "<h1>Editing %s</h1>"+ fmt.Fprintf(w, "<h1>Editing %s</h1>"+
"<form action=\"/save/%s\" method=\"POST\">"+ "<form action=\"/save/%s\" method=\"POST\">"+
"<textarea name=\"body\">%s</textarea><br>"+ "<textarea name=\"body\">%s</textarea><br>"+
"<input type=\"submit\" value=\"Save\">"+ "<input type=\"submit\" value=\"Save\">"+
"</form>", "</form>",
p.title, p.title, p.body) p.Title, p.Title, p.Body)
} }
func main() { func main() {

View File

@ -6,25 +6,25 @@ import (
"os" "os"
) )
type page struct { type Page struct {
title string Title string
body []byte Body []byte
} }
func (p *page) save() os.Error { func (p *Page) save() os.Error {
filename := p.title + ".txt" filename := p.Title + ".txt"
return ioutil.WriteFile(filename, p.body, 0600) return ioutil.WriteFile(filename, p.Body, 0600)
} }
func loadPage(title string) *page { func loadPage(title string) *Page {
filename := title + ".txt" filename := title + ".txt"
body, _ := ioutil.ReadFile(filename) body, _ := ioutil.ReadFile(filename)
return &page{title: title, body: body} return &Page{Title: title, Body: body}
} }
func main() { func main() {
p1 := &page{title: "TestPage", body: []byte("This is a sample page.")} p1 := &Page{Title: "TestPage", Body: []byte("This is a sample page.")}
p1.save() p1.save()
p2 := loadPage("TestPage") p2 := loadPage("TestPage")
fmt.Println(string(p2.body)) fmt.Println(string(p2.Body))
} }

View File

@ -6,28 +6,28 @@ import (
"os" "os"
) )
type page struct { type Page struct {
title string Title string
body []byte Body []byte
} }
func (p *page) save() os.Error { func (p *Page) save() os.Error {
filename := p.title + ".txt" filename := p.Title + ".txt"
return ioutil.WriteFile(filename, p.body, 0600) return ioutil.WriteFile(filename, p.Body, 0600)
} }
func loadPage(title string) (*page, os.Error) { func loadPage(title string) (*Page, os.Error) {
filename := title + ".txt" filename := title + ".txt"
body, err := ioutil.ReadFile(filename) body, err := ioutil.ReadFile(filename)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &page{title: title, body: body}, nil return &Page{Title: title, Body: body}, nil
} }
func main() { func main() {
p1 := &page{title: "TestPage", body: []byte("This is a sample page.")} p1 := &Page{Title: "TestPage", Body: []byte("This is a sample Page.")}
p1.save() p1.save()
p2, _ := loadPage("TestPage") p2, _ := loadPage("TestPage")
fmt.Println(string(p2.body)) fmt.Println(string(p2.Body))
} }

View File

@ -7,23 +7,23 @@ import (
"os" "os"
) )
type page struct { type Page struct {
title string Title string
body []byte Body []byte
} }
func (p *page) save() os.Error { func (p *Page) save() os.Error {
filename := p.title + ".txt" filename := p.Title + ".txt"
return ioutil.WriteFile(filename, p.body, 0600) return ioutil.WriteFile(filename, p.Body, 0600)
} }
func loadPage(title string) (*page, os.Error) { func loadPage(title string) (*Page, os.Error) {
filename := title + ".txt" filename := title + ".txt"
body, err := ioutil.ReadFile(filename) body, err := ioutil.ReadFile(filename)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &page{title: title, body: body}, nil return &Page{Title: title, Body: body}, nil
} }
const lenPath = len("/view/") const lenPath = len("/view/")
@ -31,7 +31,7 @@ const lenPath = len("/view/")
func viewHandler(w http.ResponseWriter, r *http.Request) { func viewHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[lenPath:] title := r.URL.Path[lenPath:]
p, _ := loadPage(title) p, _ := loadPage(title)
fmt.Fprintf(w, "<h1>%s</h1><div>%s</div>", p.title, p.body) fmt.Fprintf(w, "<h1>%s</h1><div>%s</div>", p.Title, p.Body)
} }
func main() { func main() {

View File

@ -6,6 +6,7 @@ import (
"go/parser" "go/parser"
"go/printer" "go/printer"
"go/ast" "go/ast"
"go/token"
"log" "log"
"os" "os"
) )
@ -25,7 +26,8 @@ func main() {
os.Exit(2) os.Exit(2)
} }
// load file // load file
file, err := parser.ParseFile(*srcFn, nil, 0) fs := token.NewFileSet()
file, err := parser.ParseFile(fs, *srcFn, nil, 0)
if err != nil { if err != nil {
log.Exit(err) log.Exit(err)
} }
@ -47,7 +49,7 @@ func main() {
os.Exit(1) os.Exit(1)
} }
b := new(bytes.Buffer) b := new(bytes.Buffer)
p.Fprint(b, file) p.Fprint(b, fs, file)
// drop package declaration // drop package declaration
if !*showPkg { if !*showPkg {
for { for {

View File

@ -1,6 +1,6 @@
<h1>Editing Test</h1> <h1>Editing Test</h1>
<form action="/save/Test" method="POST"> <form action="/save/Test" method="POST">
<div><textarea name="body" rows="20" cols="80"></textarea></div> <div><textarea name="Body" rows="20" cols="80"></textarea></div>
<div><input type="submit" value="Save"></div> <div><input type="submit" value="Save"></div>
</form> </form>

View File

@ -1,5 +1,5 @@
<h1>{title}</h1> <h1>{Title}</h1>
<p>[<a href="/edit/{title}">edit</a>]</p> <p>[<a href="/edit/{Title}">edit</a>]</p>
<div>{body}</div> <div>{Body}</div>

View File

@ -71,27 +71,27 @@ declaration.
<p> <p>
Let's start by defining the data structures. A wiki consists of a series of 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). 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 Here, we define <code>Page</code> as a struct with two fields representing
the title and body. the title and body.
</p> </p>
<pre> <pre>
!./srcextract.bin -src=part1.go -name=page !./srcextract.bin -src=part1.go -name=Page
</pre> </pre>
<p> <p>
The type <code>[]byte</code> means "a <code>byte</code> slice". The type <code>[]byte</code> means "a <code>byte</code> slice".
(See <a href="http://golang.org/doc/effective_go.html#slices">Effective Go</a> (See <a href="http://golang.org/doc/effective_go.html#slices">Effective Go</a>
for more on slices.) for more on slices.)
The <code>body</code> element is a <code>[]byte</code> rather than 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> <code>string</code> because that is the type expected by the <code>io</code>
libraries we will use, as you'll see below. libraries we will use, as you'll see below.
</p> </p>
<p> <p>
The <code>page</code> struct describes how page data will be stored in memory. 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 But what about persistent storage? We can address that by creating a
<code>save</code> method on <code>page</code>: <code>save</code> method on <code>Page</code>:
</p> </p>
<pre> <pre>
@ -100,13 +100,13 @@ But what about persistent storage? We can address that by creating a
<p> <p>
This method's signature reads: "This is a method named <code>save</code> that 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 takes as its receiver <code>p</code>, a pointer to <code>Page</code> . It takes
no parameters, and returns a value of type <code>os.Error</code>." no parameters, and returns a value of type <code>os.Error</code>."
</p> </p>
<p> <p>
This method will save the <code>page</code>'s <code>body</code> to a text 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. file. For simplicity, we will use the <code>Title</code> as the file name.
</p> </p>
<p> <p>
@ -114,7 +114,7 @@ The <code>save</code> method returns an <code>os.Error</code> value because
that is the return type of <code>WriteFile</code> (a standard library function 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 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 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 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 <code>nil</code> (the zero-value for pointers, interfaces, and some other
types). types).
</p> </p>
@ -136,8 +136,8 @@ We will want to load pages, too:
<p> <p>
The function <code>loadPage</code> constructs the file name from The function <code>loadPage</code> constructs the file name from
<code>title</code>, reads the file's contents into a new <code>Title</code>, reads the file's contents into a new
<code>page</code>, and returns a pointer to that new <code>page</code>. <code>Page</code>, and returns a pointer to that new <code>page</code>.
</p> </p>
<p> <p>
@ -151,7 +151,7 @@ error return value (in essence, assigning the value to nothing).
<p> <p>
But what happens if <code>ReadFile</code> encounters an error? For example, But what happens if <code>ReadFile</code> encounters an error? For example,
the file might not exist. We should not ignore such errors. Let's modify the the file might not exist. We should not ignore such errors. Let's modify the
function to return <code>*page</code> and <code>os.Error</code>. function to return <code>*Page</code> and <code>os.Error</code>.
</p> </p>
<pre> <pre>
@ -160,7 +160,7 @@ function to return <code>*page</code> and <code>os.Error</code>.
<p> <p>
Callers of this function can now check the second parameter; if it is 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>nil</code> then it has successfully loaded a Page. If not, it will be an
<code>os.Error</code> that can be handled by the caller (see the <a <code>os.Error</code> that can be handled by the caller (see the <a
href="http://golang.org/pkg/os/#Error">os package documentation</a> for href="http://golang.org/pkg/os/#Error">os package documentation</a> for
details). details).
@ -179,7 +179,7 @@ written:
<p> <p>
After compiling and executing this code, a file named <code>TestPage.txt</code> 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 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 then be read into the struct <code>p2</code>, and its <code>Body</code> element
printed to the screen. printed to the screen.
</p> </p>
@ -334,7 +334,7 @@ href="http://localhost:8080/view/test">http://localhost:8080/view/test</a></code
should show a page titled "test" containing the words "Hello world". should show a page titled "test" containing the words "Hello world".
</p> </p>
<h2>Editing pages</h2> <h2>Editing Pages</h2>
<p> <p>
A wiki is not a wiki without the ability to edit pages. Let's create two new A wiki is not a wiki without the ability to edit pages. Let's create two new
@ -353,7 +353,7 @@ First, we add them to <code>main()</code>:
<p> <p>
The function <code>editHandler</code> loads the page The function <code>editHandler</code> loads the page
(or, if it doesn't exist, create an empty <code>page</code> struct), (or, if it doesn't exist, create an empty <code>Page</code> struct),
and displays an HTML form. and displays an HTML form.
</p> </p>
@ -413,15 +413,15 @@ The function <code>template.ParseFile</code> will read the contents of
<p> <p>
The method <code>t.Execute</code> replaces all occurrences of The method <code>t.Execute</code> replaces all occurrences of
<code>{title}</code> and <code>{body}</code> with the values of <code>{Title}</code> and <code>{Body}</code> with the values of
<code>p.title</code> and <code>p.body</code>, and writes the resultant <code>p.Title</code> and <code>p.Body</code>, and writes the resultant
HTML to the <code>http.ResponseWriter</code>. HTML to the <code>http.ResponseWriter</code>.
</p> </p>
<p> <p>
Note that we've used <code>{body|html}</code> in the above template. Note that we've used <code>{Body|html}</code> in the above template.
The <code>|html</code> part asks the template engine to pass the value The <code>|html</code> part asks the template engine to pass the value
<code>body</code> through the <code>html</code> formatter before outputting it, <code>Body</code> through the <code>html</code> formatter before outputting it,
which escapes HTML characters (such as replacing <code>&gt;</code> with which escapes HTML characters (such as replacing <code>&gt;</code> with
<code>&amp;gt;</code>). <code>&amp;gt;</code>).
This will prevent user data from corrupting the form HTML. This will prevent user data from corrupting the form HTML.
@ -472,8 +472,8 @@ The handlers are now shorter and simpler.
<p> <p>
What if you visit <code>/view/APageThatDoesntExist</code>? The program will What if you visit <code>/view/APageThatDoesntExist</code>? The program will
crash. This is because it ignores the error return value from crash. This is because it ignores the error return value from
<code>loadPage</code>. Instead, if the requested page doesn't exist, it should <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: redirect the client to the edit Page so the content may be created:
</p> </p>
<pre> <pre>
@ -486,7 +486,7 @@ The <code>http.Redirect</code> function adds an HTTP status code of
header to the HTTP response. header to the HTTP response.
</p> </p>
<h2>Saving pages</h2> <h2>Saving Pages</h2>
<p> <p>
The function <code>saveHandler</code> will handle the form submission. The function <code>saveHandler</code> will handle the form submission.
@ -498,7 +498,7 @@ The function <code>saveHandler</code> will handle the form submission.
<p> <p>
The page title (provided in the URL) and the form's only field, The page title (provided in the URL) and the form's only field,
<code>body</code>, are stored in a new <code>page</code>. <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, 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. and the client is redirected to the <code>/view/</code> page.
</p> </p>
@ -506,7 +506,7 @@ 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>. 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 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 <code>Page</code> struct. We use <code>[]byte(body)</code> to perform
the conversion. the conversion.
</p> </p>
@ -610,7 +610,7 @@ Then we can create a global variable to store our validation regexp:
</p> </p>
<pre> <pre>
!./srcextract.bin -src=final-noclosure.go -name=titleValidator !./srcextract.bin -src=final-noclosure.go -name=TitleValidator
</pre> </pre>
<p> <p>
@ -624,7 +624,7 @@ the expression compilation fails, while <code>Compile</code> returns an
<p> <p>
Now, let's write a function that extracts the title string from the request Now, let's write a function that extracts the title string from the request
URL, and tests it against our <code>titleValidator</code> expression: URL, and tests it against our <code>TitleValidator</code> expression:
</p> </p>
<pre> <pre>
@ -708,7 +708,7 @@ The closure returned by <code>makeHandler</code> is a function that takes
an <code>http.ResponseWriter</code> and <code>http.Request</code> (in other an <code>http.ResponseWriter</code> and <code>http.Request</code> (in other
words, an <code>http.HandlerFunc</code>). words, an <code>http.HandlerFunc</code>).
The closure extracts the <code>title</code> from the request path, and The closure extracts the <code>title</code> from the request path, and
validates it with the <code>titleValidator</code> regexp. If the validates it with the <code>TitleValidator</code> regexp. If the
<code>title</code> is invalid, an error will be written to the <code>title</code> is invalid, an error will be written to the
<code>ResponseWriter</code> using the <code>http.NotFound</code> function. <code>ResponseWriter</code> using the <code>http.NotFound</code> function.
If the <code>title</code> is valid, the enclosed handler function If the <code>title</code> is valid, the enclosed handler function

View File

@ -103,6 +103,9 @@ if [[ $(uname | tr A-Z a-z | sed 's/mingw/windows/') != *windows* ]]; then
fi fi
) || exit $? ) || exit $?
(xcd ../doc/codelab/wiki
gomake test) || exit $?
for i in ../misc/dashboard/builder ../misc/goplay for i in ../misc/dashboard/builder ../misc/goplay
do do
(xcd $i (xcd $i