|
// package main is the entry point into the application. |
|
// |
|
// Packages are used to bundle common pieces of code together for the purposes |
|
// of application architecture or for reusablility in other applications. The |
|
// package name should be the first line of the file after any initial comment |
|
// |
|
// These comments in the code are used to generate the documentation which can |
|
// be made available via the godoc tool. These particular comments will not |
|
// show as this is the main package however. For further information see |
|
// |
|
// https://blog.golang.org/godoc-documenting-go-code |
|
package main |
|
|
|
import ( |
|
// These three packages are both from the standard library, the |
|
// documentation for which can be found at |
|
// |
|
// https://golang.org/pkg/ |
|
// |
|
// To import packages, they can be easily pulled from source control |
|
// via the go get command |
|
// e.g. go get github.com/tyndyll/alexa |
|
// which will fetch a package to work with Alexa requests in Go, and |
|
// which when imported could be accessed via alexa.FunctionName(). |
|
// Imports can also be aliased in the case where there are packages |
|
// with the same name |
|
"fmt" |
|
"log" |
|
"net/http" |
|
) |
|
|
|
// Printer defines the print interface |
|
// |
|
// Interfaces in Go contain a list of functions that a type must implement |
|
// in order to satisfy it. Any type that satisfies an interface can be |
|
// passed to a function that takes the interface as a parameter. |
|
type Printer interface { |
|
// Print outputs the provided string. There are no return values |
|
Print(string) |
|
} |
|
|
|
// linePrinter is the first type we will use to implement the Print interface |
|
// It is a simple printer that will simply output the passed string to a log |
|
// |
|
// Note that the linePrinter type starts with a lower case 'l'. In Go types and |
|
// functions that start with a lower case letter are private, while types and |
|
// functions that start with an upper case letter are Public, and available |
|
// outside of the package (and in the documentation) |
|
type linePrinter struct{} |
|
|
|
// Print takes a string and does not return anything. The Print function is |
|
// defined onto a pointer to the linePrinter type. 'p' is roughly equivalent to |
|
// self, but is rarely if ever called that. For more information see |
|
// https://golang.org/doc/effective_go.html#methods |
|
func (p *linePrinter) Print(name string) { |
|
log.Printf(p.BuildString(name)) |
|
} |
|
|
|
// BuildString takes a name, and returns a combined string. This isn't in the |
|
// original Printer interface, but that doesn't matter. The check as to whether |
|
// it conforms to the interface is just the list of methods that the interface |
|
// defines |
|
func (p *linePrinter) BuildString(name string) string { |
|
return fmt.Sprintf("Hello %s\n", name) |
|
} |
|
|
|
// GreeterService is the type that will contain a function that will provide |
|
// the HTTP handler. We could just use a function, but we want to have access |
|
// to a variable without relying on a global |
|
type GreeterService struct { |
|
// ToPrinter is a field on the struct, which contains a channel through |
|
// which strings can be passed. Fields are accessed via a dot notation |
|
// If no value is assigned to it, it will automatically be given a |
|
// nil value |
|
ToPrinter chan string |
|
} |
|
|
|
// SayHello is a function which implements the HTTP Handler interface. |
|
// https://golang.org/pkg/net/http/#Handler |
|
// The ResponseWriter is an instance of type, while the request is a pointer |
|
// to it |
|
func (g *GreeterService) SayHello(w http.ResponseWriter, r *http.Request) { |
|
// name is using the short declaration form to create a new variable. It |
|
// tells the compiler, take the type of the value that is returned from |
|
// the right hand side, and create a variable called name with that type |
|
// |
|
// We can see that we are accessing the URL field of the request. From |
|
// that URL we are calling the Query function to get a Values type, and |
|
// then the Get method. This only works up to the Get call if the function |
|
// has a single return |
|
name := r.URL.Query().Get("name") |
|
// There are no brackets around the if statement, and the { is required |
|
// on this line to terminate the statement |
|
if name == "" { |
|
// I'm sorry, I watch a lot of CSI while I'm working |
|
http.Error(w, "Who are you? (who who, who who)", http.StatusBadRequest) |
|
return |
|
} |
|
|
|
// We have a name, so we want to pass that through to the printer to let |
|
// it do what ever it is that it does. This type has no idea what that |
|
// could be and doesn't really care. The only time this would be a concern |
|
// is if the channel becomes full, at which point this call will block |
|
g.ToPrinter <- name |
|
|
|
// This type of if statement is very common in Go, where the if statement |
|
// is tested, and then a check performed after the semi colon. These can |
|
// be chained with && and || |
|
// |
|
// The _ variable throws away the value that is assigned to it (in this case |
|
// the number of bytes written). Go refuses to compile if there are variables |
|
// that are declared and not used, so it is necessary to throw them away or |
|
// delete them |
|
if _, err := w.Write([]byte("Hello " + name)); err != nil { |
|
http.Error(w, "Server Write Error", http.StatusInternalServerError) |
|
return |
|
} |
|
// The function automatically returns |
|
} |
|
|
|
// main is the entry point to the program. It takes no arguments and does not |
|
// return anything. Any command line arguments are available via the os.Args |
|
// variable (which requires importing the os package) |
|
func main() { |
|
// We are creating a channel that will contain strings, giving it a |
|
// buffer size of 10. The make keyword properly creates the structure |
|
// and sizes it appropriately |
|
// |
|
// Channels are a convenient way to pass information through goroutines |
|
// without having to rely on locks and mutexes. |
|
// https://golang.org/doc/effective_go.html#channels |
|
printerChannel := make(chan string, 10) |
|
|
|
// Create an instance of the linePrinter type. We are creating a |
|
// pointer to the struct by asking for it's address (&). |
|
printer := &linePrinter{} |
|
|
|
// We are creating an anonymous function and then running it in a |
|
// goroutine (https://golang.org/doc/effective_go.html#goroutines) |
|
// |
|
// The function accepts an argument which satisfies the Printer |
|
// interface |
|
|
|
// At this point this function will be executed in the background |
|
// and the program will continue |
|
go func(p Printer) { |
|
// This is the long form declaration. It is possible to assign |
|
// a value here (e.g. var name string = "Tyndyll") but in this |
|
// instance name will be set to "" |
|
var name string |
|
// We want this loop to run forever |
|
for { |
|
// Using the channel defined above, we use the <- |
|
// notation to take a value from the channel, if one |
|
// is available. If the channel is empty this request |
|
// will block. |
|
name = <-printerChannel |
|
|
|
// Call the Print method. Attempts to call the other |
|
// Public method BuildString would fail, because we have |
|
// explicitely passed through and typed it as the |
|
// Printer interface, which only has the Print method. |
|
// To access the other methods we could cast this to the |
|
// appropriate type. |
|
// |
|
// What is important to realise here, is that we could |
|
// have a type, which prints the result to an actual |
|
// printer, or emails it, or displays it on a stadium |
|
// jumbotron. We don't care what the type is, as long |
|
// as it satisfies the interface |
|
p.Print(name) |
|
} |
|
}(printer) |
|
// The goroutine is now being started |
|
|
|
// Create an instance of a pointer to a GreeterService |
|
g := &GreeterService{ |
|
// Set the initial value of the field. Notice the comma at the |
|
// end of the line. This is another of Go's style idioms, which |
|
// declares that you're always going to add another field |
|
// eventually, lets add the comma and avoid the error later |
|
ToPrinter: printerChannel, |
|
} |
|
|
|
// For the route "/", take use the function pointed to here. |
|
http.HandleFunc("/", g.SayHello) |
|
|
|
// GO! This starts the HTTP server on port 8080. There is also a TLS server |
|
// available that operates in the same manner |
|
http.ListenAndServe(":8080", nil) |
|
} |