diff options
Diffstat (limited to 'doc/articles/wiki')
| -rw-r--r-- | doc/articles/wiki/get.go | 19 | ||||
| -rw-r--r-- | doc/articles/wiki/index.html | 209 | ||||
| -rw-r--r-- | doc/articles/wiki/part3-errorhandling.go | 75 | ||||
| -rw-r--r-- | doc/articles/wiki/part3.go | 59 | ||||
| -rwxr-xr-x | doc/articles/wiki/test.bash | 7 | 
5 files changed, 261 insertions, 108 deletions
| diff --git a/doc/articles/wiki/get.go b/doc/articles/wiki/get.go index c6e9bf28b..b3e464b34 100644 --- a/doc/articles/wiki/get.go +++ b/doc/articles/wiki/get.go @@ -13,11 +13,13 @@ import (  	"net/http"  	"os"  	"strings" +	"time"  )  var (  	post = flag.String("post", "", "urlencoded form data to POST")  	addr = flag.Bool("addr", false, "find open address and print to stdout") +	wait = flag.Duration("wait_for_port", 0, "if non-zero, the amount of time to wait for the address to become available")  )  func main() { @@ -37,11 +39,18 @@ func main() {  	}  	var r *http.Response  	var err error -	if *post != "" { -		b := strings.NewReader(*post) -		r, err = http.Post(url, "application/x-www-form-urlencoded", b) -	} else { -		r, err = http.Get(url) +	loopUntil := time.Now().Add(*wait) +	for { +		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 || *wait == 0 || time.Now().After(loopUntil) { +			break +		} +		time.Sleep(100 * time.Millisecond)  	}  	if err != nil {  		log.Fatal(err) diff --git a/doc/articles/wiki/index.html b/doc/articles/wiki/index.html index 6c45d7178..ea3507f4d 100644 --- a/doc/articles/wiki/index.html +++ b/doc/articles/wiki/index.html @@ -46,7 +46,7 @@ $ cd gowiki  </pre>  <p> -Create a file named <code>wiki.go</code>, open it in your favorite editor, and  +Create a file named <code>wiki.go</code>, open it in your favorite editor, and  add the following lines:  </p> @@ -60,8 +60,8 @@ import (  </pre>  <p> -We import the <code>fmt</code> and <code>ioutil</code> packages from the Go  -standard library. Later, as we implement additional functionality, we will  +We import the <code>fmt</code> and <code>ioutil</code> packages from the Go +standard library. Later, as we implement additional functionality, we will  add more packages to this <code>import</code> declaration.  </p> @@ -77,7 +77,7 @@ the title and body.  {{code "doc/articles/wiki/part1.go" `/^type Page/` `/}/`}}  <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="/doc/articles/slices_usage_and_internals.html">Slices: usage and  internals</a> for more on slices.)  The <code>Body</code> element is a <code>[]byte</code> rather than @@ -86,8 +86,8 @@ libraries we will use, as you'll see below.  </p>  <p> -The <code>Page</code> struct describes how page data will be stored in memory.  -But what about persistent storage? We can address that by creating a  +The <code>Page</code> struct describes how page data will be stored in memory. +But what about persistent storage? We can address that by creating a  <code>save</code> method on <code>Page</code>:  </p> @@ -96,11 +96,11 @@ But what about persistent storage? We can address that by creating a  <p>  This method's signature reads: "This is a method named <code>save</code> that  takes as its receiver <code>p</code>, a pointer to <code>Page</code> . It takes -no parameters, and returns a value of type <code>error</code>."  +no parameters, and returns a value of type <code>error</code>."  </p>  <p> -This method will save the <code>Page</code>'s <code>Body</code> to a text  +This method will save the <code>Page</code>'s <code>Body</code> to a text  file. For simplicity, we will use the <code>Title</code> as the file name.  </p> @@ -110,35 +110,37 @@ that is the return type of <code>WriteFile</code> (a standard library function  that writes a byte slice to a file).  The <code>save</code> method returns the  error value, to let the application handle it should anything go wrong while  writing the file.  If all goes well, <code>Page.save()</code> will return -<code>nil</code> (the zero-value for pointers, interfaces, and some other  +<code>nil</code> (the zero-value for pointers, interfaces, and some other  types).  </p>  <p> -The octal integer constant <code>0600</code>, passed as the third parameter to +The octal integer literal <code>0600</code>, passed as the third parameter to  <code>WriteFile</code>, indicates that the file should be created with  read-write permissions for the current user only. (See the Unix man page  <code>open(2)</code> for details.)  </p>  <p> -We will want to load pages, too: +In addition to saving pages, we will want to load pages, too:  </p>  {{code "doc/articles/wiki/part1-noerror.go" `/^func loadPage/` `/^}/`}}  <p>  The function <code>loadPage</code> constructs the file name from -<code>Title</code>, reads the file's contents into a new -<code>Page</code>, and returns a pointer to that new <code>page</code>. +the title parameter, reads the file's contents into a new +variable <code>body</code>, and returns two values: a pointer to a +<code>Page</code> literal constructed with the proper title and body +values and <code>nil</code> for the error value.  </p>  <p> -Functions can return multiple values. The standard library function  -<code>io.ReadFile</code> returns <code>[]byte</code> and <code>error</code>.  +Functions can return multiple values. The standard library function +<code>io.ReadFile</code> returns <code>[]byte</code> and <code>error</code>.  In <code>loadPage</code>, error isn't being handled yet; the "blank identifier"  represented by the underscore (<code>_</code>) symbol is used to throw away the -error return value (in essence, assigning the value to nothing).  +error return value (in essence, assigning the value to nothing).  </p>  <p> @@ -152,7 +154,7 @@ function to return <code>*Page</code> and <code>error</code>.  <p>  Callers of this function can now check the second parameter; if it is  <code>nil</code> then it has successfully loaded a Page. If not, it will be an -<code>error</code> that can be handled by the caller (see the  +<code>error</code> that can be handled by the caller (see the  <a href="/ref/spec#Errors">language specification</a> for details).  </p> @@ -172,7 +174,7 @@ printed to the screen.  </p>  <p> -You can compile and run the program like this:  +You can compile and run the program like this:  </p>  <pre> @@ -182,7 +184,7 @@ This is a sample page.  </pre>  <p> -(If you're using Windows you must type "<code>wiki</code>" without the  +(If you're using Windows you must type "<code>wiki</code>" without the  "<code>./</code>" to run the program.)  </p> @@ -199,10 +201,10 @@ Here's a full working example of a simple web server:  {{code "doc/articles/wiki/http-sample.go"}}  <p> -The <code>main</code> function begins with a call to  -<code>http.HandleFunc</code>, which tells the <code>http</code> package to  -handle all requests to the web root (<code>"/"</code>) with  -<code>handler</code>.  +The <code>main</code> function begins with a call to +<code>http.HandleFunc</code>, which tells the <code>http</code> package to +handle all requests to the web root (<code>"/"</code>) with +<code>handler</code>.  </p>  <p> @@ -219,20 +221,20 @@ its arguments.  </p>  <p> -An <code>http.ResponseWriter</code> value assembles the HTTP server's response; by writing  +An <code>http.ResponseWriter</code> value assembles the HTTP server's response; by writing  to it, we send data to the HTTP client.  </p>  <p>  An <code>http.Request</code> is a data structure that represents the client -HTTP request.  The string <code>r.URL.Path</code> is the path component -of the request URL.  The trailing <code>[1:]</code> means -"create a sub-slice of <code>Path</code> from the 1st character to the end."  +HTTP request. <code>r.URL.Path</code> is the path component +of the request URL. The trailing <code>[1:]</code> means +"create a sub-slice of <code>Path</code> from the 1st character to the end."  This drops the leading "/" from the path name.  </p>  <p> -If you run this program and access the URL:  +If you run this program and access the URL:  </p>  <pre>http://localhost:8080/monkeys</pre>  <p> @@ -249,13 +251,14 @@ To use the <code>net/http</code> package, it must be imported:  <pre>  import (  	"fmt" -	<b>"net/http"</b>  	"io/ioutil" +	<b>"net/http"</b>  )  </pre>  <p> -Let's create a handler to view a wiki page:  +Let's create a handler, <code>viewHandler</code> that will allow users to +view a wiki page. It will handle URLs prefixed with "/view/".  </p>  {{code "doc/articles/wiki/part2.go" `/^const lenPath/`}} @@ -264,28 +267,28 @@ Let's create a handler to view a wiki page:  <p>  First, this function extracts the page title from <code>r.URL.Path</code>, -the path component of the request URL. The global constant  +the path component of the request URL. The global constant  <code>lenPath</code> is the length of the leading <code>"/view/"</code>  component of the request path. -The <code>Path</code> is re-sliced with <code>[lenPath:]</code> to drop the  -first 6 characters of the string. This is because the path will invariably  -begin with <code>"/view/"</code>, which is not part of the page title. +The <code>Path</code> is re-sliced with <code>[lenPath:]</code> to drop the +first 6 characters of the string. This is because the path will invariably +begin with <code>"/view/"</code>, which is not part of the page's title.  </p>  <p> -The function then loads the page data, formats the page with a string of simple  -HTML, and writes it to <code>w</code>, the <code>http.ResponseWriter</code>.  +The function then loads the page data, formats the page with a string of simple +HTML, and writes it to <code>w</code>, the <code>http.ResponseWriter</code>.  </p>  <p> -Again, note the use of <code>_</code> to ignore the <code>error</code>  +Again, note the use of <code>_</code> to ignore the <code>error</code>  return value from <code>loadPage</code>. This is done here for simplicity  and generally considered bad practice. We will attend to this later.  </p>  <p> -To use this handler, we create a <code>main</code> function that -initializes <code>http</code> using the <code>viewHandler</code> to handle +To use this handler, we rewrite our <code>main</code> function to +initialize <code>http</code> using the <code>viewHandler</code> to handle  any requests under the path <code>/view/</code>.  </p> @@ -311,6 +314,11 @@ $ ./wiki  </pre>  <p> +(If you're using Windows you must type "<code>wiki</code>" without the +"<code>./</code>" to run the program.) +</p> + +<p>  With this web server running, a visit to <code><a  href="http://localhost:8080/view/test">http://localhost:8080/view/test</a></code>  should show a page titled "test" containing the words "Hello world". @@ -326,14 +334,14 @@ form.  </p>  <p> -First, we add them to <code>main()</code>:  +First, we add them to <code>main()</code>:  </p>  {{code "doc/articles/wiki/final-noclosure.go" `/^func main/` `/^}/`}}  <p> -The function <code>editHandler</code> loads the page  -(or, if it doesn't exist, create an empty <code>Page</code> struct),  +The function <code>editHandler</code> loads the page +(or, if it doesn't exist, create an empty <code>Page</code> struct),  and displays an HTML form.  </p> @@ -343,7 +351,7 @@ and displays an HTML form.  This function will work fine, but all that hard-coded HTML is ugly.  Of course, there is a better way.  </p> -  +  <h2>The <code>html/template</code> package</h2>  <p> @@ -354,20 +362,20 @@ underlying Go code.  </p>  <p> -First, we must add <code>html/template</code> to the list of imports: +First, we must add <code>html/template</code> to the list of imports. We +also won't be using <code>fmt</code> anymore, so we have to remove that.  </p>  <pre>  import (  	<b>"html/template"</b> -	"http"  	"io/ioutil" -	"os" +	"net/http"  )  </pre>  <p> -Let's create a template file containing the HTML form.  +Let's create a template file containing the HTML form.  Open a new file named <code>edit.html</code>, and add the following lines:  </p> @@ -381,8 +389,8 @@ HTML:  {{code "doc/articles/wiki/final-noerror.go" `/^func editHandler/` `/^}/`}}  <p> -The function <code>template.ParseFiles</code> will read the contents of  -<code>edit.html</code> and return a <code>*template.Template</code>.  +The function <code>template.ParseFiles</code> will read the contents of +<code>edit.html</code> and return a <code>*template.Template</code>.  </p>  <p> @@ -405,12 +413,7 @@ HTML.  </p>  <p> -Now that we've removed the <code>fmt.Fprintf</code> statement, we can remove -<code>"fmt"</code> from the <code>import</code> list. -</p> - -<p> -While we're working with templates, let's create a template for our +Since we're working with templates now, let's create a template for our  <code>viewHandler</code> called <code>view.html</code>:  </p> @@ -428,28 +431,31 @@ handlers. Let's remove this duplication by moving the templating code  to its own function:  </p> +{{code "doc/articles/wiki/final-template.go" `/^func renderTemplate/` `/^}/`}}  {{code "doc/articles/wiki/final-template.go" `/^func viewHandler/` `/^}/`}}  {{code "doc/articles/wiki/final-template.go" `/^func editHandler/` `/^}/`}} -{{code "doc/articles/wiki/final-template.go" `/^func renderTemplate/` `/^}/`}}  <p> -The handlers are now shorter and simpler.  +If we comment out the registration of our unimplemented save handler in +<code>main</code>, we can once again build and test our program. +<a href="part3.go">Click here to view the code we've written so far.</a>  </p>  <h2>Handling non-existent pages</h2>  <p>  What if you visit <a href="http://localhost:8080/view/APageThatDoesntExist"> -<code>/view/APageThatDoesntExist</code></a>? The program will crash. This is  -because it ignores the error return value from <code>loadPage</code>. Instead, -if the requested Page doesn't exist, it should redirect the client to the edit -Page so the content may be created: +<code>/view/APageThatDoesntExist</code></a>? You'll see a page containing +HTML. This is because it ignores the error return value from +<code>loadPage</code> and continues to try and fill out the template +with no data. Instead, if the requested Page doesn't exist, it should +redirect the client to the edit Page so the content may be created:  </p> -{{code "doc/articles/wiki/final-noclosure.go" `/^func viewHandler/` `/^}/`}} +{{code "doc/articles/wiki/part3-errorhandling.go" `/^func viewHandler/` `/^}/`}}  <p> -The <code>http.Redirect</code> function adds an HTTP status code of  +The <code>http.Redirect</code> function adds an HTTP status code of  <code>http.StatusFound</code> (302) and a <code>Location</code>  header to the HTTP response.  </p> @@ -457,22 +463,24 @@ header to the HTTP response.  <h2>Saving Pages</h2>  <p> -The function <code>saveHandler</code> will handle the form submission.  +The function <code>saveHandler</code> will handle the submission of forms +located on the edit pages. After uncommenting the related line in +<code>main</code>, let's implement the the handler:  </p>  {{code "doc/articles/wiki/final-template.go" `/^func saveHandler/` `/^}/`}}  <p> -The page title (provided in the URL) and the form's only field,  -<code>Body</code>, are stored in a new <code>Page</code>.  +The page title (provided in the URL) and the form's only field, +<code>Body</code>, are stored in a new <code>Page</code>.  The <code>save()</code> method is then called to write the data to a file,  and the client is redirected to the <code>/view/</code> page.  </p>  <p>  The value returned by <code>FormValue</code> is of type <code>string</code>. -We must convert that value to <code>[]byte</code> before it will fit into  -the <code>Page</code> struct.  We use <code>[]byte(body)</code> to perform +We must convert that value to <code>[]byte</code> before it will fit into +the <code>Page</code> struct. We use <code>[]byte(body)</code> to perform  the conversion.  </p> @@ -481,9 +489,9 @@ the conversion.  <p>  There are several places in our program where errors are being ignored.  This  is bad practice, not least because when an error does occur the program will -crash.  A better solution is to handle the errors and return an error message -to the user. That way if something does go wrong, the server will continue to -function and the user will be notified. +have unintended behavior. A better solution is to handle the errors and return +an error message to the user. That way if something does go wrong, the server +will function exactly how we want and the user can be notified.  </p>  <p> @@ -493,7 +501,7 @@ First, let's handle the errors in <code>renderTemplate</code>:  {{code "doc/articles/wiki/final-parsetemplate.go" `/^func renderTemplate/` `/^}/`}}  <p> -The <code>http.Error</code> function sends a specified HTTP response code  +The <code>http.Error</code> function sends a specified HTTP response code  (in this case "Internal Server Error") and error message.  Already the decision to put this in a separate function is paying off.  </p> @@ -502,18 +510,18 @@ Already the decision to put this in a separate function is paying off.  Now let's fix up <code>saveHandler</code>:  </p> -{{code "doc/articles/wiki/final-noclosure.go" `/^func saveHandler/` `/^}/`}} +{{code "doc/articles/wiki/part3-errorhandling.go" `/^func saveHandler/` `/^}/`}}  <p> -Any errors that occur during <code>p.save()</code> will be reported  +Any errors that occur during <code>p.save()</code> will be reported  to the user.  </p>  <h2>Template caching</h2>  <p> -There is an inefficiency in this code: <code>renderTemplate</code> calls  -<code>ParseFiles</code> every time a page is rendered.  +There is an inefficiency in this code: <code>renderTemplate</code> calls +<code>ParseFiles</code> every time a page is rendered.  A better approach would be to call <code>ParseFiles</code> once at program  initialization, parsing all templates into a single <code>*Template</code>.  Then we can use the @@ -536,10 +544,11 @@ can't be loaded the only sensible thing to do is exit the program.  </p>  <p> -A <code>for</code> loop is used with a <code>range</code> statement to iterate  -over an array constant containing the names of the templates we want parsed. -If we were to add more templates to our program, we would add their names to  -that array. +The <code>ParseFiles</code> function takes any number of string arguments that +identify our template files, and parses those files into templates that are +named after the base file name. If we were to add more templates to our +program, we would add their names to the <code>ParseFiles</code> call's +arguments.  </p>  <p> @@ -571,25 +580,27 @@ Then we can create a global variable to store our validation regexp:  {{code "doc/articles/wiki/final-noclosure.go" `/^var titleValidator/`}}  <p> -The function <code>regexp.MustCompile</code> will parse and compile the  -regular expression, and return a <code>regexp.Regexp</code>.  +The function <code>regexp.MustCompile</code> will parse and compile the +regular expression, and return a <code>regexp.Regexp</code>.  <code>MustCompile</code> is distinct from <code>Compile</code> in that it will  panic if the expression compilation fails, while <code>Compile</code> returns -an <code>error</code> as a second parameter.  +an <code>error</code> as a second parameter.  </p>  <p> -Now, let's write a function that extracts the title string from the request  -URL, and tests it against our <code>TitleValidator</code> expression: +Now, let's write a function, <code>getTitle</code>, that extracts the title +string from the request URL, and tests it against our +<code>TitleValidator</code> expression:  </p>  {{code "doc/articles/wiki/final-noclosure.go" `/func getTitle/` `/^}/`}}  <p>  If the title is valid, it will be returned along with a <code>nil</code> -error value.  If the title is invalid, the function will write a  -"404 Not Found" error to the HTTP connection, and return an error to the  -handler.  +error value. If the title is invalid, the function will write a +"404 Not Found" error to the HTTP connection, and return an error to the +handler. To create a new error, we have to import the <code>errors</code> +package.  </p>  <p> @@ -604,10 +615,10 @@ Let's put a call to <code>getTitle</code> in each of the handlers:  <p>  Catching the error condition in each handler introduces a lot of repeated code. -What if we could wrap each of the handlers in a function that does this  -validation and error checking? Go's  -<a href="/ref/spec#Function_declarations">function  -literals</a> provide a powerful means of abstracting functionality  +What if we could wrap each of the handlers in a function that does this +validation and error checking? Go's +<a href="/ref/spec#Function_declarations">function +literals</a> provide a powerful means of abstracting functionality  that can help us here.  </p> @@ -654,19 +665,19 @@ Now we can take the code from <code>getTitle</code> and use it here  <p>  The closure returned by <code>makeHandler</code> is a function that takes  an <code>http.ResponseWriter</code> and <code>http.Request</code> (in other -words, an <code>http.HandlerFunc</code>).  +words, an <code>http.HandlerFunc</code>).  The closure extracts the <code>title</code> from the request path, and  validates it with the <code>TitleValidator</code> regexp. If the  <code>title</code> is invalid, an error will be written to the -<code>ResponseWriter</code> using the <code>http.NotFound</code> function.  +<code>ResponseWriter</code> using the <code>http.NotFound</code> function.  If the <code>title</code> is valid, the enclosed handler function  <code>fn</code> will be called with the <code>ResponseWriter</code>,  <code>Request</code>, and <code>title</code> as arguments.  </p>  <p> -Now we can wrap the handler functions with <code>makeHandler</code> in  -<code>main</code>, before they are registered with the <code>http</code>  +Now we can wrap the handler functions with <code>makeHandler</code> in +<code>main</code>, before they are registered with the <code>http</code>  package:  </p> @@ -698,7 +709,7 @@ $ ./wiki  <p>  Visiting <a href="http://localhost:8080/view/ANewPage">http://localhost:8080/view/ANewPage</a> -should present you with the page edit form. You should then be able to  +should present you with the page edit form. You should then be able to  enter some text, click 'Save', and be redirected to the newly created page.  </p> @@ -710,11 +721,11 @@ Here are some simple tasks you might want to tackle on your own:  <ul>  <li>Store templates in <code>tmpl/</code> and page data in <code>data/</code>. -<li>Add a handler to make the web root redirect to  +<li>Add a handler to make the web root redirect to  	<code>/view/FrontPage</code>.</li>  <li>Spruce up the page templates by making them valid HTML and adding some  	CSS rules.</li> -<li>Implement inter-page linking by converting instances of  +<li>Implement inter-page linking by converting instances of  	<code>[PageName]</code> to <br>  	<code><a href="/view/PageName">PageName</a></code>.  	(hint: you could use <code>regexp.ReplaceAllFunc</code> to do this) diff --git a/doc/articles/wiki/part3-errorhandling.go b/doc/articles/wiki/part3-errorhandling.go new file mode 100644 index 000000000..945aa1e39 --- /dev/null +++ b/doc/articles/wiki/part3-errorhandling.go @@ -0,0 +1,75 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( +	"html/template" +	"io/ioutil" +	"net/http" +) + +type Page struct { +	Title string +	Body  []byte +} + +func (p *Page) save() error { +	filename := p.Title + ".txt" +	return ioutil.WriteFile(filename, p.Body, 0600) +} + +func loadPage(title string) (*Page, error) { +	filename := title + ".txt" +	body, err := ioutil.ReadFile(filename) +	if err != nil { +		return nil, err +	} +	return &Page{Title: title, Body: body}, nil +} + +const lenPath = len("/view/") + +func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { +	t, _ := template.ParseFiles(tmpl + ".html") +	t.Execute(w, p) +} + +func viewHandler(w http.ResponseWriter, r *http.Request) { +	title := r.URL.Path[lenPath:] +	p, err := loadPage(title) +	if err != nil { +		http.Redirect(w, r, "/edit/"+title, http.StatusFound) +		return +	} +	renderTemplate(w, "view", p) +} + +func editHandler(w http.ResponseWriter, r *http.Request) { +	title := r.URL.Path[lenPath:] +	p, err := loadPage(title) +	if err != nil { +		p = &Page{Title: title} +	} +	renderTemplate(w, "edit", p) +} + +func saveHandler(w http.ResponseWriter, r *http.Request) { +	title := r.URL.Path[lenPath:] +	body := r.FormValue("body") +	p := &Page{Title: title, Body: []byte(body)} +	err := p.save() +	if err != nil { +		http.Error(w, err.Error(), http.StatusInternalServerError) +		return +	} +	http.Redirect(w, r, "/view/"+title, http.StatusFound) +} + +func main() { +	http.HandleFunc("/view/", viewHandler) +	http.HandleFunc("/edit/", editHandler) +	http.HandleFunc("/save/", saveHandler) +	http.ListenAndServe(":8080", nil) +} diff --git a/doc/articles/wiki/part3.go b/doc/articles/wiki/part3.go new file mode 100644 index 000000000..7fe4351af --- /dev/null +++ b/doc/articles/wiki/part3.go @@ -0,0 +1,59 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( +	"html/template" +	"io/ioutil" +	"net/http" +) + +type Page struct { +	Title string +	Body  []byte +} + +func (p *Page) save() error { +	filename := p.Title + ".txt" +	return ioutil.WriteFile(filename, p.Body, 0600) +} + +func loadPage(title string) (*Page, error) { +	filename := title + ".txt" +	body, err := ioutil.ReadFile(filename) +	if err != nil { +		return nil, err +	} +	return &Page{Title: title, Body: body}, nil +} + +const lenPath = len("/view/") + +func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { +	t, _ := template.ParseFiles(tmpl + ".html") +	t.Execute(w, p) +} + +func viewHandler(w http.ResponseWriter, r *http.Request) { +	title := r.URL.Path[lenPath:] +	p, _ := loadPage(title) +	renderTemplate(w, "view", p) +} + +func editHandler(w http.ResponseWriter, r *http.Request) { +	title := r.URL.Path[lenPath:] +	p, err := loadPage(title) +	if err != nil { +		p = &Page{Title: title} +	} +	renderTemplate(w, "edit", p) +} + +func main() { +	http.HandleFunc("/view/", viewHandler) +	http.HandleFunc("/edit/", editHandler) +	//http.HandleFunc("/save/", saveHandler) +	http.ListenAndServe(":8080", nil) +} diff --git a/doc/articles/wiki/test.bash b/doc/articles/wiki/test.bash index 5c2cb60dc..02ed1894a 100755 --- a/doc/articles/wiki/test.bash +++ b/doc/articles/wiki/test.bash @@ -18,11 +18,10 @@ go build -o final-test.bin final-test.go  (./final-test.bin) &  wiki_pid=$! -sleep 1 - -./get.bin http://$addr/edit/Test > test_edit.out +./get.bin --wait_for_port=5s 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 +./get.bin -post=body=some%20content http://$addr/save/Test > test_save.out +diff -u test_save.out test_view.good # should be the same as viewing  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 | 
