From 758ff64c69e34965f8af5b2d6ffd65e8d7ab2150 Mon Sep 17 00:00:00 2001 From: Ondřej Surý Date: Mon, 14 Feb 2011 13:23:51 +0100 Subject: Imported Upstream version 2011-02-01.1 --- doc/codelab/wiki/Makefile | 9 +-- doc/codelab/wiki/edit.html | 6 +- doc/codelab/wiki/final-noclosure.go | 22 +++--- doc/codelab/wiki/final-noerror.go | 18 ++--- doc/codelab/wiki/final-parsetemplate.go | 22 +++--- doc/codelab/wiki/final-template.go | 22 +++--- doc/codelab/wiki/final.go | 22 +++--- doc/codelab/wiki/get.go | 50 ++++++++++++++ doc/codelab/wiki/htmlify.go | 2 +- doc/codelab/wiki/index.html | 117 ++++++++++++++++---------------- doc/codelab/wiki/notemplate.go | 22 +++--- doc/codelab/wiki/part1-noerror.go | 20 +++--- doc/codelab/wiki/part1.go | 20 +++--- doc/codelab/wiki/part2.go | 18 ++--- doc/codelab/wiki/srcextract.go | 8 ++- doc/codelab/wiki/test.sh | 35 +++++----- doc/codelab/wiki/view.html | 6 +- doc/codelab/wiki/wiki.html | 56 +++++++-------- 18 files changed, 265 insertions(+), 210 deletions(-) create mode 100644 doc/codelab/wiki/get.go (limited to 'doc/codelab') diff --git a/doc/codelab/wiki/Makefile b/doc/codelab/wiki/Makefile index e0549fc8e..0d948ed4b 100644 --- a/doc/codelab/wiki/Makefile +++ b/doc/codelab/wiki/Makefile @@ -13,12 +13,13 @@ CLEANFILES+=index.html srcextract.bin htmlify.bin index.html: srcextract.bin htmlify.bin awk '/^!/{system(substr($$0,2)); next} {print}' "$$@" < wiki.html > index.html -test: final.bin - ./test.sh - rm -f final.6 final.bin +test: get.bin + bash ./test.sh + rm -f get.6 get.bin %.bin: %.$O $(LD) -o $@ $< -%.$O: + +%.$O: %.go $(GC) $*.go diff --git a/doc/codelab/wiki/edit.html b/doc/codelab/wiki/edit.html index 71a919496..7a5768ce9 100644 --- a/doc/codelab/wiki/edit.html +++ b/doc/codelab/wiki/edit.html @@ -1,6 +1,6 @@ -

Editing {title}

+

Editing {Title}

-
-
+ +
diff --git a/doc/codelab/wiki/final-noclosure.go b/doc/codelab/wiki/final-noclosure.go index 2f48565ca..99121f298 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 cf4852265..0f18912d2 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 f02d116b2..ea8977601 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 0bb133d3a..4d6a2cfab 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 0c0206bc0..8ecd97d74 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/get.go b/doc/codelab/wiki/get.go new file mode 100644 index 000000000..342831416 --- /dev/null +++ b/doc/codelab/wiki/get.go @@ -0,0 +1,50 @@ +package main + +import ( + "http" + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" +) + +var ( + post = flag.String("post", "", "urlencoded form data to POST") + addr = flag.Bool("addr", false, "find open address and print to stdout") +) + +func main() { + flag.Parse() + if *addr { + l, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + log.Fatal(err) + } + defer l.Close() + fmt.Print(l.Addr()) + return + } + url := flag.Arg(0) + if url == "" { + log.Fatal("no url supplied") + } + var r *http.Response + var err os.Error + if *post != "" { + b := strings.NewReader(*post) + r, err = http.Post(url, "application/x-www-form-urlencoded", b) + } else { + r, _, err = http.Get(url) + } + if err != nil { + log.Fatal(err) + } + defer r.Body.Close() + _, err = io.Copy(os.Stdout, r.Body) + if err != nil { + log.Fatal(err) + } +} diff --git a/doc/codelab/wiki/htmlify.go b/doc/codelab/wiki/htmlify.go index 4a52e077f..456d06fd5 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 c494a3ced..fe99c32d1 100644 --- a/doc/codelab/wiki/index.html +++ b/doc/codelab/wiki/index.html @@ -71,14 +71,14 @@ declaration.

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.

-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).

@@ -137,17 +137,17 @@ We will want to load pages, too:

-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.

@@ -317,7 +317,7 @@ const lenPath = len("/view/") func viewHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] 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) } @@ -377,7 +377,7 @@ href="http://localhost:8080/view/test">http://localhost:8080/view/test -

Editing pages

+

Editing Pages

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.

@@ -410,14 +410,14 @@ 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, "<h1>Editing %s</h1>"+ "<form action=\"/save/%s\" method=\"POST\">"+ "<textarea name=\"body\">%s</textarea><br>"+ "<input type=\"submit\" value=\"Save\">"+ "</form>", - p.title, p.title, p.body) + p.Title, p.Title, p.Body) } @@ -454,10 +454,10 @@ Open a new file named 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 &gt;). 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 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 function saveHandler 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, -body, are stored in a new page. +Body, are stored in a new Page. The save() method is then called to write the data to a file, and the client is redirected to the /view/ page.

@@ -615,7 +615,7 @@ and the client is redirected to the /view/ page.

The value returned by FormValue is of type string. We must convert that value to []byte before it will fit into -the page struct. We use []byte(body) to perform +the Page struct. We use []byte(body) to perform the conversion.

@@ -634,7 +634,7 @@ First, let's handle the errors in 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 up saveHandler:
 
 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 @@ the Execute method on the appropriate Template from
 templates:
 
 
