// 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) }