├── .gitignore ├── README.md └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | how-i-start-go 2 | 3 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 4 | *.o 5 | *.a 6 | *.so 7 | 8 | # Folders 9 | _obj 10 | _test 11 | 12 | # Architecture specific extensions/prefixes 13 | *.[568vq] 14 | [568vq].out 15 | 16 | *.cgo1.go 17 | *.cgo2.c 18 | _cgo_defun.c 19 | _cgo_gotypes.go 20 | _cgo_export.* 21 | 22 | _testmain.go 23 | 24 | *.exe 25 | *.test 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # How I start Go 2 | 3 | This repository holds the code that we build in the 4 | [How I Start](http://www.howistart.org/) article about 5 | [Go](http://www.howistart.org/posts/go/1). 6 | 7 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "flag" 6 | "log" 7 | "net/http" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | func main() { 13 | openWeatherMapAPIKey := flag.String("openweathermap.api.key", "0123456789abcdef", "openweathermap.org API key") 14 | wundergroundAPIKey := flag.String("wunderground.api.key", "0123456789abcdef", "wunderground.com API key") 15 | flag.Parse() 16 | 17 | mw := multiWeatherProvider{ 18 | openWeatherMap{apiKey: *openWeatherMapAPIKey}, 19 | weatherUnderground{apiKey: *wundergroundAPIKey}, 20 | } 21 | 22 | http.HandleFunc("/weather/", func(w http.ResponseWriter, r *http.Request) { 23 | begin := time.Now() 24 | city := strings.SplitN(r.URL.Path, "/", 3)[2] 25 | 26 | temp, err := mw.temperature(city) 27 | if err != nil { 28 | http.Error(w, err.Error(), http.StatusInternalServerError) 29 | return 30 | } 31 | 32 | w.Header().Set("Content-Type", "application/json; charset=utf-8") 33 | json.NewEncoder(w).Encode(map[string]interface{}{ 34 | "city": city, 35 | "temp": temp, 36 | "took": time.Since(begin).String(), 37 | }) 38 | }) 39 | 40 | http.ListenAndServe(":8080", nil) 41 | } 42 | 43 | type weatherProvider interface { 44 | temperature(city string) (float64, error) // in Kelvin, naturally 45 | } 46 | 47 | type multiWeatherProvider []weatherProvider 48 | 49 | func (w multiWeatherProvider) temperature(city string) (float64, error) { 50 | // Make a channel for temperatures, and a channel for errors. 51 | // Each provider will push a value into only one. 52 | temps := make(chan float64, len(w)) 53 | errs := make(chan error, len(w)) 54 | 55 | // For each provider, spawn a goroutine with an anonymous function. 56 | // That function will invoke the temperature method, and forward the response. 57 | for _, provider := range w { 58 | go func(p weatherProvider) { 59 | k, err := p.temperature(city) 60 | if err != nil { 61 | errs <- err 62 | return 63 | } 64 | temps <- k 65 | }(provider) 66 | } 67 | 68 | sum := 0.0 69 | 70 | // Collect a temperature or an error from each provider. 71 | for i := 0; i < len(w); i++ { 72 | select { 73 | case temp := <-temps: 74 | sum += temp 75 | case err := <-errs: 76 | return 0, err 77 | } 78 | } 79 | 80 | // Return the average, same as before. 81 | return sum / float64(len(w)), nil 82 | } 83 | 84 | type openWeatherMap struct { 85 | apiKey string 86 | } 87 | 88 | func (w openWeatherMap) temperature(city string) (float64, error) { 89 | resp, err := http.Get("http://api.openweathermap.org/data/2.5/weather?APPID=" + w.apiKey + "&q=" + city) 90 | if err != nil { 91 | return 0, err 92 | } 93 | 94 | defer resp.Body.Close() 95 | 96 | var d struct { 97 | Main struct { 98 | Kelvin float64 `json:"temp"` 99 | } `json:"main"` 100 | } 101 | 102 | if err := json.NewDecoder(resp.Body).Decode(&d); err != nil { 103 | return 0, err 104 | } 105 | 106 | log.Printf("openWeatherMap: %s: %.2f", city, d.Main.Kelvin) 107 | return d.Main.Kelvin, nil 108 | } 109 | 110 | type weatherUnderground struct { 111 | apiKey string 112 | } 113 | 114 | func (w weatherUnderground) temperature(city string) (float64, error) { 115 | resp, err := http.Get("http://api.wunderground.com/api/" + w.apiKey + "/conditions/q/" + city + ".json") 116 | if err != nil { 117 | return 0, err 118 | } 119 | 120 | defer resp.Body.Close() 121 | 122 | var d struct { 123 | Observation struct { 124 | Celsius float64 `json:"temp_c"` 125 | } `json:"current_observation"` 126 | } 127 | 128 | if err := json.NewDecoder(resp.Body).Decode(&d); err != nil { 129 | return 0, err 130 | } 131 | 132 | kelvin := d.Observation.Celsius + 273.15 133 | log.Printf("weatherUnderground: %s: %.2f", city, kelvin) 134 | return kelvin, nil 135 | } 136 | --------------------------------------------------------------------------------