From add4e167e3e32ca050a1986a877924d14e52fdc7 Mon Sep 17 00:00:00 2001
From: Andrew Gerrand
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 Editing {title}
+Editing {Title}
-
diff --git a/doc/codelab/wiki/final-noclosure.go b/doc/codelab/wiki/final-noclosure.go
index 2f48565ca2b..99121f298b8 100644
--- a/doc/codelab/wiki/final-noclosure.go
+++ b/doc/codelab/wiki/final-noclosure.go
@@ -8,23 +8,23 @@ import (
"template"
)
-type page struct {
- title string
- body []byte
+type Page struct {
+ Title string
+ Body []byte
}
-func (p *page) save() os.Error {
- filename := p.title + ".txt"
- return ioutil.WriteFile(filename, p.body, 0600)
+func (p *Page) save() os.Error {
+ filename := p.Title + ".txt"
+ return ioutil.WriteFile(filename, p.Body, 0600)
}
-func loadPage(title string) (*page, os.Error) {
+func loadPage(title string) (*Page, os.Error) {
filename := title + ".txt"
body, err := ioutil.ReadFile(filename)
if err != nil {
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) {
@@ -47,7 +47,7 @@ func editHandler(w http.ResponseWriter, r *http.Request) {
}
p, err := loadPage(title)
if err != nil {
- p = &page{title: title}
+ p = &Page{Title: title}
}
renderTemplate(w, "edit", p)
}
@@ -58,7 +58,7 @@ func saveHandler(w http.ResponseWriter, r *http.Request) {
return
}
body := r.FormValue("body")
- p := &page{title: title, body: []byte(body)}
+ p := &Page{Title: title, Body: []byte(body)}
err = p.save()
if err != nil {
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)
}
-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)
if err != nil {
http.Error(w, err.String(), http.StatusInternalServerError)
diff --git a/doc/codelab/wiki/final-noerror.go b/doc/codelab/wiki/final-noerror.go
index cf485226541..0f18912d2e6 100644
--- a/doc/codelab/wiki/final-noerror.go
+++ b/doc/codelab/wiki/final-noerror.go
@@ -7,23 +7,23 @@ import (
"template"
)
-type page struct {
- title string
- body []byte
+type Page struct {
+ Title string
+ Body []byte
}
-func (p *page) save() os.Error {
- filename := p.title + ".txt"
- return ioutil.WriteFile(filename, p.body, 0600)
+func (p *Page) save() os.Error {
+ filename := p.Title + ".txt"
+ return ioutil.WriteFile(filename, p.Body, 0600)
}
-func loadPage(title string) (*page, os.Error) {
+func loadPage(title string) (*Page, os.Error) {
filename := title + ".txt"
body, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
- return &page{title: title, body: body}, nil
+ return &Page{Title: title, Body: body}, nil
}
const lenPath = len("/view/")
@@ -32,7 +32,7 @@ func editHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[lenPath:]
p, err := loadPage(title)
if err != nil {
- p = &page{title: title}
+ p = &Page{Title: title}
}
t, _ := template.ParseFile("edit.html", nil)
t.Execute(p, w)
diff --git a/doc/codelab/wiki/final-parsetemplate.go b/doc/codelab/wiki/final-parsetemplate.go
index f02d116b2af..ea897760159 100644
--- a/doc/codelab/wiki/final-parsetemplate.go
+++ b/doc/codelab/wiki/final-parsetemplate.go
@@ -8,23 +8,23 @@ import (
"template"
)
-type page struct {
- title string
- body []byte
+type Page struct {
+ Title string
+ Body []byte
}
-func (p *page) save() os.Error {
- filename := p.title + ".txt"
- return ioutil.WriteFile(filename, p.body, 0600)
+func (p *Page) save() os.Error {
+ filename := p.Title + ".txt"
+ return ioutil.WriteFile(filename, p.Body, 0600)
}
-func loadPage(title string) (*page, os.Error) {
+func loadPage(title string) (*Page, os.Error) {
filename := title + ".txt"
body, err := ioutil.ReadFile(filename)
if err != nil {
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) {
@@ -39,14 +39,14 @@ func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
func editHandler(w http.ResponseWriter, r *http.Request, title string) {
p, err := loadPage(title)
if err != nil {
- p = &page{title: title}
+ p = &Page{Title: title}
}
renderTemplate(w, "edit", p)
}
func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
body := r.FormValue("body")
- p := &page{title: title, body: []byte(body)}
+ p := &Page{Title: title, Body: []byte(body)}
err := p.save()
if err != nil {
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)
}
-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)
if err != nil {
http.Error(w, err.String(), http.StatusInternalServerError)
diff --git a/doc/codelab/wiki/final-template.go b/doc/codelab/wiki/final-template.go
index 0bb133d3a15..4d6a2cfaba3 100644
--- a/doc/codelab/wiki/final-template.go
+++ b/doc/codelab/wiki/final-template.go
@@ -7,23 +7,23 @@ import (
"template"
)
-type page struct {
- title string
- body []byte
+type Page struct {
+ Title string
+ Body []byte
}
-func (p *page) save() os.Error {
- filename := p.title + ".txt"
- return ioutil.WriteFile(filename, p.body, 0600)
+func (p *Page) save() os.Error {
+ filename := p.Title + ".txt"
+ return ioutil.WriteFile(filename, p.Body, 0600)
}
-func loadPage(title string) (*page, os.Error) {
+func loadPage(title string) (*Page, os.Error) {
filename := title + ".txt"
body, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
- return &page{title: title, body: body}, nil
+ return &Page{Title: title, Body: body}, nil
}
const lenPath = len("/view/")
@@ -32,7 +32,7 @@ func editHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[lenPath:]
p, err := loadPage(title)
if err != nil {
- p = &page{title: title}
+ p = &Page{Title: title}
}
renderTemplate(w, "edit", p)
}
@@ -46,12 +46,12 @@ func viewHandler(w http.ResponseWriter, r *http.Request) {
func saveHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[lenPath:]
body := r.FormValue("body")
- p := &page{title: title, body: []byte(body)}
+ p := &Page{Title: title, Body: []byte(body)}
p.save()
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.Execute(p, w)
}
diff --git a/doc/codelab/wiki/final.go b/doc/codelab/wiki/final.go
index 0c0206bc0c2..8ecd97d7484 100644
--- a/doc/codelab/wiki/final.go
+++ b/doc/codelab/wiki/final.go
@@ -8,23 +8,23 @@ import (
"template"
)
-type page struct {
- title string
- body []byte
+type Page struct {
+ Title string
+ Body []byte
}
-func (p *page) save() os.Error {
- filename := p.title + ".txt"
- return ioutil.WriteFile(filename, p.body, 0600)
+func (p *Page) save() os.Error {
+ filename := p.Title + ".txt"
+ return ioutil.WriteFile(filename, p.Body, 0600)
}
-func loadPage(title string) (*page, os.Error) {
+func loadPage(title string) (*Page, os.Error) {
filename := title + ".txt"
body, err := ioutil.ReadFile(filename)
if err != nil {
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) {
@@ -39,14 +39,14 @@ func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
func editHandler(w http.ResponseWriter, r *http.Request, title string) {
p, err := loadPage(title)
if err != nil {
- p = &page{title: title}
+ p = &Page{Title: title}
}
renderTemplate(w, "edit", p)
}
func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
body := r.FormValue("body")
- p := &page{title: title, body: []byte(body)}
+ p := &Page{Title: title, Body: []byte(body)}
err := p.save()
if err != nil {
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)
if err != nil {
http.Error(w, err.String(), http.StatusInternalServerError)
diff --git a/doc/codelab/wiki/htmlify.go b/doc/codelab/wiki/htmlify.go
index 4a52e077f28..456d06fd525 100644
--- a/doc/codelab/wiki/htmlify.go
+++ b/doc/codelab/wiki/htmlify.go
@@ -8,5 +8,5 @@ import (
func main() {
b, _ := ioutil.ReadAll(os.Stdin)
- template.HTMLFormatter(os.Stdout, b, "")
+ template.HTMLFormatter(os.Stdout, "", b)
}
diff --git a/doc/codelab/wiki/index.html b/doc/codelab/wiki/index.html
index c494a3cedcd..e4273de7a65 100644
--- a/doc/codelab/wiki/index.html
+++ b/doc/codelab/wiki/index.html
@@ -71,14 +71,14 @@ declaration.
page
as a struct with two fields representing
+Here, we define Page
as a struct with two fields representing
the title and body.
-type page struct {
- title string
- body []byte
+type Page struct {
+ Title string
+ Body []byte
}
@@ -86,33 +86,33 @@ type page struct {
The type []byte
means "a byte
slice".
(See Effective Go
for more on slices.)
-The body
element is a []byte
rather than
+The Body
element is a []byte
rather than
string
because that is the type expected by the io
libraries we will use, as you'll see below.
-The page
struct describes how page data will be stored in memory.
+The Page
struct describes how page data will be stored in memory.
But what about persistent storage? We can address that by creating a
-save
method on page
:
+save
method on Page
:
-func (p *page) save() os.Error { - filename := p.title + ".txt" - return ioutil.WriteFile(filename, p.body, 0600) +func (p *Page) save() os.Error { + filename := p.Title + ".txt" + return ioutil.WriteFile(filename, p.Body, 0600) }
This method's signature reads: "This is a method named save
that
-takes as its receiver p
, a pointer to page
. It takes
+takes as its receiver p
, a pointer to Page
. It takes
no parameters, and returns a value of type os.Error
."
-This method will save the page
's body
to a text
-file. For simplicity, we will use the title
as the file name.
+This method will save the Page
's Body
to a text
+file. For simplicity, we will use the Title
as the file name.
@@ -120,7 +120,7 @@ The save
method returns an os.Error
value because
that is the return type of WriteFile
(a standard library function
that writes a byte slice to a file). The save
method returns the
error value, to let the application handle it should anything go wrong while
-writing the file. If all goes well, page.save()
will return
+writing the file. If all goes well, Page.save()
will return
nil
(the zero-value for pointers, interfaces, and some other
types).
-func loadPage(title string) *page { +func loadPage(title string) *Page { filename := title + ".txt" body, _ := ioutil.ReadFile(filename) - return &page{title: title, body: body} + return &Page{Title: title, Body: body} }
The function loadPage
constructs the file name from
-title
, reads the file's contents into a new
-page
, and returns a pointer to that new page
.
+Title
, reads the file's contents into a new
+Page
, and returns a pointer to that new page
.
@@ -161,23 +161,23 @@ error return value (in essence, assigning the value to nothing).
But what happens if ReadFile
encounters an error? For example,
the file might not exist. We should not ignore such errors. Let's modify the
-function to return *page
and os.Error
.
+function to return *Page
and os.Error
.
-func loadPage(title string) (*page, os.Error) { +func loadPage(title string) (*Page, os.Error) { filename := title + ".txt" body, err := ioutil.ReadFile(filename) if err != nil { return nil, err } - return &page{title: title, body: body}, nil + return &Page{Title: title, Body: body}, nil }
Callers of this function can now check the second parameter; if it is
-nil
then it has successfully loaded a page. If not, it will be an
+nil
then it has successfully loaded a Page. If not, it will be an
os.Error
that can be handled by the caller (see the os package documentation for
details).
@@ -191,17 +191,17 @@ written:
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() p2, _ := loadPage("TestPage") - fmt.Println(string(p2.body)) + fmt.Println(string(p2.Body)) }
After compiling and executing this code, a file named TestPage.txt
would be created, containing the contents of p1
. The file would
-then be read into the struct p2
, and its body
element
+then be read into the struct p2
, and its Body
element
printed to the screen.
A wiki is not a wiki without the ability to edit pages. Let's create two new @@ -401,7 +401,7 @@ func main() {
The function editHandler
loads the page
-(or, if it doesn't exist, create an empty page
struct),
+(or, if it doesn't exist, create an empty Page
struct),
and displays an HTML form.
edit.html
, and add the following lines:
-<h1>Editing {title}</h1> +<h1>Editing {Title}</h1> -<form action="/save/{title}" method="POST"> -<div><textarea name="body" rows="20" cols="80">{body|html}</textarea></div> +<form action="/save/{Title}" method="POST"> +<div><textarea name="Body" rows="20" cols="80">{Body|html}</textarea></div> <div><input type="submit" value="Save"></div> </form>@@ -472,7 +472,7 @@ func editHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, err := loadPage(title) if err != nil { - p = &page{title: title} + p = &Page{Title: title} } t, _ := template.ParseFile("edit.html", nil) t.Execute(p, w) @@ -486,15 +486,15 @@ The function
template.ParseFile
will read the contents of
The method t.Execute
replaces all occurrences of
-{title}
and {body}
with the values of
-p.title
and p.body
, and writes the resultant
+{Title}
and {Body}
with the values of
+p.Title
and p.Body
, and writes the resultant
HTML to the http.ResponseWriter
.
-Note that we've used {body|html}
in the above template.
+Note that we've used {Body|html}
in the above template.
The |html
part asks the template engine to pass the value
-body
through the html
formatter before outputting it,
+Body
through the html
formatter before outputting it,
which escapes HTML characters (such as replacing >
with
>
).
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
-<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>
@@ -548,12 +548,12 @@ func editHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, err := loadPage(title) if err != nil { - p = &page{title: title} + p = &Page{Title: title} } renderTemplate(w, "edit", p) } -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.Execute(p, w) } @@ -568,8 +568,8 @@ The handlers are now shorter and simpler.
What if you visit /view/APageThatDoesntExist
? The program will
crash. This is because it ignores the error return value from
-loadPage
. Instead, if the requested page doesn't exist, it should
-redirect the client to the edit page so the content may be created:
+loadPage
. Instead, if the requested Page doesn't exist, it should
+redirect the client to the edit Page so the content may be created:
@@ -589,7 +589,7 @@ The", - p.title, p.title, p.body) + p.Title, p.Title, p.Body) } func main() { diff --git a/doc/codelab/wiki/part1-noerror.go b/doc/codelab/wiki/part1-noerror.go index 39e8331e393..14cfc321a78 100644 --- a/doc/codelab/wiki/part1-noerror.go +++ b/doc/codelab/wiki/part1-noerror.go @@ -6,25 +6,25 @@ import ( "os" ) -type page struct { - title string - body []byte +type Page struct { + Title string + Body []byte } -func (p *page) save() os.Error { - filename := p.title + ".txt" - return ioutil.WriteFile(filename, p.body, 0600) +func (p *Page) save() os.Error { + filename := p.Title + ".txt" + return ioutil.WriteFile(filename, p.Body, 0600) } -func loadPage(title string) *page { +func loadPage(title string) *Page { filename := title + ".txt" body, _ := ioutil.ReadFile(filename) - return &page{title: title, body: body} + return &Page{Title: title, Body: body} } 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() p2 := loadPage("TestPage") - fmt.Println(string(p2.body)) + fmt.Println(string(p2.Body)) } diff --git a/doc/codelab/wiki/part1.go b/doc/codelab/wiki/part1.go index f3678baa51f..4b0654f8b12 100644 --- a/doc/codelab/wiki/part1.go +++ b/doc/codelab/wiki/part1.go @@ -6,28 +6,28 @@ import ( "os" ) -type page struct { - title string - body []byte +type Page struct { + Title string + Body []byte } -func (p *page) save() os.Error { - filename := p.title + ".txt" - return ioutil.WriteFile(filename, p.body, 0600) +func (p *Page) save() os.Error { + filename := p.Title + ".txt" + return ioutil.WriteFile(filename, p.Body, 0600) } -func loadPage(title string) (*page, os.Error) { +func loadPage(title string) (*Page, os.Error) { filename := title + ".txt" body, err := ioutil.ReadFile(filename) if err != nil { return nil, err } - return &page{title: title, body: body}, nil + return &Page{Title: title, Body: body}, nil } 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() p2, _ := loadPage("TestPage") - fmt.Println(string(p2.body)) + fmt.Println(string(p2.Body)) } diff --git a/doc/codelab/wiki/part2.go b/doc/codelab/wiki/part2.go index 8d4454a74ab..d57c3a01f1b 100644 --- a/doc/codelab/wiki/part2.go +++ b/doc/codelab/wiki/part2.go @@ -7,23 +7,23 @@ import ( "os" ) -type page struct { - title string - body []byte +type Page struct { + Title string + Body []byte } -func (p *page) save() os.Error { - filename := p.title + ".txt" - return ioutil.WriteFile(filename, p.body, 0600) +func (p *Page) save() os.Error { + filename := p.Title + ".txt" + return ioutil.WriteFile(filename, p.Body, 0600) } -func loadPage(title string) (*page, os.Error) { +func loadPage(title string) (*Page, os.Error) { filename := title + ".txt" body, err := ioutil.ReadFile(filename) if err != nil { return nil, err } - return &page{title: title, body: body}, nil + return &Page{Title: title, Body: body}, nil } const lenPath = len("/view/") @@ -31,7 +31,7 @@ const lenPath = len("/view/") func viewHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, _ := loadPage(title) - fmt.Fprintf(w, "http.Redirect
function adds an HTTP status code of header to the HTTP response. -Saving pages
+Saving Pages
The function
saveHandler
will handle the form submission. @@ -599,7 +599,7 @@ The functionsaveHandler
will handle the form submission. func saveHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] body := r.FormValue("body") - p := &page{title: title, body: []byte(body)} + p := &Page{Title: title, Body: []byte(body)} p.save() http.Redirect(w, r, "/view/"+title, http.StatusFound) } @@ -607,7 +607,7 @@ func saveHandler(w http.ResponseWriter, r *http.Request) {The page title (provided in the URL) and the form's only field, -
@@ -615,7 +615,7 @@ and the client is redirected to thebody
, are stored in a newpage
. +Body
, are stored in a newPage
. Thesave()
method is then called to write the data to a file, and the client is redirected to the/view/
page./view/
page.The value returned by
@@ -634,7 +634,7 @@ First, let's handle the errors inFormValue
is of typestring
. We must convert that value to[]byte
before it will fit into -thepage
struct. We use[]byte(body)
to perform +thePage
struct. We use[]byte(body)
to perform the conversion.renderTemplate
:-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) if err != nil { http.Error(w, err.String(), http.StatusInternalServerError) @@ -660,7 +660,7 @@ Now let's fix upsaveHandler
:func saveHandler(w http.ResponseWriter, r *http.Request, title string) { body := r.FormValue("body") - p := &page{title: title, body: []byte(body)} + p := &Page{Title: title, Body: []byte(body)} err := p.save() if err != nil { http.Error(w, err.String(), http.StatusInternalServerError) @@ -725,7 +725,7 @@ theExecute
method on the appropriateTemplate
fromtemplates
:-func renderTemplate(w http.ResponseWriter, tmpl string, p *page) { +func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { err := templates[tmpl].Execute(p, w) if err != nil { http.Error(w, err.String(), http.StatusInternalServerError) @@ -747,7 +747,6 @@ Then we can create a global variable to store our validation regexp:-var titleValidator = regexp.MustCompile("^[a-zA-Z0-9]+$")@@ -761,7 +760,7 @@ the expression compilation fails, while
Compile
returns anNow, let's write a function that extracts the title string from the request -URL, and tests it against our
titleValidator
expression: +URL, and tests it against ourTitleValidator
expression:@@ -807,7 +806,7 @@ func editHandler(w http.ResponseWriter, r *http.Request) { } p, err := loadPage(title) if err != nil { - p = &page{title: title} + p = &Page{Title: title} } renderTemplate(w, "edit", p) } @@ -818,7 +817,7 @@ func saveHandler(w http.ResponseWriter, r *http.Request) { return } body := r.FormValue("body") - p := &page{title: title, body: []byte(body)} + p := &Page{Title: title, Body: []byte(body)} err = p.save() if err != nil { http.Error(w, err.String(), http.StatusInternalServerError) @@ -895,7 +894,7 @@ The closure returned bymakeHandler
is a function that takes anhttp.ResponseWriter
andhttp.Request
(in other words, anhttp.HandlerFunc
). The closure extracts thetitle
from the request path, and -validates it with thetitleValidator
regexp. If the +validates it with theTitleValidator
regexp. If thetitle
is invalid, an error will be written to theResponseWriter
using thehttp.NotFound
function. If thetitle
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) { p, err := loadPage(title) if err != nil { - p = &page{title: title} + p = &Page{Title: title} } renderTemplate(w, "edit", p) } func saveHandler(w http.ResponseWriter, r *http.Request, title string) { body := r.FormValue("body") - p := &page{title: title, body: []byte(body)} + p := &Page{Title: title, Body: []byte(body)} err := p.save() if err != nil { http.Error(w, err.String(), http.StatusInternalServerError) diff --git a/doc/codelab/wiki/notemplate.go b/doc/codelab/wiki/notemplate.go index c1f952c8382..9cbe9ad768e 100644 --- a/doc/codelab/wiki/notemplate.go +++ b/doc/codelab/wiki/notemplate.go @@ -7,23 +7,23 @@ import ( "os" ) -type page struct { - title string - body []byte +type Page struct { + Title string + Body []byte } -func (p *page) save() os.Error { - filename := p.title + ".txt" - return ioutil.WriteFile(filename, p.body, 0600) +func (p *Page) save() os.Error { + filename := p.Title + ".txt" + return ioutil.WriteFile(filename, p.Body, 0600) } -func loadPage(title string) (*page, os.Error) { +func loadPage(title string) (*Page, os.Error) { filename := title + ".txt" body, err := ioutil.ReadFile(filename) if err != nil { return nil, err } - return &page{title: title, body: body}, nil + return &Page{Title: title, Body: body}, nil } const lenPath = len("/view/") @@ -31,21 +31,21 @@ const lenPath = len("/view/") func viewHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, _ := loadPage(title) - fmt.Fprintf(w, "%s
%s", p.title, p.body) + fmt.Fprintf(w, "%s
%s", p.Title, p.Body) } func editHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, err := loadPage(title) if err != nil { - p = &page{title: title} + p = &Page{Title: title} } fmt.Fprintf(w, "Editing %s
"+ "
[edit]
+[edit]
-
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 page
as a struct with two fields representing
+Here, we define Page
as a struct with two fields representing
the title and body.
-!./srcextract.bin -src=part1.go -name=page +!./srcextract.bin -src=part1.go -name=Page
The type []byte
means "a byte
slice".
(See Effective Go
for more on slices.)
-The body
element is a []byte
rather than
+The Body
element is a []byte
rather than
string
because that is the type expected by the io
libraries we will use, as you'll see below.
-The page
struct describes how page data will be stored in memory.
+The Page
struct describes how page data will be stored in memory.
But what about persistent storage? We can address that by creating a
-save
method on page
:
+save
method on Page
:
@@ -100,13 +100,13 @@ But what about persistent storage? We can address that by creating aThis method's signature reads: "This is a method named
save
that -takes as its receiverp
, a pointer topage
. It takes +takes as its receiverp
, a pointer toPage
. It takes no parameters, and returns a value of typeos.Error
."-This method will save the
page
'sbody
to a text -file. For simplicity, we will use thetitle
as the file name. +This method will save thePage
'sBody
to a text +file. For simplicity, we will use theTitle
as the file name.@@ -114,7 +114,7 @@ The
@@ -136,8 +136,8 @@ We will want to load pages, too:save
method returns anos.Error
value because that is the return type ofWriteFile
(a standard library function that writes a byte slice to a file). Thesave
method returns the error value, to let the application handle it should anything go wrong while -writing the file. If all goes well,page.save()
will return +writing the file. If all goes well,Page.save()
will returnnil
(the zero-value for pointers, interfaces, and some other types).The function
loadPage
constructs the file name from -title
, reads the file's contents into a new -page
, and returns a pointer to that newpage
. +Title
, reads the file's contents into a new +Page
, and returns a pointer to that newpage
.@@ -151,7 +151,7 @@ error return value (in essence, assigning the value to nothing).
But what happens if
ReadFile
encounters an error? For example, the file might not exist. We should not ignore such errors. Let's modify the -function to return*page
andos.Error
. +function to return*Page
andos.Error
.@@ -160,7 +160,7 @@ function to return*page
andos.Error
.Callers of this function can now check the second parameter; if it is -
nil
then it has successfully loaded a page. If not, it will be an +nil
then it has successfully loaded a Page. If not, it will be anos.Error
that can be handled by the caller (see the os package documentation for details). @@ -179,7 +179,7 @@ written:After compiling and executing this code, a file named
@@ -334,7 +334,7 @@ href="http://localhost:8080/view/test">http://localhost:8080/view/test -TestPage.txt
would be created, containing the contents ofp1
. The file would -then be read into the structp2
, and itsbody
element +then be read into the structp2
, and itsBody
element printed to the screen.Editing pages
+Editing Pages
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
main()
:The function
@@ -413,15 +413,15 @@ The functioneditHandler
loads the page -(or, if it doesn't exist, create an emptypage
struct), +(or, if it doesn't exist, create an emptyPage
struct), and displays an HTML form.template.ParseFile
will read the contents ofThe method
t.Execute
replaces all occurrences of -{title}
and{body}
with the values of -p.title
andp.body
, and writes the resultant +{Title}
and{Body}
with the values of +p.Title
andp.Body
, and writes the resultant HTML to thehttp.ResponseWriter
.-Note that we've used
{body|html}
in the above template. +Note that we've used{Body|html}
in the above template. The|html
part asks the template engine to pass the value -body
through thehtml
formatter before outputting it, +Body
through thehtml
formatter before outputting it, which escapes HTML characters (such as replacing>
with>
). This will prevent user data from corrupting the form HTML. @@ -472,8 +472,8 @@ The handlers are now shorter and simpler.What if you visit
/view/APageThatDoesntExist
? The program will crash. This is because it ignores the error return value from -loadPage
. Instead, if the requested page doesn't exist, it should -redirect the client to the edit page so the content may be created: +loadPage
. Instead, if the requested Page doesn't exist, it should +redirect the client to the edit Page so the content may be created:@@ -486,7 +486,7 @@ Thehttp.Redirect
function adds an HTTP status code of header to the HTTP response. -Saving pages
+Saving Pages
The function
saveHandler
will handle the form submission. @@ -498,7 +498,7 @@ The functionsaveHandler
will handle the form submission.The page title (provided in the URL) and the form's only field, -
@@ -506,7 +506,7 @@ and the client is redirected to thebody
, are stored in a newpage
. +Body
, are stored in a newPage
. Thesave()
method is then called to write the data to a file, and the client is redirected to the/view/
page./view/
page.The value returned by
@@ -610,7 +610,7 @@ Then we can create a global variable to store our validation regexp:FormValue
is of typestring
. We must convert that value to[]byte
before it will fit into -thepage
struct. We use[]byte(body)
to perform +thePage
struct. We use[]byte(body)
to perform the conversion.-!./srcextract.bin -src=final-noclosure.go -name=titleValidator +!./srcextract.bin -src=final-noclosure.go -name=TitleValidator@@ -624,7 +624,7 @@ the expression compilation fails, while
Compile
returns anNow, let's write a function that extracts the title string from the request -URL, and tests it against our
titleValidator
expression: +URL, and tests it against ourTitleValidator
expression:@@ -708,7 +708,7 @@ The closure returned bymakeHandler
is a function that takes anhttp.ResponseWriter
andhttp.Request
(in other words, anhttp.HandlerFunc
). The closure extracts thetitle
from the request path, and -validates it with thetitleValidator
regexp. If the +validates it with theTitleValidator
regexp. If thetitle
is invalid, an error will be written to theResponseWriter
using thehttp.NotFound
function. If thetitle
is valid, the enclosed handler function diff --git a/src/run.bash b/src/run.bash index 0cd129253c2..9d7b02f9f95 100755 --- a/src/run.bash +++ b/src/run.bash @@ -103,6 +103,9 @@ if [[ $(uname | tr A-Z a-z | sed 's/mingw/windows/') != *windows* ]]; then fi ) || exit $? +(xcd ../doc/codelab/wiki +gomake test) || exit $? + for i in ../misc/dashboard/builder ../misc/goplay do (xcd $i