-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 an

Now, 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 our TitleValidator 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 by makeHandler is a function that takes
 an http.ResponseWriter and http.Request (in other
 words, an http.HandlerFunc). 
 The closure extracts the title from the request path, and
-validates it with the titleValidator regexp. If the
+validates it with the TitleValidator regexp. If the
 title is invalid, an error will be written to the
 ResponseWriter using the http.NotFound function. 
 If the title 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 c1f952c83..9cbe9ad76 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

"+ "
"+ "
"+ ""+ "
", - 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 39e8331e3..14cfc321a 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 f3678baa5..4b0654f8b 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 8d4454a74..d57c3a01f 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, "

%s

%s
", p.title, p.body) + fmt.Fprintf(w, "

%s

%s
", p.Title, p.Body) } func main() { diff --git a/doc/codelab/wiki/srcextract.go b/doc/codelab/wiki/srcextract.go index 0addc61c4..cab092f58 100644 --- a/doc/codelab/wiki/srcextract.go +++ b/doc/codelab/wiki/srcextract.go @@ -6,6 +6,7 @@ import ( "go/parser" "go/printer" "go/ast" + "go/token" "log" "os" ) @@ -25,9 +26,10 @@ func main() { os.Exit(2) } // load file - file, err := parser.ParseFile(*srcFn, nil, 0) + fs := token.NewFileSet() + file, err := parser.ParseFile(fs, *srcFn, nil, 0) if err != nil { - log.Exit(err) + log.Fatal(err) } // create printer p := &printer.Config{ @@ -47,7 +49,7 @@ func main() { os.Exit(1) } b := new(bytes.Buffer) - p.Fprint(b, file) + p.Fprint(b, fs, file) // drop package declaration if !*showPkg { for { diff --git a/doc/codelab/wiki/test.sh b/doc/codelab/wiki/test.sh index 5b752fe3c..95ff145b9 100755 --- a/doc/codelab/wiki/test.sh +++ b/doc/codelab/wiki/test.sh @@ -1,24 +1,27 @@ -#1/bin/bash - -./final.bin & -wiki_pid=$! +#!/usr/bin/env bash +set -e +wiki_pid= cleanup() { kill $wiki_pid - rm -f test_*.out Test.txt - exit ${1:-1} + rm -f test_*.out Test.txt final-test.bin final-test.go } -trap cleanup INT +trap cleanup 0 INT -sleep 1 +gomake get.bin +addr=$(./get.bin -addr) +sed s/:8080/$addr/ < final.go > final-test.go +gomake final-test.bin +./final-test.bin & +wiki_pid=$! -curl -s -o test_edit.out http://localhost:8080/edit/Test -cmp test_edit.out test_edit.good || cleanup 1 -curl -s -o /dev/null -d body=some%20content http://localhost:8080/save/Test -cmp Test.txt test_Test.txt.good || cleanup 1 -curl -s -o test_view.out http://localhost:8080/view/Test -cmp test_view.out test_view.good || cleanup 1 +sleep 1 -echo "Passed" -cleanup 0 +./get.bin http://$addr/edit/Test > test_edit.out +diff -u test_edit.out test_edit.good +./get.bin -post=body=some%20content http://$addr/save/Test +diff -u Test.txt test_Test.txt.good +./get.bin http://$addr/view/Test > test_view.out +diff -u test_view.out test_view.good +echo PASS diff --git a/doc/codelab/wiki/view.html b/doc/codelab/wiki/view.html index a46622d01..ca2ffc20b 100644 --- a/doc/codelab/wiki/view.html +++ b/doc/codelab/wiki/view.html @@ -1,5 +1,5 @@ -

{title}

+

{Title}

-

[edit]

+

[edit]

-
{body}
+
{Body}
diff --git a/doc/codelab/wiki/wiki.html b/doc/codelab/wiki/wiki.html index 919385edf..ff2c3088b 100644 --- a/doc/codelab/wiki/wiki.html +++ b/doc/codelab/wiki/wiki.html @@ -71,27 +71,27 @@ declaration.

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 a
 
 

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.

@@ -114,7 +114,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).

@@ -136,8 +136,8 @@ We will want to load pages, too:

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.

@@ -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 and os.Error. +function to return *Page and os.Error.

@@ -160,7 +160,7 @@ function to return *page and os.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 an os.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 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.

@@ -334,7 +334,7 @@ href="http://localhost:8080/view/test">http://localhost:8080/view/test -

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 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.

@@ -413,15 +413,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 &gt;). 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 @@ The 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. @@ -498,7 +498,7 @@ The function saveHandler will handle the form submission.

The page title (provided in the URL) and the form's only field, -body, are stored in a new page. +Body, are stored in a new Page. The save() method is then called to write the data to a file, and the client is redirected to the /view/ page.

@@ -506,7 +506,7 @@ and the client is redirected to the /view/ page.

The value returned by FormValue is of type string. We must convert that value to []byte before it will fit into -the page struct. We use []byte(body) to perform +the Page struct. We use []byte(body) to perform the conversion.

@@ -610,7 +610,7 @@ Then we can create a global variable to store our validation regexp:

-!./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 an

Now, 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 our TitleValidator expression:

@@ -708,7 +708,7 @@ The closure returned by makeHandler is a function that takes
 an http.ResponseWriter and http.Request (in other
 words, an http.HandlerFunc). 
 The closure extracts the title from the request path, and
-validates it with the titleValidator regexp. If the
+validates it with the TitleValidator regexp. If the
 title is invalid, an error will be written to the
 ResponseWriter using the http.NotFound function. 
 If the title is valid, the enclosed handler function
-- 
cgit v1.2.3