├── .gitignore ├── LICENSE ├── README.md ├── chapter1 ├── channel.go ├── coverage.out ├── goroutine.go ├── hello │ ├── hello.go │ └── hello_test.go ├── http_get.go ├── inigo.go ├── read_status.go ├── returns.go └── returns2.go ├── chapter10 ├── closing_connections.go ├── grpc_client.go ├── grpc_server.go ├── hellopb │ ├── hello.pb.go │ └── hello.proto ├── json.go ├── multiple_connections.go ├── protobuf_client.go ├── protobuf_server.go ├── user │ ├── user.go │ └── user_generated.go └── userpb │ ├── user.pb.go │ └── user.proto ├── chapter11 ├── annotations │ ├── config.ini │ ├── json.go │ ├── jsonxml │ │ └── jsonxml.go │ └── load.go ├── generator │ ├── myint.go │ ├── myint_queue.go │ ├── queue │ ├── queue.go │ └── simple.go ├── interfaces │ ├── implements.go │ └── typeassert.go ├── structs │ └── structwalker.go └── values │ └── sumkind.go ├── chapter2 ├── callback_shutdown.go ├── conf.ini ├── conf.json ├── conf.yaml ├── count_cli.go ├── env_config.go ├── flag_cli.go ├── go_flags_example.go ├── hello_cli.go ├── ini_config.go ├── json_config.go ├── manners_shutdown.go ├── multiple_handlers.go ├── path_handlers.go ├── regex_handlers.go └── yaml_config.go ├── chapter3 ├── closing │ ├── bad.go │ ├── close-sender.go │ └── okay.go ├── closures │ └── simple.go ├── echoback │ └── echoback.go ├── echoredux │ └── echoredux.go ├── race │ ├── 1.txt │ ├── 2.txt │ ├── 3.txt │ ├── 4.txt │ ├── 5.txt │ ├── 6.txt │ ├── README │ ├── fixed.go │ └── race.go ├── scratch │ ├── closingstuff.go │ ├── main.go │ └── out.txt ├── simplelock │ └── lock.go └── waitgroup │ ├── exampledata │ ├── example1.txt │ ├── example1.txt.gz │ ├── example2.txt │ ├── example2.txt.gz │ ├── example3.txt │ └── example3.txt.gz │ ├── simple_gz.go │ ├── wg │ └── wg.go ├── chapter3_original_depenency_management ├── README.md ├── mygodep │ └── main.go └── myproject │ ├── glide.yaml │ └── history.txt ├── chapter4 ├── closure_scope.go ├── data.csv ├── error_example.go ├── http_server.go ├── parser_error.go ├── proper_panic.go ├── recover_panic.go ├── safely │ └── safely.go ├── safely_example.go ├── same_error.go ├── simple_defer.go ├── simple_server.go ├── simple_server_start.go ├── two_defers.go ├── two_errors.go ├── useful_recover.go └── zero_divider.go ├── chapter5 ├── delvetest │ ├── .dbg_history │ ├── debug │ ├── delvetest │ ├── main │ └── main.go ├── logs │ ├── buffered_logger.go │ ├── log.txt │ ├── log_client.go │ ├── log_server.go │ ├── outfile.go │ ├── simple.go │ ├── syslog.go │ ├── syslog_logger.go │ └── udp_logger.go ├── stack │ └── trace.go └── tests │ ├── bench │ └── bench_test.go │ ├── canary │ └── canary_test.go │ ├── generative │ └── generative_test.go │ ├── hello │ ├── unit.go │ └── unit_test.go │ └── msg │ ├── send_message.go │ └── send_message_test.go ├── chapter6 ├── buffered_template.go ├── cache_template.go ├── date_command.go ├── email.go ├── inherit_templates │ ├── base.html │ ├── inherit.go │ ├── page.html │ └── user.html ├── nested_templates │ ├── head.html │ ├── index.html │ └── nested_templates.go ├── object_templates │ ├── index.html │ ├── object_templates.go │ └── quote.html ├── simple_template.go └── templates │ └── simple.html ├── chapter7 ├── cache_serving.go ├── embedded_files │ └── embedded_files.go ├── file.html ├── file_increment_save.go ├── file_increment_save_notes.go ├── file_mime_type.go ├── file_multiple.html ├── file_not_found.go ├── file_plus.html ├── file_serving.go ├── files │ ├── readme.txt │ └── sub │ │ └── readme.txt ├── form_file.go ├── form_file_multiple.go ├── one_level_serving.go ├── serve_alternate_location.go ├── serve_subdir_simple.go └── servefile.go ├── chapter8 ├── custom_client_timeout.go ├── custom_request_delete.go ├── get_custom_error.go ├── get_semantic_version_api.go ├── http_custom_error.go ├── http_error.go ├── http_timeout_handling.go ├── parse_arbitrary_json.go ├── semantic_version_api.go ├── simple_get.go ├── simple_json_parser.go └── versioned_url_api.go └── chapter9 ├── api_interface.go ├── api_interface_with_errors.go ├── detect_dep.go ├── hostlookup.go └── runtime.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.sublime-project 2 | *.sublime-workspace -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2015, Matt Butcher and Matt Farina 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go in Practice 2 | 3 | The source code repository for Manning Publications [Go in Practice](http://manning.com/butcher/). 4 | 5 | The source here is uncommented. To understand the source you'll need the 6 | commentary that goes along with it in the book. -------------------------------------------------------------------------------- /chapter1/channel.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func printCount(c chan int) { 9 | num := 0 10 | for num >= 0 { 11 | num = <-c 12 | fmt.Print(num, " ") 13 | } 14 | } 15 | 16 | func main() { 17 | c := make(chan int) 18 | a := []int{8, 6, 7, 5, 3, 0, 9, -1} 19 | 20 | go printCount(c) 21 | 22 | for _, v := range a { 23 | c <- v 24 | } 25 | time.Sleep(time.Millisecond * 1) 26 | fmt.Println("End of main") 27 | } 28 | -------------------------------------------------------------------------------- /chapter1/coverage.out: -------------------------------------------------------------------------------- 1 | mode: set 2 | go-in-practice/ch1/hello.go:5.23,7.2 1 1 3 | go-in-practice/ch1/hello.go:9.13,12.2 2 0 4 | -------------------------------------------------------------------------------- /chapter1/goroutine.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func count() { 9 | for i := 0; i < 5; i++ { 10 | fmt.Println(i) 11 | time.Sleep(time.Millisecond * 1) 12 | } 13 | } 14 | 15 | func main() { 16 | go count() 17 | time.Sleep(time.Millisecond * 2) 18 | fmt.Println("Hello World") 19 | time.Sleep(time.Millisecond * 5) 20 | } 21 | -------------------------------------------------------------------------------- /chapter1/hello/hello.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func getName() string { 6 | return "World!" 7 | } 8 | 9 | func main() { 10 | name := getName() 11 | fmt.Println("Hello ", name) 12 | } 13 | -------------------------------------------------------------------------------- /chapter1/hello/hello_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestName(t *testing.T) { 6 | name := getName() 7 | 8 | if name != "World!" { 9 | t.Error("Respone from getName is unexpected value") 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /chapter1/http_get.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | ) 8 | 9 | func main() { 10 | resp, _ := http.Get("http://example.com/") 11 | body, _ := ioutil.ReadAll(resp.Body) 12 | fmt.Println(string(body)) 13 | resp.Body.Close() 14 | } 15 | -------------------------------------------------------------------------------- /chapter1/inigo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | ) 7 | 8 | func hello(res http.ResponseWriter, req *http.Request) { 9 | fmt.Fprint(res, "Hello, my name is Inigo Montoya") 10 | } 11 | 12 | func main() { 13 | http.HandleFunc("/", hello) 14 | http.ListenAndServe("localhost:4000", nil) 15 | } 16 | -------------------------------------------------------------------------------- /chapter1/read_status.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "net" 7 | ) 8 | 9 | func main() { 10 | conn, _ := net.Dial("tcp", "golang.org:80") 11 | fmt.Fprintf(conn, "GET / HTTP/1.0\r\n\r\n") 12 | status, _ := bufio.NewReader(conn).ReadString('\n') 13 | fmt.Println(status) 14 | } 15 | -------------------------------------------------------------------------------- /chapter1/returns.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func Names() (string, string) { 8 | return "Foo", "Bar" 9 | } 10 | 11 | func main() { 12 | n1, n2 := Names() 13 | fmt.Println(n1, n2) 14 | 15 | n3, _ := Names() 16 | fmt.Println(n3) 17 | } 18 | -------------------------------------------------------------------------------- /chapter1/returns2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func Names() (first string, second string) { 8 | first = "Foo" 9 | second = "Bar" 10 | return 11 | } 12 | 13 | func main() { 14 | n1, n2 := Names() 15 | fmt.Println(n1, n2) 16 | } 17 | -------------------------------------------------------------------------------- /chapter10/closing_connections.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | "os" 8 | ) 9 | 10 | func main() { 11 | r, err := http.Get("http://mattfarina.com") 12 | if err != nil { 13 | fmt.Println(err) 14 | os.Exit(1) 15 | } 16 | o, err := ioutil.ReadAll(r.Body) 17 | if err != nil { 18 | fmt.Println(err) 19 | os.Exit(1) 20 | } 21 | r.Body.Close() 22 | fmt.Println(string(o)) 23 | 24 | r2, err := http.Get("http://mattfarina.com/about") 25 | if err != nil { 26 | fmt.Println(err) 27 | os.Exit(1) 28 | } 29 | o, err = ioutil.ReadAll(r2.Body) 30 | if err != nil { 31 | fmt.Println(err) 32 | os.Exit(1) 33 | } 34 | r2.Body.Close() 35 | fmt.Println(string(o)) 36 | } 37 | -------------------------------------------------------------------------------- /chapter10/grpc_client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | pb "github.com/Masterminds/go-in-practice/chapter10/hellopb" 8 | "golang.org/x/net/context" 9 | "google.golang.org/grpc" 10 | ) 11 | 12 | func main() { 13 | address := "localhost:55555" 14 | conn, err := grpc.Dial(address, grpc.WithInsecure()) 15 | if err != nil { 16 | fmt.Println("unable to connect:", err) 17 | os.Exit(1) 18 | } 19 | defer conn.Close() 20 | c := pb.NewHelloClient(conn) 21 | 22 | name := "Inigo Montoya" 23 | hr := &pb.HelloRequest{Name: name} 24 | r, err := c.Say(context.Background(), hr) 25 | if err != nil { 26 | fmt.Println("could not say:", err) 27 | os.Exit(1) 28 | } 29 | fmt.Println(r.Message) 30 | } 31 | -------------------------------------------------------------------------------- /chapter10/grpc_server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net" 6 | 7 | pb "github.com/Masterminds/go-in-practice/chapter10/hellopb" 8 | "golang.org/x/net/context" 9 | "google.golang.org/grpc" 10 | ) 11 | 12 | type server struct{} 13 | 14 | func (s *server) Say(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) { 15 | msg := "Hello " + in.Name + "!" 16 | return &pb.HelloResponse{Message: msg}, nil 17 | } 18 | 19 | func main() { 20 | l, err := net.Listen("tcp", ":55555") 21 | if err != nil { 22 | log.Fatalf("failed to listen for tcp: %s", err) 23 | } 24 | s := grpc.NewServer() 25 | pb.RegisterHelloServer(s, &server{}) 26 | s.Serve(l) 27 | } 28 | -------------------------------------------------------------------------------- /chapter10/hellopb/hello.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. 2 | // source: hello.proto 3 | // DO NOT EDIT! 4 | 5 | /* 6 | Package chapter10 is a generated protocol buffer package. 7 | 8 | It is generated from these files: 9 | hello.proto 10 | 11 | It has these top-level messages: 12 | HelloRequest 13 | HelloResponse 14 | */ 15 | package chapter10 16 | 17 | import proto "github.com/golang/protobuf/proto" 18 | import fmt "fmt" 19 | import math "math" 20 | 21 | import ( 22 | context "golang.org/x/net/context" 23 | grpc "google.golang.org/grpc" 24 | ) 25 | 26 | // Reference imports to suppress errors if they are not otherwise used. 27 | var _ = proto.Marshal 28 | var _ = fmt.Errorf 29 | var _ = math.Inf 30 | 31 | // This is a compile-time assertion to ensure that this generated file 32 | // is compatible with the proto package it is being compiled against. 33 | const _ = proto.ProtoPackageIsVersion1 34 | 35 | type HelloRequest struct { 36 | Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` 37 | } 38 | 39 | func (m *HelloRequest) Reset() { *m = HelloRequest{} } 40 | func (m *HelloRequest) String() string { return proto.CompactTextString(m) } 41 | func (*HelloRequest) ProtoMessage() {} 42 | func (*HelloRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } 43 | 44 | type HelloResponse struct { 45 | Message string `protobuf:"bytes,1,opt,name=message" json:"message,omitempty"` 46 | } 47 | 48 | func (m *HelloResponse) Reset() { *m = HelloResponse{} } 49 | func (m *HelloResponse) String() string { return proto.CompactTextString(m) } 50 | func (*HelloResponse) ProtoMessage() {} 51 | func (*HelloResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } 52 | 53 | func init() { 54 | proto.RegisterType((*HelloRequest)(nil), "chapter10.HelloRequest") 55 | proto.RegisterType((*HelloResponse)(nil), "chapter10.HelloResponse") 56 | } 57 | 58 | // Reference imports to suppress errors if they are not otherwise used. 59 | var _ context.Context 60 | var _ grpc.ClientConn 61 | 62 | // Client API for Hello service 63 | 64 | type HelloClient interface { 65 | Say(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error) 66 | } 67 | 68 | type helloClient struct { 69 | cc *grpc.ClientConn 70 | } 71 | 72 | func NewHelloClient(cc *grpc.ClientConn) HelloClient { 73 | return &helloClient{cc} 74 | } 75 | 76 | func (c *helloClient) Say(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error) { 77 | out := new(HelloResponse) 78 | err := grpc.Invoke(ctx, "/chapter10.Hello/Say", in, out, c.cc, opts...) 79 | if err != nil { 80 | return nil, err 81 | } 82 | return out, nil 83 | } 84 | 85 | // Server API for Hello service 86 | 87 | type HelloServer interface { 88 | Say(context.Context, *HelloRequest) (*HelloResponse, error) 89 | } 90 | 91 | func RegisterHelloServer(s *grpc.Server, srv HelloServer) { 92 | s.RegisterService(&_Hello_serviceDesc, srv) 93 | } 94 | 95 | func _Hello_Say_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { 96 | in := new(HelloRequest) 97 | if err := dec(in); err != nil { 98 | return nil, err 99 | } 100 | out, err := srv.(HelloServer).Say(ctx, in) 101 | if err != nil { 102 | return nil, err 103 | } 104 | return out, nil 105 | } 106 | 107 | var _Hello_serviceDesc = grpc.ServiceDesc{ 108 | ServiceName: "chapter10.Hello", 109 | HandlerType: (*HelloServer)(nil), 110 | Methods: []grpc.MethodDesc{ 111 | { 112 | MethodName: "Say", 113 | Handler: _Hello_Say_Handler, 114 | }, 115 | }, 116 | Streams: []grpc.StreamDesc{}, 117 | } 118 | 119 | var fileDescriptor0 = []byte{ 120 | // 139 bytes of a gzipped FileDescriptorProto 121 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0xce, 0x48, 0xcd, 0xc9, 122 | 0xc9, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x4c, 0xce, 0x48, 0x2c, 0x28, 0x49, 0x2d, 123 | 0x32, 0x34, 0x50, 0x52, 0xe2, 0xe2, 0xf1, 0x00, 0xc9, 0x04, 0xa5, 0x16, 0x96, 0xa6, 0x16, 0x97, 124 | 0x08, 0x09, 0x71, 0xb1, 0xe4, 0x25, 0xe6, 0xa6, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x06, 0x81, 125 | 0xd9, 0x4a, 0x9a, 0x5c, 0xbc, 0x50, 0x35, 0xc5, 0x05, 0xf9, 0x79, 0xc5, 0xa9, 0x42, 0x12, 0x5c, 126 | 0xec, 0xb9, 0xa9, 0xc5, 0xc5, 0x89, 0xe9, 0x30, 0x75, 0x30, 0xae, 0x91, 0x33, 0x17, 0x2b, 0x58, 127 | 0xa9, 0x90, 0x15, 0x17, 0x73, 0x70, 0x62, 0xa5, 0x90, 0xb8, 0x1e, 0xdc, 0x2a, 0x3d, 0x64, 0x7b, 128 | 0xa4, 0x24, 0x30, 0x25, 0x20, 0x86, 0x2b, 0x31, 0x24, 0xb1, 0x81, 0x5d, 0x69, 0x0c, 0x08, 0x00, 129 | 0x00, 0xff, 0xff, 0x07, 0xf3, 0x53, 0x0a, 0xb4, 0x00, 0x00, 0x00, 130 | } 131 | -------------------------------------------------------------------------------- /chapter10/hellopb/hello.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package chapter10; 4 | 5 | service Hello { 6 | rpc Say (HelloRequest) returns (HelloResponse) {} 7 | } 8 | 9 | message HelloRequest { 10 | string name = 1; 11 | } 12 | 13 | message HelloResponse { 14 | string message = 1; 15 | } 16 | -------------------------------------------------------------------------------- /chapter10/json.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/Masterminds/go-in-practice/chapter10/user" 8 | "github.com/ugorji/go/codec" 9 | ) 10 | 11 | func main() { 12 | jh := new(codec.JsonHandle) 13 | u := &user.User{ 14 | Name: "Inigo Montoya", 15 | Email: "inigo@montoya.example.com", 16 | } 17 | 18 | var out []byte 19 | err := codec.NewEncoderBytes(&out, jh).Encode(&u) 20 | if err != nil { 21 | fmt.Println(err) 22 | os.Exit(1) 23 | } 24 | 25 | fmt.Println(string(out)) 26 | 27 | var u2 user.User 28 | err = codec.NewDecoderBytes(out, jh).Decode(&u2) 29 | if err != nil { 30 | fmt.Println(err) 31 | os.Exit(1) 32 | } 33 | 34 | fmt.Println(u2) 35 | } 36 | -------------------------------------------------------------------------------- /chapter10/multiple_connections.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | "os" 8 | ) 9 | 10 | func main() { 11 | r, err := http.Get("http://mattfarina.com") 12 | if err != nil { 13 | fmt.Println(err) 14 | os.Exit(1) 15 | } 16 | defer r.Body.Close() 17 | o, err := ioutil.ReadAll(r.Body) 18 | if err != nil { 19 | fmt.Println(err) 20 | os.Exit(1) 21 | } 22 | fmt.Println(string(o)) 23 | 24 | r2, err := http.Get("http://mattfarina.com/about") 25 | if err != nil { 26 | fmt.Println(err) 27 | os.Exit(1) 28 | } 29 | defer r2.Body.Close() 30 | o, err = ioutil.ReadAll(r2.Body) 31 | if err != nil { 32 | fmt.Println(err) 33 | os.Exit(1) 34 | } 35 | fmt.Println(string(o)) 36 | } 37 | -------------------------------------------------------------------------------- /chapter10/protobuf_client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | "os" 8 | 9 | pb "github.com/Masterminds/go-in-practice/chapter10/userpb" 10 | "github.com/golang/protobuf/proto" 11 | ) 12 | 13 | func main() { 14 | res, err := http.Get("http://localhost:8080") 15 | if err != nil { 16 | fmt.Println(err) 17 | os.Exit(1) 18 | } 19 | defer res.Body.Close() 20 | 21 | b, err := ioutil.ReadAll(res.Body) 22 | if err != nil { 23 | fmt.Println("Error reading response body: %v", err) 24 | os.Exit(1) 25 | } 26 | 27 | var u pb.User 28 | err = proto.Unmarshal(b, &u) 29 | if err != nil { 30 | fmt.Println("Error decoding response body: %v", err) 31 | os.Exit(1) 32 | } 33 | 34 | fmt.Println(u.GetName()) 35 | fmt.Println(u.GetId()) 36 | fmt.Println(u.GetEmail()) 37 | } 38 | -------------------------------------------------------------------------------- /chapter10/protobuf_server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | 6 | pb "github.com/Masterminds/go-in-practice/chapter10/userpb" 7 | "github.com/golang/protobuf/proto" 8 | ) 9 | 10 | func main() { 11 | http.HandleFunc("/", handler) 12 | http.ListenAndServe(":8080", nil) 13 | } 14 | 15 | func handler(res http.ResponseWriter, req *http.Request) { 16 | u := &pb.User{ 17 | Name: proto.String("Inigo Montoya"), 18 | Id: proto.Int32(1234), 19 | Email: proto.String("inigo@montoya.example.com"), 20 | } 21 | 22 | body, err := proto.Marshal(u) 23 | if err != nil { 24 | http.Error(res, err.Error(), http.StatusInternalServerError) 25 | return 26 | } 27 | res.Header().Set("Content-Type", "application/x-protobuf") 28 | res.Write(body) 29 | } 30 | -------------------------------------------------------------------------------- /chapter10/user/user.go: -------------------------------------------------------------------------------- 1 | //go:generate codecgen -o user_generated.go user.go 2 | 3 | package user 4 | 5 | type User struct { 6 | Name string `codec:"name"` 7 | Email string `codec:",omitempty"` 8 | } 9 | -------------------------------------------------------------------------------- /chapter10/user/user_generated.go: -------------------------------------------------------------------------------- 1 | // ************************************************************ 2 | // DO NOT EDIT. 3 | // THIS FILE IS AUTO-GENERATED BY codecgen. 4 | // ************************************************************ 5 | 6 | package user 7 | 8 | import ( 9 | "errors" 10 | "fmt" 11 | codec1978 "github.com/ugorji/go/codec" 12 | "reflect" 13 | "runtime" 14 | ) 15 | 16 | const ( 17 | // ----- content types ---- 18 | codecSelferC_UTF85734 = 1 19 | codecSelferC_RAW5734 = 0 20 | // ----- value types used ---- 21 | codecSelferValueTypeArray5734 = 10 22 | codecSelferValueTypeMap5734 = 9 23 | // ----- containerStateValues ---- 24 | codecSelfer_containerMapKey5734 = 2 25 | codecSelfer_containerMapValue5734 = 3 26 | codecSelfer_containerMapEnd5734 = 4 27 | codecSelfer_containerArrayElem5734 = 6 28 | codecSelfer_containerArrayEnd5734 = 7 29 | ) 30 | 31 | var ( 32 | codecSelferBitsize5734 = uint8(reflect.TypeOf(uint(0)).Bits()) 33 | codecSelferOnlyMapOrArrayEncodeToStructErr5734 = errors.New(`only encoded map or array can be decoded into a struct`) 34 | ) 35 | 36 | type codecSelfer5734 struct{} 37 | 38 | func init() { 39 | if codec1978.GenVersion != 5 { 40 | _, file, _, _ := runtime.Caller(0) 41 | err := fmt.Errorf("codecgen version mismatch: current: %v, need %v. Re-generate file: %v", 42 | 5, codec1978.GenVersion, file) 43 | panic(err) 44 | } 45 | if false { // reference the types, but skip this branch at build/run time 46 | } 47 | } 48 | 49 | func (x *User) CodecEncodeSelf(e *codec1978.Encoder) { 50 | var h codecSelfer5734 51 | z, r := codec1978.GenHelperEncoder(e) 52 | _, _, _ = h, z, r 53 | if x == nil { 54 | r.EncodeNil() 55 | } else { 56 | yym1 := z.EncBinary() 57 | _ = yym1 58 | if false { 59 | } else if z.HasExtensions() && z.EncExt(x) { 60 | } else { 61 | yysep2 := !z.EncBinary() 62 | yy2arr2 := z.EncBasicHandle().StructToArray 63 | var yyq2 [2]bool 64 | _, _, _ = yysep2, yyq2, yy2arr2 65 | const yyr2 bool = false 66 | yyq2[1] = x.Email != "" 67 | var yynn2 int 68 | if yyr2 || yy2arr2 { 69 | r.EncodeArrayStart(2) 70 | } else { 71 | yynn2 = 1 72 | for _, b := range yyq2 { 73 | if b { 74 | yynn2++ 75 | } 76 | } 77 | r.EncodeMapStart(yynn2) 78 | yynn2 = 0 79 | } 80 | if yyr2 || yy2arr2 { 81 | z.EncSendContainerState(codecSelfer_containerArrayElem5734) 82 | yym4 := z.EncBinary() 83 | _ = yym4 84 | if false { 85 | } else { 86 | r.EncodeString(codecSelferC_UTF85734, string(x.Name)) 87 | } 88 | } else { 89 | z.EncSendContainerState(codecSelfer_containerMapKey5734) 90 | r.EncodeString(codecSelferC_UTF85734, string("name")) 91 | z.EncSendContainerState(codecSelfer_containerMapValue5734) 92 | yym5 := z.EncBinary() 93 | _ = yym5 94 | if false { 95 | } else { 96 | r.EncodeString(codecSelferC_UTF85734, string(x.Name)) 97 | } 98 | } 99 | if yyr2 || yy2arr2 { 100 | z.EncSendContainerState(codecSelfer_containerArrayElem5734) 101 | if yyq2[1] { 102 | yym7 := z.EncBinary() 103 | _ = yym7 104 | if false { 105 | } else { 106 | r.EncodeString(codecSelferC_UTF85734, string(x.Email)) 107 | } 108 | } else { 109 | r.EncodeString(codecSelferC_UTF85734, "") 110 | } 111 | } else { 112 | if yyq2[1] { 113 | z.EncSendContainerState(codecSelfer_containerMapKey5734) 114 | r.EncodeString(codecSelferC_UTF85734, string("Email")) 115 | z.EncSendContainerState(codecSelfer_containerMapValue5734) 116 | yym8 := z.EncBinary() 117 | _ = yym8 118 | if false { 119 | } else { 120 | r.EncodeString(codecSelferC_UTF85734, string(x.Email)) 121 | } 122 | } 123 | } 124 | if yyr2 || yy2arr2 { 125 | z.EncSendContainerState(codecSelfer_containerArrayEnd5734) 126 | } else { 127 | z.EncSendContainerState(codecSelfer_containerMapEnd5734) 128 | } 129 | } 130 | } 131 | } 132 | 133 | func (x *User) CodecDecodeSelf(d *codec1978.Decoder) { 134 | var h codecSelfer5734 135 | z, r := codec1978.GenHelperDecoder(d) 136 | _, _, _ = h, z, r 137 | yym9 := z.DecBinary() 138 | _ = yym9 139 | if false { 140 | } else if z.HasExtensions() && z.DecExt(x) { 141 | } else { 142 | yyct10 := r.ContainerType() 143 | if yyct10 == codecSelferValueTypeMap5734 { 144 | yyl10 := r.ReadMapStart() 145 | if yyl10 == 0 { 146 | z.DecSendContainerState(codecSelfer_containerMapEnd5734) 147 | } else { 148 | x.codecDecodeSelfFromMap(yyl10, d) 149 | } 150 | } else if yyct10 == codecSelferValueTypeArray5734 { 151 | yyl10 := r.ReadArrayStart() 152 | if yyl10 == 0 { 153 | z.DecSendContainerState(codecSelfer_containerArrayEnd5734) 154 | } else { 155 | x.codecDecodeSelfFromArray(yyl10, d) 156 | } 157 | } else { 158 | panic(codecSelferOnlyMapOrArrayEncodeToStructErr5734) 159 | } 160 | } 161 | } 162 | 163 | func (x *User) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { 164 | var h codecSelfer5734 165 | z, r := codec1978.GenHelperDecoder(d) 166 | _, _, _ = h, z, r 167 | var yys11Slc = z.DecScratchBuffer() // default slice to decode into 168 | _ = yys11Slc 169 | var yyhl11 bool = l >= 0 170 | for yyj11 := 0; ; yyj11++ { 171 | if yyhl11 { 172 | if yyj11 >= l { 173 | break 174 | } 175 | } else { 176 | if r.CheckBreak() { 177 | break 178 | } 179 | } 180 | z.DecSendContainerState(codecSelfer_containerMapKey5734) 181 | yys11Slc = r.DecodeBytes(yys11Slc, true, true) 182 | yys11 := string(yys11Slc) 183 | z.DecSendContainerState(codecSelfer_containerMapValue5734) 184 | switch yys11 { 185 | case "name": 186 | if r.TryDecodeAsNil() { 187 | x.Name = "" 188 | } else { 189 | x.Name = string(r.DecodeString()) 190 | } 191 | case "Email": 192 | if r.TryDecodeAsNil() { 193 | x.Email = "" 194 | } else { 195 | x.Email = string(r.DecodeString()) 196 | } 197 | default: 198 | z.DecStructFieldNotFound(-1, yys11) 199 | } // end switch yys11 200 | } // end for yyj11 201 | z.DecSendContainerState(codecSelfer_containerMapEnd5734) 202 | } 203 | 204 | func (x *User) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { 205 | var h codecSelfer5734 206 | z, r := codec1978.GenHelperDecoder(d) 207 | _, _, _ = h, z, r 208 | var yyj14 int 209 | var yyb14 bool 210 | var yyhl14 bool = l >= 0 211 | yyj14++ 212 | if yyhl14 { 213 | yyb14 = yyj14 > l 214 | } else { 215 | yyb14 = r.CheckBreak() 216 | } 217 | if yyb14 { 218 | z.DecSendContainerState(codecSelfer_containerArrayEnd5734) 219 | return 220 | } 221 | z.DecSendContainerState(codecSelfer_containerArrayElem5734) 222 | if r.TryDecodeAsNil() { 223 | x.Name = "" 224 | } else { 225 | x.Name = string(r.DecodeString()) 226 | } 227 | yyj14++ 228 | if yyhl14 { 229 | yyb14 = yyj14 > l 230 | } else { 231 | yyb14 = r.CheckBreak() 232 | } 233 | if yyb14 { 234 | z.DecSendContainerState(codecSelfer_containerArrayEnd5734) 235 | return 236 | } 237 | z.DecSendContainerState(codecSelfer_containerArrayElem5734) 238 | if r.TryDecodeAsNil() { 239 | x.Email = "" 240 | } else { 241 | x.Email = string(r.DecodeString()) 242 | } 243 | for { 244 | yyj14++ 245 | if yyhl14 { 246 | yyb14 = yyj14 > l 247 | } else { 248 | yyb14 = r.CheckBreak() 249 | } 250 | if yyb14 { 251 | break 252 | } 253 | z.DecSendContainerState(codecSelfer_containerArrayElem5734) 254 | z.DecStructFieldNotFound(yyj14-1, "") 255 | } 256 | z.DecSendContainerState(codecSelfer_containerArrayEnd5734) 257 | } 258 | -------------------------------------------------------------------------------- /chapter10/userpb/user.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. 2 | // source: user.proto 3 | // DO NOT EDIT! 4 | 5 | /* 6 | Package chapter10 is a generated protocol buffer package. 7 | 8 | It is generated from these files: 9 | user.proto 10 | 11 | It has these top-level messages: 12 | User 13 | */ 14 | package chapter10 15 | 16 | import proto "github.com/golang/protobuf/proto" 17 | import fmt "fmt" 18 | import math "math" 19 | 20 | // Reference imports to suppress errors if they are not otherwise used. 21 | var _ = proto.Marshal 22 | var _ = fmt.Errorf 23 | var _ = math.Inf 24 | 25 | // This is a compile-time assertion to ensure that this generated file 26 | // is compatible with the proto package it is being compiled against. 27 | const _ = proto.ProtoPackageIsVersion1 28 | 29 | type User struct { 30 | Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` 31 | Id *int32 `protobuf:"varint,2,req,name=id" json:"id,omitempty"` 32 | Email *string `protobuf:"bytes,3,opt,name=email" json:"email,omitempty"` 33 | XXX_unrecognized []byte `json:"-"` 34 | } 35 | 36 | func (m *User) Reset() { *m = User{} } 37 | func (m *User) String() string { return proto.CompactTextString(m) } 38 | func (*User) ProtoMessage() {} 39 | func (*User) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } 40 | 41 | func (m *User) GetName() string { 42 | if m != nil && m.Name != nil { 43 | return *m.Name 44 | } 45 | return "" 46 | } 47 | 48 | func (m *User) GetId() int32 { 49 | if m != nil && m.Id != nil { 50 | return *m.Id 51 | } 52 | return 0 53 | } 54 | 55 | func (m *User) GetEmail() string { 56 | if m != nil && m.Email != nil { 57 | return *m.Email 58 | } 59 | return "" 60 | } 61 | 62 | func init() { 63 | proto.RegisterType((*User)(nil), "chapter10.User") 64 | } 65 | 66 | var fileDescriptor0 = []byte{ 67 | // 94 bytes of a gzipped FileDescriptorProto 68 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x2a, 0x2d, 0x4e, 0x2d, 69 | 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x4c, 0xce, 0x48, 0x2c, 0x28, 0x49, 0x2d, 0x32, 70 | 0x34, 0x50, 0xd2, 0xe7, 0x62, 0x09, 0x05, 0x4a, 0x08, 0xf1, 0x70, 0xb1, 0xe4, 0x25, 0xe6, 0xa6, 71 | 0x4a, 0x30, 0x2a, 0x30, 0x69, 0x70, 0x0a, 0x71, 0x71, 0x31, 0x65, 0xa6, 0x48, 0x30, 0x01, 0xd9, 72 | 0xac, 0x42, 0xbc, 0x5c, 0xac, 0xa9, 0xb9, 0x89, 0x99, 0x39, 0x12, 0xcc, 0x0a, 0x8c, 0x1a, 0x9c, 73 | 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x61, 0x0d, 0xf1, 0xf6, 0x48, 0x00, 0x00, 0x00, 74 | } 75 | -------------------------------------------------------------------------------- /chapter10/userpb/user.proto: -------------------------------------------------------------------------------- 1 | package chapter10; 2 | 3 | message User { 4 | required string name = 1; 5 | required int32 id = 2; 6 | optional string email = 3; 7 | } 8 | -------------------------------------------------------------------------------- /chapter11/annotations/config.ini: -------------------------------------------------------------------------------- 1 | total=247 2 | running=2 3 | sleeping=245 4 | threads=1189 5 | load=70.87 6 | -------------------------------------------------------------------------------- /chapter11/annotations/json.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | type Name struct { 9 | First string `json:"firstName" xml:"FirstName"` 10 | Last string `json:"lastName,omitempty"` 11 | Other string `not,even.a=tag` 12 | } 13 | 14 | func main() { 15 | n := &Name{"Inigo", "Montoya", ""} 16 | data, _ := json.Marshal(n) 17 | fmt.Printf("%s\n", data) 18 | } 19 | -------------------------------------------------------------------------------- /chapter11/annotations/jsonxml/jsonxml.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "encoding/xml" 6 | "os" 7 | ) 8 | 9 | type Person struct { 10 | FirstName string `json:"first" xml:"firstName,attr"` 11 | LastName string `json:"last" xml:"lastName"` 12 | } 13 | 14 | func main() { 15 | p := &Person{FirstName: "Inigo", LastName: "Montoya"} 16 | j, _ := json.MarshalIndent(p, "", " ") 17 | os.Stdout.Write(j) 18 | println() 19 | 20 | x, _ := xml.MarshalIndent(p, "", " ") 21 | os.Stdout.Write(x) 22 | } 23 | -------------------------------------------------------------------------------- /chapter11/annotations/load.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "errors" 7 | "fmt" 8 | "reflect" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | type Processes struct { 14 | Total int `ini:"total"` 15 | Running int `ini:"running"` 16 | Sleeping int `ini:"sleeping"` 17 | Threads int `ini:"threads"` 18 | Load float64 `ini:"load"` 19 | } 20 | 21 | func main() { 22 | fmt.Println("Write a struct to output:") 23 | proc := &Processes{ 24 | Total: 23, 25 | Running: 3, 26 | Sleeping: 20, 27 | Threads: 34, 28 | Load: 1.8, 29 | } 30 | data, err := Marshal(proc) 31 | if err != nil { 32 | panic(err) 33 | } 34 | fmt.Println(string(data)) 35 | 36 | fmt.Println("Read the data back into a struct") 37 | proc2 := &Processes{} 38 | if err := Unmarshal(data, proc2); err != nil { 39 | panic(err) 40 | } 41 | fmt.Printf("Struct: %#v", proc2) 42 | } 43 | 44 | func fieldName(field reflect.StructField) string { 45 | if t := field.Tag.Get("ini"); t != "" { 46 | return t 47 | } 48 | return field.Name 49 | } 50 | 51 | func Marshal(v interface{}) ([]byte, error) { 52 | var b bytes.Buffer 53 | val := reflect.Indirect(reflect.ValueOf(v)) 54 | if val.Kind() != reflect.Struct { 55 | return []byte{}, errors.New("unmarshal can only take structs") 56 | } 57 | 58 | t := val.Type() 59 | for i := 0; i < t.NumField(); i++ { 60 | f := t.Field(i) 61 | name := fieldName(f) 62 | raw := val.Field(i).Interface() 63 | fmt.Fprintf(&b, "%s=%v\n", name, raw) 64 | } 65 | return b.Bytes(), nil 66 | } 67 | 68 | func Unmarshal(data []byte, v interface{}) error { 69 | 70 | val := reflect.Indirect(reflect.ValueOf(v)) 71 | t := val.Type() 72 | 73 | b := bytes.NewBuffer(data) 74 | scanner := bufio.NewScanner(b) 75 | for scanner.Scan() { 76 | line := scanner.Text() 77 | pair := strings.SplitN(line, "=", 2) 78 | if len(pair) < 2 { 79 | // Skip any malformed lines. 80 | continue 81 | } 82 | setField(pair[0], pair[1], t, val) 83 | } 84 | return nil 85 | } 86 | 87 | func setField(name, value string, t reflect.Type, v reflect.Value) { 88 | for i := 0; i < t.NumField(); i++ { 89 | field := t.Field(i) 90 | if name == fieldName(field) { 91 | var dest reflect.Value 92 | switch field.Type.Kind() { 93 | default: 94 | fmt.Printf("Kind %s not supported.\n", field.Type.Kind()) 95 | continue 96 | case reflect.Int: 97 | ival, err := strconv.Atoi(value) 98 | if err != nil { 99 | fmt.Printf("Could not convert %q to int: %s\n", value, err) 100 | continue 101 | } 102 | dest = reflect.ValueOf(ival) 103 | case reflect.Float64: 104 | fval, err := strconv.ParseFloat(value, 64) 105 | if err != nil { 106 | fmt.Printf("Could not convert %q to float64: %s\n", value, err) 107 | continue 108 | } 109 | dest = reflect.ValueOf(fval) 110 | case reflect.String: 111 | dest = reflect.ValueOf(value) 112 | case reflect.Bool: 113 | bval, err := strconv.ParseBool(value) 114 | if err != nil { 115 | fmt.Printf("Could not convert %q to bool: %s\n", value, err) 116 | continue 117 | } 118 | dest = reflect.ValueOf(bval) 119 | } 120 | v.Field(i).Set(dest) 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /chapter11/generator/myint.go: -------------------------------------------------------------------------------- 1 | //go:generate ./queue MyInt 2 | package main 3 | 4 | import "fmt" 5 | 6 | type MyInt int 7 | 8 | func main() { 9 | var one, two, three MyInt = 1, 2, 3 10 | q := NewMyIntQueue() 11 | q.Insert(one) 12 | q.Insert(two) 13 | q.Insert(three) 14 | 15 | fmt.Printf("First value: %d\n", q.Remove()) 16 | } 17 | -------------------------------------------------------------------------------- /chapter11/generator/myint_queue.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type MyIntQueue struct { 4 | q []MyInt 5 | } 6 | 7 | func NewMyIntQueue() *MyIntQueue { 8 | return &MyIntQueue{ 9 | q: []MyInt{}, 10 | } 11 | } 12 | 13 | func (o *MyIntQueue) Insert(v MyInt) { 14 | o.q = append(o.q, v) 15 | } 16 | 17 | func (o *MyIntQueue) Remove() MyInt { 18 | if len(o.q) == 0 { 19 | panic("Oops.") 20 | } 21 | first := o.q[0] 22 | o.q = o.q[1:] 23 | return first 24 | } 25 | -------------------------------------------------------------------------------- /chapter11/generator/queue: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Masterminds/go-in-practice/e9313183d93763f3bf558ffa6c062ebc3d0179fb/chapter11/generator/queue -------------------------------------------------------------------------------- /chapter11/generator/queue.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | "text/template" 8 | ) 9 | 10 | var tpl = `package {{.Package}} 11 | 12 | type {{.MyType}}Queue struct { 13 | q []{{.MyType}} 14 | } 15 | 16 | func New{{.MyType}}Queue() *{{.MyType}}Queue { 17 | return &{{.MyType}}Queue{ 18 | q: []{{.MyType}}{}, 19 | } 20 | } 21 | 22 | func (o *{{.MyType}}Queue) Insert(v {{.MyType}}) { 23 | o.q = append(o.q, v) 24 | } 25 | 26 | func (o *{{.MyType}}Queue) Remove() {{.MyType}} { 27 | if len(o.q) == 0 { 28 | panic("Oops.") 29 | } 30 | first := o.q[0] 31 | o.q = o.q[1:] 32 | return first 33 | } 34 | ` 35 | 36 | func main() { 37 | tt := template.Must(template.New("queue").Parse(tpl)) 38 | for i := 1; i < len(os.Args); i++ { 39 | dest := strings.ToLower(os.Args[i]) + "_queue.go" 40 | file, err := os.Create(dest) 41 | if err != nil { 42 | fmt.Printf("Could not create %s: %s (skip)\n", dest, err) 43 | continue 44 | } 45 | 46 | vals := map[string]string{ 47 | "MyType": os.Args[i], 48 | "Package": os.Getenv("GOPACKAGE"), 49 | } 50 | tt.Execute(file, vals) 51 | 52 | file.Close() 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /chapter11/generator/simple.go: -------------------------------------------------------------------------------- 1 | //go:generate echo hello 2 | package main 3 | 4 | func main() { 5 | println("Goodbyte") 6 | } 7 | -------------------------------------------------------------------------------- /chapter11/interfaces/implements.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "reflect" 7 | ) 8 | 9 | type Name struct { 10 | First, Last string 11 | } 12 | 13 | func (n *Name) String() string { 14 | return n.First + " " + n.Last 15 | } 16 | 17 | func main() { 18 | n := &Name{First: "Inigo", Last: "Montoya"} 19 | 20 | stringer := (*fmt.Stringer)(nil) 21 | implements(n, stringer) 22 | 23 | writer := (*io.Writer)(nil) 24 | implements(n, writer) 25 | 26 | s := &[]string{} 27 | implements(s, writer) 28 | } 29 | 30 | func implements(concrete interface{}, target interface{}) bool { 31 | iface := reflect.TypeOf(target).Elem() 32 | 33 | v := reflect.ValueOf(concrete) 34 | t := v.Type() 35 | 36 | if t.Implements(iface) { 37 | fmt.Printf("%T is a %s\n", concrete, iface.Name()) 38 | return true 39 | } 40 | fmt.Printf("%T is not a %s\n", concrete, iface.Name()) 41 | return false 42 | } 43 | -------------------------------------------------------------------------------- /chapter11/interfaces/typeassert.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | ) 7 | 8 | func main() { 9 | b := bytes.NewBuffer([]byte("Hello")) 10 | if isStringer(b) { 11 | fmt.Printf("%T is a stringer\n", b) 12 | } 13 | i := 123 14 | if isStringer(i) { 15 | fmt.Printf("%T is a stringer\n", i) 16 | } 17 | } 18 | 19 | func isStringer(v interface{}) bool { 20 | _, ok := v.(fmt.Stringer) 21 | return ok 22 | } 23 | -------------------------------------------------------------------------------- /chapter11/structs/structwalker.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "strings" 7 | ) 8 | 9 | func main() { 10 | fmt.Println("Walking a simple integer") 11 | var one MyInt = 1 12 | walk(one, 0) 13 | 14 | fmt.Println("Walking a simple struct") 15 | two := struct{ Name string }{"foo"} 16 | walk(two, 0) 17 | 18 | p := &Person{ 19 | Name: &Name{"Count", "Tyrone", "Rugen"}, 20 | Address: &Address{"Humperdink Castle", "Florian"}, 21 | } 22 | fmt.Println("Walking a struct with struct fields") 23 | walk(p, 0) 24 | } 25 | 26 | type MyInt int 27 | 28 | type Person struct { 29 | Name *Name 30 | Address *Address 31 | } 32 | 33 | type Name struct { 34 | Title, First, Last string 35 | } 36 | 37 | type Address struct { 38 | Street, Region string 39 | } 40 | 41 | func walk(u interface{}, depth int) { 42 | val := reflect.Indirect(reflect.ValueOf(u)) 43 | t := val.Type() 44 | tabs := strings.Repeat("\t", depth+1) 45 | fmt.Printf("%sValue is type %q (%s)\n", tabs, t, val.Kind()) 46 | if val.Kind() == reflect.Struct { 47 | for i := 0; i < t.NumField(); i++ { 48 | field := t.Field(i) 49 | fieldVal := reflect.Indirect(val.Field(i)) 50 | 51 | tabs := strings.Repeat("\t", depth+2) 52 | fmt.Printf("%sField %q is type %q (%s)\n", 53 | tabs, field.Name, field.Type, fieldVal.Kind()) 54 | 55 | if fieldVal.Kind() == reflect.Struct { 56 | walk(fieldVal.Interface(), depth+1) 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /chapter11/values/sumkind.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "strconv" 7 | ) 8 | 9 | type MyInt int64 10 | 11 | func main() { 12 | var a uint8 = 2 13 | var b int = 37 14 | var c string = "3.2" 15 | var d MyInt = 1 16 | res := sum(a, b, c, d) 17 | fmt.Printf("Result: %f\n", res) 18 | } 19 | 20 | func sum(v ...interface{}) float64 { 21 | var res float64 = 0 22 | for _, val := range v { 23 | ref := reflect.ValueOf(val) 24 | switch ref.Kind() { 25 | case reflect.Int, reflect.Int64: 26 | res += float64(ref.Int()) 27 | case reflect.Uint8: 28 | res += float64(ref.Uint()) 29 | case reflect.String: 30 | a, err := strconv.ParseFloat(ref.String(), 64) 31 | if err != nil { 32 | panic(err) 33 | } 34 | res += a 35 | default: 36 | fmt.Printf("Unsupported type %T. Ignoring.\n", val) 37 | } 38 | } 39 | return res 40 | } 41 | -------------------------------------------------------------------------------- /chapter2/callback_shutdown.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "os" 7 | ) 8 | 9 | func main() { 10 | http.HandleFunc("/shutdown", shutdown) 11 | http.HandleFunc("/", homePage) 12 | http.ListenAndServe(":8080", nil) 13 | } 14 | 15 | func shutdown(res http.ResponseWriter, req *http.Request) { 16 | os.Exit(0) 17 | } 18 | 19 | func homePage(res http.ResponseWriter, req *http.Request) { 20 | if req.URL.Path != "/" { 21 | http.NotFound(res, req) 22 | return 23 | } 24 | fmt.Fprint(res, "The homepage.") 25 | } 26 | -------------------------------------------------------------------------------- /chapter2/conf.ini: -------------------------------------------------------------------------------- 1 | ; A comment line 2 | [Section] 3 | enabled = true 4 | path = /usr/local # another comment -------------------------------------------------------------------------------- /chapter2/conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "enabled": true, 3 | "path": "/usr/local" 4 | } -------------------------------------------------------------------------------- /chapter2/conf.yaml: -------------------------------------------------------------------------------- 1 | enabled: true 2 | path: /usr/local -------------------------------------------------------------------------------- /chapter2/count_cli.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "gopkg.in/urfave/cli.v1" 8 | ) 9 | 10 | func main() { 11 | app := cli.NewApp() 12 | app.Usage = "Count up or down." 13 | app.Commands = []cli.Command{ 14 | { 15 | Name: "up", 16 | ShortName: "u", 17 | Usage: "Count Up", 18 | Flags: []cli.Flag{ 19 | cli.IntFlag{ 20 | Name: "stop, s", 21 | Usage: "Value to count up to", 22 | Value: 10, 23 | }, 24 | }, 25 | Action: func(c *cli.Context) error { 26 | start := c.Int("stop") 27 | if start <= 0 { 28 | fmt.Println("Stop cannot be negative.") 29 | } 30 | for i := 1; i <= start; i++ { 31 | fmt.Println(i) 32 | } 33 | return nil 34 | }, 35 | }, 36 | { 37 | Name: "down", 38 | ShortName: "d", 39 | Usage: "Count Down", 40 | Flags: []cli.Flag{ 41 | cli.IntFlag{ 42 | Name: "start, s", 43 | Usage: "Start counting down from", 44 | Value: 10, 45 | }, 46 | }, 47 | Action: func(c *cli.Context) error { 48 | start := c.Int("start") 49 | if start < 0 { 50 | fmt.Println("Start cannot be negative.") 51 | } 52 | for i := start; i >= 0; i-- { 53 | fmt.Println(i) 54 | } 55 | return nil 56 | }, 57 | }, 58 | } 59 | 60 | app.Run(os.Args) 61 | } 62 | -------------------------------------------------------------------------------- /chapter2/env_config.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "os" 7 | ) 8 | 9 | func main() { 10 | http.HandleFunc("/", homePage) 11 | http.ListenAndServe(":"+os.Getenv("PORT"), nil) 12 | } 13 | 14 | func homePage(res http.ResponseWriter, req *http.Request) { 15 | if req.URL.Path != "/" { 16 | http.NotFound(res, req) 17 | return 18 | } 19 | fmt.Fprint(res, "The homepage.") 20 | } 21 | -------------------------------------------------------------------------------- /chapter2/flag_cli.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | ) 7 | 8 | var name = flag.String("name", "World", "A name to say hello to.") 9 | 10 | var spanish bool 11 | 12 | func init() { 13 | flag.BoolVar(&spanish, "spanish", false, "Use Spanish language.") 14 | flag.BoolVar(&spanish, "s", false, "Use Spanish language.") 15 | } 16 | 17 | func main() { 18 | flag.Parse() 19 | if spanish == true { 20 | fmt.Printf("Hola %s!\n", *name) 21 | } else { 22 | fmt.Printf("Hello %s!\n", *name) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chapter2/go_flags_example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | flags "github.com/jessevdk/go-flags" 6 | ) 7 | 8 | var opts struct { 9 | Name string `short:"n" long:"name" default:"World" description:"A name to say hello to."` 10 | Spanish bool `short:"s" long:"spanish" description:"Use Spanish Language"` 11 | } 12 | 13 | func main() { 14 | flags.Parse(&opts) 15 | 16 | if opts.Spanish == true { 17 | fmt.Printf("Hola %s!\n", opts.Name) 18 | } else { 19 | fmt.Printf("Hello %s!\n", opts.Name) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /chapter2/hello_cli.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "gopkg.in/urfave/cli.v1" 8 | ) 9 | 10 | func main() { 11 | app := cli.NewApp() 12 | app.Name = "hello_cli" 13 | app.Usage = "Print hello world" 14 | app.Flags = []cli.Flag{ 15 | cli.StringFlag{ 16 | Name: "name, n", 17 | Value: "World", 18 | Usage: "Who to say hello to.", 19 | }, 20 | } 21 | app.Action = func(c *cli.Context) error { 22 | name := c.GlobalString("name") 23 | fmt.Printf("Hello %s!\n", name) 24 | 25 | return nil 26 | } 27 | 28 | app.Run(os.Args) 29 | } 30 | -------------------------------------------------------------------------------- /chapter2/ini_config.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "code.google.com/p/gcfg" 7 | ) 8 | 9 | func main() { 10 | config := struct { 11 | Section struct { 12 | Enabled bool 13 | Path string 14 | } 15 | }{} 16 | 17 | err := gcfg.ReadFileInto(&config, "conf.ini") 18 | if err != nil { 19 | fmt.Println("Failed to parse config file: %s", err) 20 | } 21 | fmt.Println(config.Section.Enabled) 22 | fmt.Println(config.Section.Path) 23 | } 24 | -------------------------------------------------------------------------------- /chapter2/json_config.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | type configuration struct { 10 | Enabled bool 11 | Path string 12 | } 13 | 14 | func main() { 15 | file, _ := os.Open("conf.json") 16 | defer file.Close() 17 | 18 | decoder := json.NewDecoder(file) 19 | conf := configuration{} 20 | err := decoder.Decode(&conf) 21 | if err != nil { 22 | fmt.Println("Error:", err) 23 | } 24 | fmt.Println(conf.Path) 25 | } 26 | -------------------------------------------------------------------------------- /chapter2/manners_shutdown.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "os" 7 | "os/signal" 8 | 9 | "github.com/braintree/manners" 10 | ) 11 | 12 | func main() { 13 | handler := newHandler() 14 | 15 | ch := make(chan os.Signal) 16 | signal.Notify(ch, os.Interrupt, os.Kill) 17 | go listenForShutdown(ch) 18 | 19 | manners.ListenAndServe(":8080", handler) 20 | } 21 | 22 | func newHandler() *handler { 23 | return &handler{} 24 | } 25 | 26 | type handler struct{} 27 | 28 | func (h *handler) ServeHTTP(res http.ResponseWriter, req *http.Request) { 29 | query := req.URL.Query() 30 | name := query.Get("name") 31 | if name == "" { 32 | name = "Inigo Montoya" 33 | } 34 | fmt.Fprint(res, "Hello, my name is ", name) 35 | } 36 | 37 | func listenForShutdown(ch <-chan os.Signal) { 38 | <-ch 39 | manners.Close() 40 | } 41 | -------------------------------------------------------------------------------- /chapter2/multiple_handlers.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "strings" 7 | ) 8 | 9 | func main() { 10 | http.HandleFunc("/hello", hello) 11 | http.HandleFunc("/goodbye/", goodbye) 12 | http.HandleFunc("/", homePage) 13 | http.ListenAndServe(":8080", nil) 14 | } 15 | 16 | func hello(res http.ResponseWriter, req *http.Request) { 17 | query := req.URL.Query() 18 | name := query.Get("name") 19 | if name == "" { 20 | name = "Inigo Montoya" 21 | } 22 | fmt.Fprint(res, "Hello, my name is ", name) 23 | } 24 | 25 | func goodbye(res http.ResponseWriter, req *http.Request) { 26 | path := req.URL.Path 27 | parts := strings.Split(path, "/") 28 | name := parts[2] 29 | if name == "" { 30 | name = "Inigo Montoya" 31 | } 32 | fmt.Fprint(res, "Goodbye ", name) 33 | } 34 | 35 | func homePage(res http.ResponseWriter, req *http.Request) { 36 | if req.URL.Path != "/" { 37 | http.NotFound(res, req) 38 | return 39 | } 40 | fmt.Fprint(res, "The homepage.") 41 | } 42 | -------------------------------------------------------------------------------- /chapter2/path_handlers.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "path" 7 | "strings" 8 | ) 9 | 10 | func main() { 11 | pr := newPathResolver() 12 | pr.Add("GET /hello", hello) 13 | pr.Add("* /goodbye/*", goodbye) 14 | http.ListenAndServe(":8080", pr) 15 | } 16 | 17 | func newPathResolver() *pathResolver { 18 | return &pathResolver{make(map[string]http.HandlerFunc)} 19 | } 20 | 21 | type pathResolver struct { 22 | handlers map[string]http.HandlerFunc 23 | } 24 | 25 | func (p *pathResolver) Add(path string, handler http.HandlerFunc) { 26 | p.handlers[path] = handler 27 | } 28 | 29 | func (p *pathResolver) ServeHTTP(res http.ResponseWriter, req *http.Request) { 30 | check := req.Method + " " + req.URL.Path 31 | for pattern, handlerFunc := range p.handlers { 32 | if ok, err := path.Match(pattern, check); ok && err == nil { 33 | handlerFunc(res, req) 34 | return 35 | } else if err != nil { 36 | fmt.Fprint(res, err) 37 | } 38 | } 39 | 40 | http.NotFound(res, req) 41 | } 42 | 43 | func hello(res http.ResponseWriter, req *http.Request) { 44 | query := req.URL.Query() 45 | name := query.Get("name") 46 | if name == "" { 47 | name = "Inigo Montoya" 48 | } 49 | fmt.Fprint(res, "Hello, my name is ", name) 50 | } 51 | 52 | func goodbye(res http.ResponseWriter, req *http.Request) { 53 | path := req.URL.Path 54 | parts := strings.Split(path, "/") 55 | name := parts[2] 56 | if name == "" { 57 | name = "Inigo Montoya" 58 | } 59 | fmt.Fprint(res, "Goodbye ", name) 60 | } 61 | -------------------------------------------------------------------------------- /chapter2/regex_handlers.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "regexp" 7 | "strings" 8 | ) 9 | 10 | func main() { 11 | rr := newPathResolver() 12 | rr.Add("GET /hello", hello) 13 | rr.Add("(GET|HEAD) /goodbye(/?[A-Za-z0-9]*)?", goodbye) 14 | http.ListenAndServe(":8080", rr) 15 | } 16 | 17 | func newPathResolver() *regexResolver { 18 | return ®exResolver{ 19 | handlers: make(map[string]http.HandlerFunc), 20 | cache: make(map[string]*regexp.Regexp), 21 | } 22 | } 23 | 24 | type regexResolver struct { 25 | handlers map[string]http.HandlerFunc 26 | cache map[string]*regexp.Regexp 27 | } 28 | 29 | func (r *regexResolver) Add(regex string, handler http.HandlerFunc) { 30 | r.handlers[regex] = handler 31 | cache, _ := regexp.Compile(regex) 32 | r.cache[regex] = cache 33 | } 34 | 35 | func (r *regexResolver) ServeHTTP(res http.ResponseWriter, req *http.Request) { 36 | check := req.Method + " " + req.URL.Path 37 | for pattern, handlerFunc := range r.handlers { 38 | if r.cache[pattern].MatchString(check) == true { 39 | handlerFunc(res, req) 40 | return 41 | } 42 | } 43 | 44 | http.NotFound(res, req) 45 | } 46 | 47 | func hello(res http.ResponseWriter, req *http.Request) { 48 | query := req.URL.Query() 49 | name := query.Get("name") 50 | if name == "" { 51 | name = "Inigo Montoya" 52 | } 53 | fmt.Fprint(res, "Hello, my name is ", name) 54 | } 55 | 56 | func goodbye(res http.ResponseWriter, req *http.Request) { 57 | path := req.URL.Path 58 | parts := strings.Split(path, "/") 59 | name := "" 60 | if len(parts) > 2 { 61 | name = parts[2] 62 | } 63 | if name == "" { 64 | name = "Inigo Montoya" 65 | } 66 | fmt.Fprint(res, "Goodbye ", name) 67 | } 68 | -------------------------------------------------------------------------------- /chapter2/yaml_config.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kylelemons/go-gypsy/yaml" 6 | ) 7 | 8 | func main() { 9 | config, err := yaml.ReadFile("conf.yaml") 10 | if err != nil { 11 | fmt.Println(err) 12 | } 13 | fmt.Println(config.Get("path")) 14 | fmt.Println(config.GetBool("enabled")) 15 | } 16 | -------------------------------------------------------------------------------- /chapter3/closing/bad.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | msg := make(chan string) 10 | until := time.After(5 * time.Second) 11 | 12 | go send(msg) 13 | 14 | for { 15 | select { 16 | case m := <-msg: 17 | fmt.Println(m) 18 | case <-until: 19 | close(msg) 20 | time.Sleep(500 * time.Millisecond) 21 | return 22 | } 23 | } 24 | } 25 | 26 | func send(ch chan string) { 27 | for { 28 | ch <- "hello" 29 | time.Sleep(500 * time.Millisecond) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /chapter3/closing/close-sender.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "time" 4 | 5 | func main() { 6 | ch := make(chan bool) 7 | timeout := time.After(600 * time.Millisecond) 8 | go send(ch) 9 | for { 10 | select { 11 | case <-ch: 12 | println("Got message.") 13 | case <-timeout: 14 | println("Time out") 15 | return 16 | default: 17 | println("*yawn*") 18 | time.Sleep(100 * time.Millisecond) 19 | } 20 | } 21 | } 22 | 23 | func send(ch chan bool) { 24 | time.Sleep(120 * time.Millisecond) 25 | ch <- true 26 | close(ch) 27 | println("Sent and closed") 28 | } 29 | -------------------------------------------------------------------------------- /chapter3/closing/okay.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | msg := make(chan string) 10 | done := make(chan bool) 11 | until := time.After(5 * time.Second) 12 | 13 | go send(msg, done) 14 | 15 | for { 16 | select { 17 | case m := <-msg: 18 | fmt.Println(m) 19 | case <-until: 20 | done <- true 21 | time.Sleep(500 * time.Millisecond) 22 | return 23 | } 24 | } 25 | } 26 | 27 | func send(ch chan<- string, done <-chan bool) { 28 | for { 29 | select { 30 | case <-done: 31 | println("Done") 32 | close(ch) 33 | return 34 | default: 35 | ch <- "hello" 36 | time.Sleep(500 * time.Millisecond) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /chapter3/closures/simple.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | //"runtime" 6 | ) 7 | 8 | func main() { 9 | fmt.Println("Outside a goroutine.") 10 | go func() { 11 | fmt.Println("Inside a goroutine") 12 | }() 13 | fmt.Println("Outside again.") 14 | 15 | //runtime.Gosched() 16 | } 17 | -------------------------------------------------------------------------------- /chapter3/echoback/echoback.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | go echo(os.Stdin, os.Stdout) 12 | time.Sleep(30 * time.Second) 13 | fmt.Println("Timed out.") 14 | os.Exit(0) 15 | } 16 | 17 | func echo(in io.Reader, out io.Writer) { 18 | io.Copy(out, in) 19 | } 20 | -------------------------------------------------------------------------------- /chapter3/echoredux/echoredux.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | done := time.After(30 * time.Second) 11 | echo := make(chan []byte) 12 | go readStdin(echo) 13 | for { 14 | select { 15 | case buf := <-echo: 16 | os.Stdout.Write(buf) 17 | case <-done: 18 | fmt.Println("Timed out") 19 | close(echo) 20 | os.Exit(0) 21 | } 22 | } 23 | } 24 | 25 | func readStdin(out chan<- []byte) { 26 | for { 27 | data := make([]byte, 1024) 28 | l, _ := os.Stdin.Read(data) 29 | if l > 0 { 30 | out <- data 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chapter3/race/1.txt: -------------------------------------------------------------------------------- 1 | HAD you followed Captain Ahab down into his cabin after the squall that took place on the night succeeding that wild ratification of his purpose with his crew, you would have seen him go to a locker in the transom, and bringing out a large wrinkled roll of yellowish sea-charts, spread them before him on his screwed-down table. Then seating himself before it, you would have seen him intently study the various lines and shadings which there met his eye; and with slow but steady pencil trace additional courses over spaces that before were blank. At intervals, he would refer to piles of old log-books beside him, wherein were set down the seasons and places in which, on various former voyages of various ships, sperm whales had been captured or seen. 2 | -------------------------------------------------------------------------------- /chapter3/race/2.txt: -------------------------------------------------------------------------------- 1 | While thus employed, the heavy pewter lamp suspended in chains over his head, continually rocked with the motion of the ship, and forever threw shifting gleams and shadows of lines upon his wrinkled brow, till it almost seemed that while he himself was marking out lines and courses on the wrinkled charts, some invisible pencil was also tracing lines and courses upon the deeply marked chart of his forehead. 2 | -------------------------------------------------------------------------------- /chapter3/race/3.txt: -------------------------------------------------------------------------------- 1 | But it was not this night in particular that, in the solitude of his cabin, Ahab thus pondered over his charts. Almost every night they were brought out; almost every night some pencil marks were effaced, and others were substituted. For with the charts of all four oceans before him, Ahab was threading a maze of currents and eddies, with a view to the more certain accomplishment of that monomaniac thought of his soul. 2 | -------------------------------------------------------------------------------- /chapter3/race/4.txt: -------------------------------------------------------------------------------- 1 | Now, to anyone not fully acquainted with the ways of the leviathans, it might seem an absurdly hopeless task thus to seek out one solitary creature in the unhooped oceans of this planet. But not so did it seem to Ahab, who knew the sets of all tides and currents; and thereby calculating the driftings of the sperm whale’s food; and, also, calling to mind the regular, ascertained seasons for hunting him in particular latitudes; could arrive at reasonable surmises, almost approaching to certainties, concerning the timeliest day to be upon this or that ground in search of his prey. 2 | -------------------------------------------------------------------------------- /chapter3/race/5.txt: -------------------------------------------------------------------------------- 1 | So assured, indeed, is the fact concerning the periodicalness of the sperm whale’s resorting to given waters, that many hunters believe that, could he be closely observed and studied throughout the world; were the logs for one voyage of the entire whale-fleet carefully collated, then the migrations of the sperm whale would be found to correspond in invariability to those of the herring-shoals or the flights of swallows. On this hint, attempts have been made to construct elaborate migratory charts of the sperm whale. 2 | -------------------------------------------------------------------------------- /chapter3/race/6.txt: -------------------------------------------------------------------------------- 1 | Besides, when making a passage from one feeding-ground to another, the sperm whales, guided by some infallible instinct—say, rather, secret intelligence from the Deity—mostly swim in veins, as they are called; continuing their way along a given ocean-line with such undeviating exactitude, that no ship ever sailed her course, by any chart, with one tithe of such marvellous precision. Though, in these cases, the direction taken by any one whale be straight as a surveyor’s parallel, and though the line of advance be strictly confined to its own unavoidable, straight wake, yet the arbitrary vein in which at these times he is said to swim, generally embraces some few miles in width (more or less, as the vein is presumed to expand or contract); but never exceeds the visual sweep from the whale-ship’s mast-heads, when circumspectly gliding along this magic zone. The sum is, that at particular seasons within that breadth and along that path, migrating whales may with great confidence be looked for. 2 | -------------------------------------------------------------------------------- /chapter3/race/README: -------------------------------------------------------------------------------- 1 | Example race condition fixed by locking. 2 | 3 | Text is from Moby Dick: http://www.bartleby.com/91/44.html 4 | -------------------------------------------------------------------------------- /chapter3/race/fixed.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "strings" 8 | "sync" 9 | ) 10 | 11 | func main() { 12 | var wg sync.WaitGroup 13 | 14 | w := newWords() 15 | for _, f := range os.Args[1:] { 16 | wg.Add(1) 17 | go func(file string) { 18 | if err := tallyWords(file, w); err != nil { 19 | fmt.Println(err.Error()) 20 | } 21 | wg.Done() 22 | }(f) 23 | } 24 | wg.Wait() 25 | 26 | fmt.Println("Words that appear more than once:") 27 | w.Lock() 28 | for word, count := range w.found { 29 | if count > 1 { 30 | fmt.Printf("%s: %d\n", word, count) 31 | } 32 | } 33 | w.Unlock() 34 | } 35 | 36 | type words struct { 37 | sync.Mutex 38 | found map[string]int 39 | } 40 | 41 | func newWords() *words { 42 | return &words{found: map[string]int{}} 43 | } 44 | 45 | func (w *words) add(word string, n int) { 46 | w.Lock() 47 | defer w.Unlock() 48 | count, ok := w.found[word] 49 | if !ok { 50 | w.found[word] = n 51 | return 52 | } 53 | w.found[word] = count + n 54 | } 55 | 56 | func tallyWords(filename string, dict *words) error { 57 | file, err := os.Open(filename) 58 | if err != nil { 59 | return err 60 | } 61 | defer file.Close() 62 | 63 | scanner := bufio.NewScanner(file) 64 | scanner.Split(bufio.ScanWords) 65 | for scanner.Scan() { 66 | word := strings.ToLower(scanner.Text()) 67 | dict.add(word, 1) 68 | } 69 | return scanner.Err() 70 | } 71 | -------------------------------------------------------------------------------- /chapter3/race/race.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "strings" 8 | "sync" 9 | ) 10 | 11 | func main() { 12 | var wg sync.WaitGroup 13 | 14 | w := newWords() 15 | for _, f := range os.Args[1:] { 16 | wg.Add(1) 17 | go func(file string) { 18 | if err := tallyWords(file, w); err != nil { 19 | fmt.Println(err.Error()) 20 | } 21 | wg.Done() 22 | }(f) 23 | } 24 | wg.Wait() 25 | 26 | fmt.Println("Words that appear more than once:") 27 | for word, count := range w.found { 28 | if count > 1 { 29 | fmt.Printf("%s: %d\n", word, count) 30 | } 31 | } 32 | } 33 | 34 | type words struct { 35 | found map[string]int 36 | } 37 | 38 | func newWords() *words { 39 | return &words{found: map[string]int{}} 40 | } 41 | 42 | func (w *words) add(word string, n int) { 43 | count, ok := w.found[word] 44 | if !ok { 45 | w.found[word] = n 46 | return 47 | } 48 | w.found[word] = count + n 49 | } 50 | 51 | func tallyWords(filename string, dict *words) error { 52 | file, err := os.Open(filename) 53 | if err != nil { 54 | return err 55 | } 56 | defer file.Close() 57 | 58 | scanner := bufio.NewScanner(file) 59 | scanner.Split(bufio.ScanWords) 60 | for scanner.Scan() { 61 | word := strings.ToLower(scanner.Text()) 62 | dict.add(word, 1) 63 | } 64 | return scanner.Err() 65 | } 66 | -------------------------------------------------------------------------------- /chapter3/scratch/closingstuff.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "time" 4 | 5 | func main() { 6 | ch := make(chan bool) 7 | timeout := time.After(600 * time.Millisecond) 8 | go send(ch) 9 | for { 10 | select { 11 | case <-ch: 12 | println("Got message.") 13 | case <-timeout: 14 | println("Time out") 15 | return 16 | default: 17 | println("*yawn*") 18 | time.Sleep(100 * time.Millisecond) 19 | } 20 | } 21 | } 22 | 23 | func send(ch chan bool) { 24 | time.Sleep(120 * time.Millisecond) 25 | ch <- true 26 | close(ch) 27 | println("Sent and closed") 28 | } 29 | -------------------------------------------------------------------------------- /chapter3/scratch/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | func main() { 8 | //time.Sleep(5 * time.Second) 9 | 10 | //sleep := time.After(5 * time.Second) 11 | //<-sleep 12 | //pp := make(chan string) 13 | //go pingpong(pp, true) 14 | //go pingpong(pp, false) 15 | ch := make(chan bool) 16 | go ping(ch) 17 | go pong(ch) 18 | ch <- true 19 | select {} 20 | } 21 | 22 | func ping(ch chan bool) { 23 | for { 24 | select { 25 | case <-ch: 26 | println("ping") 27 | ch <- true 28 | time.Sleep(200 * time.Millisecond) 29 | } 30 | } 31 | } 32 | 33 | func pong(ch chan bool) { 34 | for { 35 | select { 36 | case <-ch: 37 | println("pong") 38 | ch <- true 39 | time.Sleep(200 * time.Millisecond) 40 | } 41 | } 42 | } 43 | 44 | func pingpong(ch chan string, start bool) { 45 | if start { 46 | ch <- "ping" 47 | } 48 | 49 | for { 50 | select { 51 | case str := <-ch: 52 | println(str) 53 | if str == "ping" { 54 | ch <- "pong" 55 | time.Sleep(200 * time.Millisecond) 56 | continue 57 | } 58 | ch <- "ping" 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /chapter3/simplelock/lock.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | lock := make(chan bool, 1) 10 | for i := 1; i < 7; i++ { 11 | go worker(i, lock) 12 | } 13 | time.Sleep(10 * time.Second) 14 | } 15 | 16 | func worker(id int, lock chan bool) { 17 | fmt.Printf("%d wants the lock\n", id) 18 | lock <- true 19 | fmt.Printf("%d has the lock\n", id) 20 | time.Sleep(500 * time.Millisecond) 21 | fmt.Printf("%d is releasing the lock\n", id) 22 | <-lock 23 | } 24 | -------------------------------------------------------------------------------- /chapter3/waitgroup/exampledata/example1.txt: -------------------------------------------------------------------------------- 1 | how are you today? I am fine, thanks. 2 | -------------------------------------------------------------------------------- /chapter3/waitgroup/exampledata/example1.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Masterminds/go-in-practice/e9313183d93763f3bf558ffa6c062ebc3d0179fb/chapter3/waitgroup/exampledata/example1.txt.gz -------------------------------------------------------------------------------- /chapter3/waitgroup/exampledata/example2.txt: -------------------------------------------------------------------------------- 1 | how are you today? I am fine, thanks. 2 | -------------------------------------------------------------------------------- /chapter3/waitgroup/exampledata/example2.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Masterminds/go-in-practice/e9313183d93763f3bf558ffa6c062ebc3d0179fb/chapter3/waitgroup/exampledata/example2.txt.gz -------------------------------------------------------------------------------- /chapter3/waitgroup/exampledata/example3.txt: -------------------------------------------------------------------------------- 1 | how are you today? I am fine, thanks. 2 | -------------------------------------------------------------------------------- /chapter3/waitgroup/exampledata/example3.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Masterminds/go-in-practice/e9313183d93763f3bf558ffa6c062ebc3d0179fb/chapter3/waitgroup/exampledata/example3.txt.gz -------------------------------------------------------------------------------- /chapter3/waitgroup/simple_gz.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "compress/gzip" 5 | "io" 6 | "os" 7 | //"sync" 8 | ) 9 | 10 | func main() { 11 | for _, file := range os.Args { 12 | compress(file) 13 | } 14 | //var wg sync.WaitGroup 15 | } 16 | 17 | func compress(filename string) error { 18 | in, err := os.Open(filename) 19 | if err != nil { 20 | return err 21 | } 22 | defer in.Close() 23 | 24 | out, err := os.Create(filename + ".gz") 25 | if err != nil { 26 | return err 27 | } 28 | defer out.Close() 29 | 30 | gzout := gzip.NewWriter(out) 31 | _, err = io.Copy(gzout, in) 32 | gzout.Close() 33 | 34 | return err 35 | } 36 | -------------------------------------------------------------------------------- /chapter3/waitgroup/wg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Masterminds/go-in-practice/e9313183d93763f3bf558ffa6c062ebc3d0179fb/chapter3/waitgroup/wg -------------------------------------------------------------------------------- /chapter3/waitgroup/wg.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "compress/gzip" 5 | "fmt" 6 | "io" 7 | "os" 8 | "sync" 9 | ) 10 | 11 | func main() { 12 | var wg sync.WaitGroup 13 | var i int 14 | var file string 15 | for i, file = range os.Args[1:] { 16 | wg.Add(1) 17 | go func(filename string) { 18 | compress(filename) 19 | wg.Done() 20 | }(file) 21 | } 22 | wg.Wait() 23 | 24 | fmt.Printf("Compressed %d files\n", i+1) 25 | } 26 | 27 | func compress(filename string) error { 28 | fmt.Printf("Compressing %s\n", filename) 29 | in, err := os.Open(filename) 30 | if err != nil { 31 | return err 32 | } 33 | defer in.Close() 34 | 35 | out, err := os.Create(filename + ".gz") 36 | if err != nil { 37 | return err 38 | } 39 | defer out.Close() 40 | 41 | gzout := gzip.NewWriter(out) 42 | _, err = io.Copy(gzout, in) 43 | gzout.Close() 44 | 45 | return err 46 | } 47 | -------------------------------------------------------------------------------- /chapter3_original_depenency_management/README.md: -------------------------------------------------------------------------------- 1 | In the original version of Go In Practice, we wrote an entire chapter on 2 | dependency management. But during the course of writing the book, Go 1.5 3 | was released, which (in our opinion) solves most of the problems we were 4 | dealing with when we first wrote the book. 5 | -------------------------------------------------------------------------------- /chapter3_original_depenency_management/mygodep/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/Masterminds/cookoo" 6 | ) 7 | 8 | func main() { 9 | fmt.Println("Hello Godep!") 10 | _, _, cxt := cookoo.Cookoo() 11 | 12 | cxt.Log("info", "OH HAI") 13 | } 14 | -------------------------------------------------------------------------------- /chapter3_original_depenency_management/myproject/glide.yaml: -------------------------------------------------------------------------------- 1 | package: myproject 2 | import: 3 | - package: github.com/Masterminds/cookoo 4 | repo: git@github.com:Masterminds/cookoo.git 5 | ref: master 6 | vcs: git 7 | -------------------------------------------------------------------------------- /chapter3_original_depenency_management/myproject/history.txt: -------------------------------------------------------------------------------- 1 | $ mkdir myproject 2 | $ cd myproject 3 | $ glide init 4 | [INFO] Your new GOPATH is /Users/matt/Code/myproject/_vendor. Run 'glide gopath' to see it again._ 5 | [INFO] Initialized. You can now edit 'glide.yaml' 6 | $ vi glide.yaml 7 | $ glide in 8 | >> You are now gliding into a new shell. To exit, type 'exit' 9 | $ glide install 10 | [INFO] Installing github.com/Masterminds/cookoo with Git (From 11 | git@github.com:Masterminds/cookoo.git) 12 | ... 13 | [INFO] Building dependencies. 14 | [INFO] Running go build github.com/Masterminds/cookoo 15 | $ 16 | -------------------------------------------------------------------------------- /chapter4/closure_scope.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | var msg string 7 | defer func() { 8 | fmt.Println(msg) 9 | }() 10 | msg = "Hello world" 11 | } 12 | -------------------------------------------------------------------------------- /chapter4/data.csv: -------------------------------------------------------------------------------- 1 | one,two 2 | hello,world 3 | goodbye,world 4 | -------------------------------------------------------------------------------- /chapter4/error_example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "strings" 8 | ) 9 | 10 | // Concat concatenates a bunch of strings. 11 | // Strings are separated by spaces. 12 | // It returns an empty string and an error if no strings were passed in. 13 | func Concat(parts ...string) (string, error) { 14 | if len(parts) == 0 { 15 | return "", errors.New("No strings supplied") 16 | } 17 | 18 | return strings.Join(parts, " "), nil 19 | } 20 | 21 | func main() { 22 | 23 | args := os.Args[1:] 24 | 25 | /* 26 | if result, err := Concat(args...); err != nil { 27 | fmt.Printf("Error: %s\n", err) 28 | } else { 29 | fmt.Printf("Concatenated string: '%s'\n", result) 30 | } 31 | */ 32 | result, _ := Concat(args...) 33 | fmt.Printf("Concatenated string: '%s'\n", result) 34 | 35 | } 36 | 37 | func assumeGoodDesign() { 38 | // Streamlined error handling 39 | batch := []string{} 40 | result, _ := Concat(batch...) 41 | fmt.Printf("Concatenated string: '%s'\n", result) 42 | 43 | } 44 | -------------------------------------------------------------------------------- /chapter4/http_server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "net/http" 6 | ) 7 | 8 | func main() { 9 | http.HandleFunc("/", handler) 10 | http.ListenAndServe(":8080", nil) 11 | } 12 | func handler(res http.ResponseWriter, req *http.Request) { 13 | panic(errors.New("Fake panic!")) 14 | } 15 | -------------------------------------------------------------------------------- /chapter4/parser_error.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | 7 | err := &ParseError{ 8 | Message: "Unexpected char ';'", 9 | Line: 5, 10 | Char: 38, 11 | } 12 | 13 | fmt.Println(err.Error()) 14 | } 15 | 16 | type ParseError struct { 17 | Message string 18 | Line, Char int 19 | } 20 | 21 | func (p *ParseError) Error() string { 22 | format := "%s on Line %d, Char %d" 23 | return fmt.Sprintf(format, p.Message, p.Line, p.Char) 24 | } 25 | -------------------------------------------------------------------------------- /chapter4/proper_panic.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "errors" 4 | 5 | func main() { 6 | panic(errors.New("Something bad happened.")) 7 | } 8 | -------------------------------------------------------------------------------- /chapter4/recover_panic.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | func main() { 9 | msg := "Everything's fine" 10 | defer func() { 11 | if err := recover(); err != nil { 12 | fmt.Printf("Trapped panic: %s (%T)\n", err, err) 13 | } 14 | fmt.Println(msg) 15 | }() 16 | 17 | yikes() 18 | } 19 | 20 | func yikes() { 21 | panic(errors.New("Something bad happened.")) 22 | } 23 | -------------------------------------------------------------------------------- /chapter4/safely/safely.go: -------------------------------------------------------------------------------- 1 | package safely 2 | 3 | import ( 4 | "log" 5 | ) 6 | 7 | type GoDoer func() 8 | 9 | func Go(todo GoDoer) { 10 | go func() { 11 | defer func() { 12 | if err := recover(); err != nil { 13 | log.Printf("Panic in safely.Go: %s", err) 14 | } 15 | }() 16 | todo() 17 | }() 18 | } 19 | -------------------------------------------------------------------------------- /chapter4/safely_example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "./safely" 5 | "errors" 6 | "time" 7 | ) 8 | 9 | func message() { 10 | println("Inside goroutine") 11 | panic(errors.New("Oops!")) 12 | } 13 | 14 | func main() { 15 | safely.Go(message) 16 | println("Outside goroutine") 17 | time.Sleep(200) 18 | } 19 | -------------------------------------------------------------------------------- /chapter4/same_error.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "errors" 4 | 5 | func main() { 6 | errA := errors.New("Error") 7 | //errB := errors.New("Error") 8 | 9 | if errA != errA { 10 | panic("Expected same!") 11 | } 12 | println("Done") 13 | } 14 | -------------------------------------------------------------------------------- /chapter4/simple_defer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | defer goodbye() 7 | 8 | fmt.Println("Hello world.") 9 | } 10 | 11 | func goodbye() { 12 | fmt.Println("Goodbye") 13 | } 14 | -------------------------------------------------------------------------------- /chapter4/simple_server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "errors" 6 | "fmt" 7 | "net" 8 | ) 9 | 10 | func main() { 11 | listen() 12 | } 13 | 14 | func listen() { 15 | listener, err := net.Listen("tcp", ":1026") 16 | if err != nil { 17 | fmt.Println("Failed to open port on 1026") 18 | return 19 | } 20 | for { 21 | conn, err := listener.Accept() 22 | if err != nil { 23 | fmt.Println("Error accepting connection") 24 | continue 25 | } 26 | 27 | go handle(conn) 28 | } 29 | } 30 | 31 | func handle(conn net.Conn) { 32 | defer func() { 33 | if err := recover(); err != nil { 34 | fmt.Printf("Fatal error: %s", err) 35 | } 36 | conn.Close() 37 | }() 38 | reader := bufio.NewReader(conn) 39 | 40 | data, err := reader.ReadBytes('\n') 41 | if err != nil { 42 | fmt.Println("Failed to read from socket.") 43 | } 44 | 45 | response(data, conn) 46 | } 47 | 48 | func response(data []byte, conn net.Conn) { 49 | conn.Write(data) 50 | panic(errors.New("Pretend I'm a real error")) 51 | } 52 | -------------------------------------------------------------------------------- /chapter4/simple_server_start.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "errors" 6 | "fmt" 7 | "net" 8 | ) 9 | 10 | func main() { 11 | listen() 12 | } 13 | 14 | func listen() { 15 | defer func() { 16 | if err := recover(); err != nil { 17 | fmt.Printf("Caught a panic and recovered") 18 | } 19 | }() 20 | listener, err := net.Listen("tcp", ":1026") 21 | if err != nil { 22 | fmt.Println("Failed to open port on 1026") 23 | return 24 | } 25 | for { 26 | conn, err := listener.Accept() 27 | if err != nil { 28 | fmt.Println("Error accepting connection") 29 | continue 30 | } 31 | 32 | go handle(conn) 33 | } 34 | } 35 | 36 | func handle(conn net.Conn) { 37 | reader := bufio.NewReader(conn) 38 | 39 | data, err := reader.ReadBytes('\n') 40 | if err != nil { 41 | fmt.Println("Failed to read from socket.") 42 | conn.Close() 43 | } 44 | 45 | response(data, conn) 46 | } 47 | 48 | func response(data []byte, conn net.Conn) { 49 | defer func() { 50 | conn.Close() 51 | }() 52 | conn.Write(data) 53 | panic(errors.New("Failure in response!")) 54 | } 55 | -------------------------------------------------------------------------------- /chapter4/two_defers.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | defer func() { println("a") }() 5 | defer func() { println("b") }() 6 | } 7 | -------------------------------------------------------------------------------- /chapter4/two_errors.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "math/rand" 7 | ) 8 | 9 | var ErrTimeout = errors.New("The request timed out") 10 | var ErrRejected = errors.New("The request was rejected") 11 | 12 | var random = rand.New(rand.NewSource(35)) 13 | 14 | //var random = rand.New(rand.NewSource(78)) 15 | 16 | func main() { 17 | response, err := SendRequest("Hello") 18 | for err == ErrTimeout { 19 | fmt.Println("Timeout. Retrying.") 20 | response, err = SendRequest("Hello") 21 | } 22 | if err != nil { 23 | fmt.Println(err) 24 | } else { 25 | fmt.Println(response) 26 | } 27 | } 28 | 29 | func SendRequest(req string) (string, error) { 30 | 31 | switch random.Int() % 3 { 32 | case 0: 33 | return "Success", nil 34 | case 1: 35 | return "", ErrRejected 36 | default: 37 | return "", ErrTimeout 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /chapter4/useful_recover.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io" 7 | "os" 8 | ) 9 | 10 | func main() { 11 | var file io.ReadCloser 12 | file, err := OpenCSV("data.csv") 13 | if err != nil { 14 | fmt.Printf("Error: %s", err) 15 | return 16 | } 17 | defer file.Close() 18 | 19 | // Do something with file. 20 | 21 | } 22 | 23 | func OpenCSV(filename string) (file *os.File, err error) { 24 | defer func() { 25 | if r := recover(); r != nil { 26 | file.Close() 27 | err = r.(error) 28 | } 29 | }() 30 | 31 | file, err = os.Open(filename) 32 | if err != nil { 33 | fmt.Printf("Failed to open file\n") 34 | return file, err 35 | } 36 | 37 | RemoveEmptyLines(file) 38 | 39 | return file, err 40 | } 41 | 42 | func RemoveEmptyLines(f *os.File) { 43 | panic(errors.New("Failed parse")) 44 | } 45 | -------------------------------------------------------------------------------- /chapter4/zero_divider.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | var ErrDivideByZero = errors.New("Can't divide by zero") 9 | 10 | func main() { 11 | fmt.Println("Divide 1 by 0") 12 | _, err := precheckDivide(1, 0) 13 | if err != nil { 14 | fmt.Printf("Error: %s\n", err) 15 | } 16 | 17 | fmt.Println("Divide 2 by 0") 18 | divide(2, 0) 19 | } 20 | 21 | func precheckDivide(a, b int) (int, error) { 22 | if b == 0 { 23 | return 0, ErrDivideByZero 24 | } 25 | return divide(a, b), nil 26 | } 27 | 28 | func divide(a, b int) int { 29 | return a / b 30 | } 31 | -------------------------------------------------------------------------------- /chapter5/delvetest/.dbg_history: -------------------------------------------------------------------------------- 1 | c 2 | quit 3 | exit 4 | -------------------------------------------------------------------------------- /chapter5/delvetest/debug: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Masterminds/go-in-practice/e9313183d93763f3bf558ffa6c062ebc3d0179fb/chapter5/delvetest/debug -------------------------------------------------------------------------------- /chapter5/delvetest/delvetest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Masterminds/go-in-practice/e9313183d93763f3bf558ffa6c062ebc3d0179fb/chapter5/delvetest/delvetest -------------------------------------------------------------------------------- /chapter5/delvetest/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Masterminds/go-in-practice/e9313183d93763f3bf558ffa6c062ebc3d0179fb/chapter5/delvetest/main -------------------------------------------------------------------------------- /chapter5/delvetest/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | time.Sleep(2 * time.Second) 10 | fmt.Println("OH HAI!") 11 | } 12 | -------------------------------------------------------------------------------- /chapter5/logs/buffered_logger.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "log" 7 | "net" 8 | "runtime" 9 | "sync" 10 | "time" 11 | ) 12 | 13 | func main() { 14 | runtime.GOMAXPROCS(runtime.NumCPU()) 15 | 16 | logger := New("localhost:1902", 30*time.Second) 17 | 18 | var wg sync.WaitGroup 19 | wg.Add(20) 20 | 21 | for i := 0; i < 20; i++ { 22 | 23 | go func(logger *TcpLogger, id int) { 24 | for j := 0; j < 100; j++ { 25 | logger.Printf("Client %d message %d.", id, j) 26 | time.Sleep(1 * time.Second) 27 | } 28 | wg.Done() 29 | }(logger, i) 30 | } 31 | 32 | wg.Wait() 33 | } 34 | 35 | type TcpLogger struct { 36 | *log.Logger 37 | Addr string 38 | Timeout time.Duration 39 | Queue *logQueue 40 | } 41 | 42 | func New(addr string, timeout time.Duration) *TcpLogger { 43 | conn, err := net.DialTimeout("tcp", addr, timeout) 44 | if err != nil { 45 | panic("Failed to connect to localhost:1902") 46 | } 47 | 48 | buff := make(chan []byte, 10) 49 | queue := &logQueue{conn, buff} 50 | 51 | f := log.Ldate | log.Lshortfile 52 | logger := log.New(queue, "example ", f) 53 | 54 | l := &TcpLogger{ 55 | logger, 56 | addr, 57 | timeout, 58 | queue, 59 | } 60 | 61 | go DequeueLogs(l) 62 | 63 | return l 64 | } 65 | 66 | func (t *TcpLogger) Reconnect() error { 67 | limit := 10 68 | for i := 0; i < limit; i++ { 69 | conn, err := net.DialTimeout("tcp", t.Addr, t.Timeout) 70 | if err == nil { 71 | t.Queue.Destination = conn 72 | return nil 73 | } 74 | time.Sleep(100 * time.Millisecond) 75 | } 76 | 77 | msg := "Failed to reconnect after %d tries." 78 | return fmt.Errorf(msg, limit) 79 | } 80 | 81 | type logQueue struct { 82 | Destination io.Writer 83 | Messages chan []byte 84 | } 85 | 86 | func (l *logQueue) Write(data []byte) (int, error) { 87 | 88 | // Write can never modify data, so we copy it. 89 | msg := make([]byte, len(data)) 90 | copy(msg, data) 91 | 92 | l.Messages <- msg 93 | 94 | return len(data), nil 95 | } 96 | 97 | func DequeueLogs(logger *TcpLogger) { 98 | for msg := range logger.Queue.Messages { 99 | if _, e := logger.Queue.Destination.Write(msg); e != nil { 100 | fmt.Println("Attempting reconnect.") 101 | if e := logger.Reconnect(); e != nil { 102 | fmt.Printf("Failed reconnect. Dropping message. Msg: %s", msg) 103 | } else { 104 | fmt.Println("Reconnected. Resending messages.") 105 | logger.Queue.Destination.Write(msg) 106 | } 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /chapter5/logs/log.txt: -------------------------------------------------------------------------------- 1 | example 2015/05/12 09:30:58.166855 outfile.go:19: This is a regular message. 2 | example 2015/05/12 09:30:58.167120 outfile.go:20: This is a fatal error. 3 | -------------------------------------------------------------------------------- /chapter5/logs/log_client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net" 6 | ) 7 | 8 | func main() { 9 | 10 | conn, err := net.Dial("tcp", "localhost:1902") 11 | if err != nil { 12 | panic("Failed to connect to localhost:1902") 13 | } 14 | defer conn.Close() 15 | 16 | f := log.Ldate | log.Lshortfile 17 | logger := log.New(conn, "example ", f) 18 | 19 | //logger := log.New(logfile, "example ", log.LstdFlags|log.Lshortfile) 20 | 21 | logger.Println("This is a regular message.") 22 | logger.Panicln("This is a panic.") 23 | } 24 | -------------------------------------------------------------------------------- /chapter5/logs/log_server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "net" 8 | ) 9 | 10 | // Simple log server 11 | func main() { 12 | 13 | listener, err := net.Listen("tcp", ":1902") 14 | if err != nil { 15 | panic(err) 16 | } 17 | 18 | for { 19 | conn, err := listener.Accept() 20 | if err != nil { 21 | fmt.Printf("Failed to accept: %s", err) 22 | } 23 | printmsg(conn) 24 | } 25 | } 26 | 27 | func printmsg(conn net.Conn) { 28 | var err error 29 | var msg string 30 | for { 31 | msg, err = bufio.NewReader(conn).ReadString('\n') 32 | if err != nil { 33 | if err != io.EOF { 34 | fmt.Printf("Error reading message: %s\n", err) 35 | } 36 | return 37 | } 38 | fmt.Println(msg) 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /chapter5/logs/outfile.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | ) 7 | 8 | func main() { 9 | logfile, _ := os.Create("./log.txt") 10 | defer logfile.Close() 11 | 12 | f := log.Ldate | log.Lmicroseconds | log.Lshortfile | log.Llongfile 13 | logger := log.New(logfile, "example ", f) 14 | 15 | //logger := log.New(logfile, "example ", log.LstdFlags|log.Lshortfile) 16 | 17 | logger.Println("This is a regular message.") 18 | logger.Fatalln("This is a fatal error.") 19 | logger.Println("This is the end of the function.") 20 | } 21 | -------------------------------------------------------------------------------- /chapter5/logs/simple.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | ) 6 | 7 | func main() { 8 | log.Println("This is a regular message.") 9 | log.Fatalln("This is a fatal error.") 10 | log.Println("This is the end of the function.") 11 | } 12 | -------------------------------------------------------------------------------- /chapter5/logs/syslog.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log/syslog" 5 | ) 6 | 7 | func main() { 8 | logger, err := syslog.New(syslog.LOG_LOCAL3, "narwhal") 9 | if err != nil { 10 | panic("Cannot attach to syslog") 11 | } 12 | 13 | logger.Debug("Debug message.") 14 | logger.Notice("Notice message.") 15 | logger.Warning("Warning message.") 16 | logger.Alert("Alert message.") 17 | } 18 | -------------------------------------------------------------------------------- /chapter5/logs/syslog_logger.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "log/syslog" 7 | ) 8 | 9 | func main() { 10 | priority := syslog.LOG_LOCAL3 | syslog.LOG_NOTICE 11 | flags := log.Ldate | log.Lshortfile 12 | logger, err := syslog.NewLogger(priority, flags) 13 | if err != nil { 14 | fmt.Printf("Can't attach to syslog: %s", err) 15 | return 16 | } 17 | 18 | logger.Println("This is a test log message.") 19 | } 20 | -------------------------------------------------------------------------------- /chapter5/logs/udp_logger.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | 11 | timeout := 30 * time.Second 12 | conn, err := net.DialTimeout("udp", "localhost:1902", timeout) 13 | if err != nil { 14 | panic("Failed to connect to localhost:1902") 15 | } 16 | defer conn.Close() 17 | 18 | f := log.Ldate | log.Lshortfile 19 | logger := log.New(conn, "example ", f) 20 | 21 | //logger := log.New(logfile, "example ", log.LstdFlags|log.Lshortfile) 22 | 23 | logger.Println("This is a regular message.") 24 | logger.Panicln("This is a panic.") 25 | } 26 | -------------------------------------------------------------------------------- /chapter5/stack/trace.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | ) 7 | 8 | func main() { 9 | foo() 10 | } 11 | 12 | func foo() { 13 | bar() 14 | } 15 | 16 | func bar() { 17 | buf := make([]byte, 1024) 18 | runtime.Stack(buf, false) 19 | fmt.Printf("Trace:\n %s\n", buf) 20 | 21 | caller, file, line, _ := runtime.Caller(0) 22 | fmt.Printf("Ptr: %d File: %s, Line: %d", caller, file, line) 23 | } 24 | -------------------------------------------------------------------------------- /chapter5/tests/bench/bench_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | "text/template" 7 | ) 8 | 9 | func BenchmarkTemplates(b *testing.B) { 10 | // b.Logf("b.N is %d\n", b.N) 11 | tpl := "Hello {{.Name}}" 12 | data := &map[string]string{ 13 | "Name": "World", 14 | } 15 | var buf bytes.Buffer 16 | for i := 0; i < b.N; i++ { 17 | t, _ := template.New("test").Parse(tpl) 18 | t.Execute(&buf, data) 19 | buf.Reset() 20 | } 21 | } 22 | func BenchmarkCompiledTemplates(b *testing.B) { 23 | // b.Logf("b.N is %d\n", b.N) 24 | tpl := "Hello {{.Name}}" 25 | t, _ := template.New("test").Parse(tpl) 26 | data := &map[string]string{ 27 | "Name": "World", 28 | } 29 | var buf bytes.Buffer 30 | for i := 0; i < b.N; i++ { 31 | t.Execute(&buf, data) 32 | buf.Reset() 33 | } 34 | } 35 | 36 | func BenchmarkParallelTemplates(b *testing.B) { 37 | tpl := "Hello {{.Name}}" 38 | t, _ := template.New("test").Parse(tpl) 39 | data := &map[string]string{ 40 | "Name": "World", 41 | } 42 | b.RunParallel(func(pb *testing.PB) { 43 | var buf bytes.Buffer 44 | for pb.Next() { 45 | t.Execute(&buf, data) 46 | buf.Reset() 47 | } 48 | }) 49 | } 50 | 51 | func BenchmarkParallelOops(b *testing.B) { 52 | tpl := "Hello {{.Name}}" 53 | t, _ := template.New("test").Parse(tpl) 54 | data := &map[string]string{ 55 | "Name": "World", 56 | } 57 | var buf bytes.Buffer 58 | b.RunParallel(func(pb *testing.PB) { 59 | for pb.Next() { 60 | t.Execute(&buf, data) 61 | buf.Reset() 62 | } 63 | }) 64 | } 65 | -------------------------------------------------------------------------------- /chapter5/tests/canary/canary_test.go: -------------------------------------------------------------------------------- 1 | package canary 2 | 3 | import ( 4 | "io" 5 | "testing" 6 | ) 7 | 8 | type MyWriter struct{} 9 | 10 | func (m *MyWriter) Write([]byte) error { 11 | return nil 12 | } 13 | 14 | func main() { 15 | m := map[string]interface{}{ 16 | "w": &MyWriter{}, 17 | } 18 | } 19 | 20 | func doSomething(m map[string]interface{}) { 21 | w := m["w"].(io.Writer) 22 | } 23 | 24 | func TestWriter(t *testing.T) { 25 | var _ io.Writer = &MyWriter{} 26 | } 27 | -------------------------------------------------------------------------------- /chapter5/tests/generative/generative_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "strings" 6 | "testing" 7 | "testing/quick" 8 | ) 9 | 10 | // Pad whitespace-pads a string to a given length. 11 | // 12 | // If the string is longer than that, it truncates. 13 | func Pad(s string, max uint) string { 14 | log.Printf("Testing Len: %d, Str: %s\n", max, s) 15 | l := uint(len(s)) 16 | if l > max { 17 | return s[:max] 18 | } 19 | s += strings.Repeat(" ", int(max-l)) 20 | return s 21 | } 22 | 23 | func TestPad(t *testing.T) { 24 | if r := Pad("test", 6); len(r) != 6 { 25 | t.Errorf("Expected 6, got %d", len(r)) 26 | } 27 | } 28 | 29 | func TestPadGenerative(t *testing.T) { 30 | fn := func(s string, max uint8) bool { 31 | p := Pad(s, uint(max)) 32 | return len(p) == int(max) 33 | } 34 | 35 | if err := quick.Check(fn, &quick.Config{MaxCount: 200}); err != nil { 36 | t.Error(err) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /chapter5/tests/hello/unit.go: -------------------------------------------------------------------------------- 1 | package hello 2 | 3 | func Hello() string { 4 | return "hello" 5 | } 6 | -------------------------------------------------------------------------------- /chapter5/tests/hello/unit_test.go: -------------------------------------------------------------------------------- 1 | package hello 2 | 3 | import "testing" 4 | 5 | func TestHello(t *testing.T) { 6 | if Hello() != "hello" { 7 | t.Errorf("Expected 'hello', but got '%s'", Hello()) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /chapter5/tests/msg/send_message.go: -------------------------------------------------------------------------------- 1 | package msg 2 | 3 | type Message struct { 4 | // ... 5 | } 6 | 7 | func (m *Message) Send(email, subject string, body []byte) error { 8 | // ... 9 | return nil 10 | } 11 | 12 | type Messager interface { 13 | Send(email, subject string, body []byte) error 14 | } 15 | 16 | func Alert(m Messager, problem []byte) error { 17 | return m.Send("noc@example.com", "Critical Error", problem) 18 | } 19 | -------------------------------------------------------------------------------- /chapter5/tests/msg/send_message_test.go: -------------------------------------------------------------------------------- 1 | package msg 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | type MockMessage struct { 8 | email, subject string 9 | body []byte 10 | } 11 | 12 | func (m *MockMessage) Send(email, subject string, body []byte) error { 13 | m.email = email 14 | m.subject = subject 15 | m.body = body 16 | return nil 17 | } 18 | 19 | func TestAlert(t *testing.T) { 20 | msgr := new(MockMessage) 21 | body := []byte("Critical Error") 22 | 23 | Alert(msgr, body) 24 | 25 | if msgr.subject != "Critical Error" { 26 | t.Errorf("Expected 'Critical Error', Got '%s'", msgr.subject) 27 | } 28 | // ... 29 | } 30 | -------------------------------------------------------------------------------- /chapter6/buffered_template.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "html/template" 7 | "net/http" 8 | ) 9 | 10 | var t *template.Template 11 | 12 | func init() { 13 | t = template.Must(template.ParseFiles("./templates/simple.html")) 14 | } 15 | 16 | type Page struct { 17 | Title, Content string 18 | } 19 | 20 | func diaplayPage(w http.ResponseWriter, r *http.Request) { 21 | p := &Page{ 22 | Title: "An Example", 23 | Content: "Have fun stormin’ da castle.", 24 | } 25 | var b bytes.Buffer 26 | err := t.Execute(&b, p) 27 | if err != nil { 28 | fmt.Fprint(w, "A error occured.") 29 | return 30 | } 31 | b.WriteTo(w) 32 | } 33 | 34 | func main() { 35 | http.HandleFunc("/", diaplayPage) 36 | http.ListenAndServe(":8080", nil) 37 | } 38 | -------------------------------------------------------------------------------- /chapter6/cache_template.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "html/template" 5 | "net/http" 6 | ) 7 | 8 | var t = template.Must(template.ParseFiles("./templates/simple.html")) 9 | 10 | type Page struct { 11 | Title, Content string 12 | } 13 | 14 | func diaplayPage(w http.ResponseWriter, r *http.Request) { 15 | p := &Page{ 16 | Title: "An Example", 17 | Content: "Have fun stormin’ da castle.", 18 | } 19 | t.Execute(w, p) 20 | } 21 | 22 | func main() { 23 | http.HandleFunc("/", diaplayPage) 24 | http.ListenAndServe(":8080", nil) 25 | } 26 | -------------------------------------------------------------------------------- /chapter6/date_command.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "html/template" 5 | "net/http" 6 | "time" 7 | ) 8 | 9 | var tpl = ` 10 | 11 |
12 | 13 |{{.Date | dateFormat "Jan 2, 2006"}}
17 | 18 | ` 19 | 20 | var funcMap = template.FuncMap{ 21 | "dateFormat": dateFormat, 22 | } 23 | 24 | func dateFormat(layout string, d time.Time) string { 25 | return d.Format(layout) 26 | } 27 | 28 | func serveTemplate(res http.ResponseWriter, req *http.Request) { 29 | t := template.New("date") 30 | t.Funcs(funcMap) 31 | t.Parse(tpl) 32 | data := struct{ Date time.Time }{ 33 | Date: time.Now(), 34 | } 35 | t.Execute(res, data) 36 | } 37 | 38 | func main() { 39 | http.HandleFunc("/", serveTemplate) 40 | http.ListenAndServe(":8080", nil) 41 | } 42 | -------------------------------------------------------------------------------- /chapter6/email.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "net/smtp" 7 | "strconv" 8 | "text/template" 9 | ) 10 | 11 | type EmailMessage struct { 12 | From, Subject, Body string 13 | To []string 14 | } 15 | 16 | type EmailCredentials struct { 17 | Username, Password, Server string 18 | Port int 19 | } 20 | 21 | const emailTemplate = `From: {{.From}} 22 | To: {{.To}} 23 | Subject {{.Subject}} 24 | 25 | {{.Body}} 26 | ` 27 | 28 | var t *template.Template 29 | 30 | func init() { 31 | t = template.New("email") 32 | t.Parse(emailTemplate) 33 | } 34 | 35 | func main() { 36 | message := &EmailMessage{ 37 | From: "me@example.com", 38 | To: []string{"you@example.com"}, 39 | Subject: "A test", 40 | Body: "Just saying hi", 41 | } 42 | 43 | var body bytes.Buffer 44 | t.Execute(&body, message) 45 | 46 | fmt.Printf("%s", body.Bytes()) 47 | 48 | authCreds := &EmailCredentials{ 49 | Username: "myUsername", 50 | Password: "myPass", 51 | Server: "smtp.example.com", 52 | Port: 25, 53 | } 54 | 55 | auth := smtp.PlainAuth("", 56 | authCreds.Username, 57 | authCreds.Password, 58 | authCreds.Server, 59 | ) 60 | 61 | smtp.SendMail(authCreds.Server+":"+strconv.Itoa(authCreds.Port), 62 | auth, 63 | message.From, 64 | message.To, 65 | body.Bytes()) 66 | } 67 | -------------------------------------------------------------------------------- /chapter6/inherit_templates/base.html: -------------------------------------------------------------------------------- 1 | {{define "base"}} 2 | 3 | 4 | 5 |4 | {{.Content}} 5 |
6 | {{end}} 7 | {{define "styles"}} 8 | 13 | {{end}} 14 | -------------------------------------------------------------------------------- /chapter6/inherit_templates/user.html: -------------------------------------------------------------------------------- 1 | {{define "title"}}User: {{.Username}}{{end}} 2 | {{define "content"}} 3 |{{.Content}}
7 | 8 | -------------------------------------------------------------------------------- /chapter6/nested_templates/nested_templates.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "html/template" 5 | "net/http" 6 | ) 7 | 8 | var t *template.Template 9 | 10 | func init() { 11 | t = template.Must(template.ParseFiles("index.html", "head.html")) 12 | } 13 | 14 | type Page struct { 15 | Title, Content string 16 | } 17 | 18 | func diaplayPage(w http.ResponseWriter, r *http.Request) { 19 | p := &Page{ 20 | Title: "An Example", 21 | Content: "Have fun stormin’ da castle.", 22 | } 23 | t.ExecuteTemplate(w, "index.html", p) 24 | } 25 | 26 | func main() { 27 | http.HandleFunc("/", diaplayPage) 28 | http.ListenAndServe(":8080", nil) 29 | } 30 | -------------------------------------------------------------------------------- /chapter6/object_templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |{{.Content}}
10 | 11 | -------------------------------------------------------------------------------- /chapter6/object_templates/object_templates.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "html/template" 6 | "net/http" 7 | ) 8 | 9 | var t *template.Template 10 | var qc template.HTML 11 | 12 | func init() { 13 | t = template.Must(template.ParseFiles("index.html", "quote.html")) 14 | } 15 | 16 | type Page struct { 17 | Title string 18 | Content template.HTML 19 | } 20 | 21 | type Quote struct { 22 | Quote, Person string 23 | } 24 | 25 | func main() { 26 | q := &Quote{ 27 | Quote: `You keep using that word. I do not think 28 | it means what you think it means.`, 29 | Person: "Inigo Montoya", 30 | } 31 | var b bytes.Buffer 32 | t.ExecuteTemplate(&b, "quote.html", q) 33 | qc = template.HTML(b.String()) 34 | 35 | http.HandleFunc("/", diaplayPage) 36 | http.ListenAndServe(":8080", nil) 37 | } 38 | 39 | func diaplayPage(w http.ResponseWriter, r *http.Request) { 40 | p := &Page{ 41 | Title: "A Quote", 42 | Content: qc, 43 | } 44 | t.ExecuteTemplate(w, "index.html", p) 45 | } 46 | -------------------------------------------------------------------------------- /chapter6/object_templates/quote.html: -------------------------------------------------------------------------------- 1 |2 | “{{.Quote}}” 3 | — {{.Person}} 4 |5 | 6 | -------------------------------------------------------------------------------- /chapter6/simple_template.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "html/template" 5 | "net/http" 6 | ) 7 | 8 | type Page struct { 9 | Title, Content string 10 | } 11 | 12 | func diaplayPage(w http.ResponseWriter, r *http.Request) { 13 | p := &Page{ 14 | Title: "An Example", 15 | Content: "Have fun stormin’ da castle.", 16 | } 17 | t := template.Must(template.ParseFiles("templates/simple.html")) 18 | t.Execute(w, p) 19 | } 20 | 21 | func main() { 22 | http.HandleFunc("/", diaplayPage) 23 | http.ListenAndServe(":8080", nil) 24 | } 25 | -------------------------------------------------------------------------------- /chapter6/templates/simple.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
{{.Content}}
10 | 11 | -------------------------------------------------------------------------------- /chapter7/cache_serving.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "net/http" 7 | "os" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | type cacheFile struct { 13 | content io.ReadSeeker 14 | modTime time.Time 15 | } 16 | 17 | var cache map[string]*cacheFile 18 | var mutex = new(sync.RWMutex) 19 | 20 | func main() { 21 | cache = make(map[string]*cacheFile) 22 | http.HandleFunc("/", serveFiles) 23 | http.ListenAndServe(":8080", nil) 24 | } 25 | 26 | func serveFiles(res http.ResponseWriter, req *http.Request) { 27 | mutex.RLock() 28 | v, found := cache[req.URL.Path] 29 | mutex.RUnlock() 30 | 31 | if !found { 32 | mutex.Lock() 33 | defer mutex.Unlock() 34 | fileName := "./files" + req.URL.Path 35 | f, err := os.Open(fileName) 36 | defer f.Close() 37 | 38 | if err != nil { 39 | http.NotFound(res, req) 40 | return 41 | } 42 | 43 | var b bytes.Buffer 44 | _, err = io.Copy(&b, f) 45 | if err != nil { 46 | http.NotFound(res, req) 47 | return 48 | } 49 | r := bytes.NewReader(b.Bytes()) 50 | 51 | info, _ := f.Stat() 52 | v = &cacheFile{ 53 | content: r, 54 | modTime: info.ModTime(), 55 | } 56 | cache[req.URL.Path] = v 57 | } 58 | 59 | http.ServeContent(res, req, req.URL.Path, v.modTime, v.content) 60 | } 61 | -------------------------------------------------------------------------------- /chapter7/embedded_files/embedded_files.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/GeertJohan/go.rice" 5 | "net/http" 6 | ) 7 | 8 | func main() { 9 | box := rice.MustFindBox("../files/") 10 | httpbox := box.HTTPBox() 11 | http.ListenAndServe(":8080", http.FileServer(httpbox)) 12 | } 13 | -------------------------------------------------------------------------------- /chapter7/file.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |A demo.
21 | 22 | ` 23 | 24 | func init() { 25 | t = template.Must(template.New("date").Parse(tpl)) 26 | } 27 | 28 | func servePage(res http.ResponseWriter, req *http.Request) { 29 | data := struct{ Location *string }{ 30 | Location: l, 31 | } 32 | t.Execute(res, data) 33 | } 34 | 35 | func main() { 36 | flag.Parse() 37 | http.HandleFunc("/", servePage) 38 | http.ListenAndServe(":8080", nil) 39 | } 40 | -------------------------------------------------------------------------------- /chapter7/serve_subdir_simple.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | ) 7 | 8 | func main() { 9 | dir := http.Dir("./files/") 10 | handler := http.StripPrefix("/static/", http.FileServer(dir)) 11 | http.Handle("/static/", handler) 12 | 13 | http.HandleFunc("/", homePage) 14 | http.ListenAndServe(":8080", nil) 15 | } 16 | 17 | func homePage(res http.ResponseWriter, req *http.Request) { 18 | if req.URL.Path != "/" { 19 | http.NotFound(res, req) 20 | return 21 | } 22 | fmt.Fprint(res, "The homepage.") 23 | } 24 | -------------------------------------------------------------------------------- /chapter7/servefile.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | func main() { 8 | http.HandleFunc("/", readme) 9 | http.ListenAndServe(":8080", nil) 10 | } 11 | 12 | func readme(res http.ResponseWriter, req *http.Request) { 13 | http.ServeFile(res, req, "./files/readme.txt") 14 | } 15 | -------------------------------------------------------------------------------- /chapter8/custom_client_timeout.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | "os" 8 | "time" 9 | ) 10 | 11 | func main() { 12 | cc := &http.Client{Timeout: time.Second} 13 | res, err := cc.Get("http://goinpracticebook.com") 14 | if err != nil { 15 | fmt.Println(err) 16 | os.Exit(1) 17 | } 18 | b, _ := ioutil.ReadAll(res.Body) 19 | res.Body.Close() 20 | fmt.Printf("%s", b) 21 | } 22 | -------------------------------------------------------------------------------- /chapter8/custom_request_delete.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | ) 7 | 8 | func main() { 9 | req, _ := http.NewRequest("DELETE", "http://example.com/foo/bar", nil) 10 | res, _ := http.DefaultClient.Do(req) 11 | fmt.Printf("%s", res.Status) 12 | } 13 | -------------------------------------------------------------------------------- /chapter8/get_custom_error.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "os" 9 | ) 10 | 11 | type Error struct { 12 | HTTPCode int `json:"-"` 13 | Code int `json:"code,omitempty"` 14 | Message string `json:"message"` 15 | } 16 | 17 | func (e Error) Error() string { 18 | fs := "HTTP: %d, Code: %d, Message: %s" 19 | return fmt.Sprintf(fs, e.HTTPCode, e.Code, e.Message) 20 | } 21 | 22 | func get(u string) (*http.Response, error) { 23 | res, err := http.Get(u) 24 | if err != nil { 25 | return res, err 26 | } 27 | 28 | if res.StatusCode < 200 || res.StatusCode >= 300 { 29 | if res.Header.Get("Content-Type") != "application/json" { 30 | sm := "Unknown error. HTTP status: %s" 31 | return res, fmt.Errorf(sm, res.Status) 32 | } 33 | b, _ := ioutil.ReadAll(res.Body) 34 | res.Body.Close() 35 | var data struct { 36 | Err Error `json:"error"` 37 | } 38 | err = json.Unmarshal(b, &data) 39 | if err != nil { 40 | sm := "Unable to parse json: %s. HTTP status: %s" 41 | return res, fmt.Errorf(sm, err, res.Status) 42 | } 43 | data.Err.HTTPCode = res.StatusCode 44 | 45 | return res, data.Err 46 | } 47 | 48 | return res, nil 49 | } 50 | 51 | func main() { 52 | res, err := get("http://localhost:8080") 53 | if err != nil { 54 | fmt.Println(err) 55 | os.Exit(1) 56 | } 57 | 58 | b, _ := ioutil.ReadAll(res.Body) 59 | res.Body.Close() 60 | fmt.Printf("%s", b) 61 | } 62 | -------------------------------------------------------------------------------- /chapter8/get_semantic_version_api.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | ) 8 | 9 | func main() { 10 | ct := "application/vnd.mytodos.json; version=2.0" 11 | req, _ := http.NewRequest("GET", "http://localhost:8080/test", nil) 12 | req.Header.Set("Accept", ct) 13 | res, _ := http.DefaultClient.Do(req) 14 | if res.Header.Get("Content-Type") != ct { 15 | fmt.Println("Unexpected content type returned") 16 | return 17 | } 18 | b, _ := ioutil.ReadAll(res.Body) 19 | res.Body.Close() 20 | fmt.Printf("%s", b) 21 | } 22 | -------------------------------------------------------------------------------- /chapter8/http_custom_error.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | ) 8 | 9 | type Error struct { 10 | HTTPCode int `json:"-"` 11 | Code int `json:"code,omitempty"` 12 | Message string `json:"message"` 13 | } 14 | 15 | func JSONError(w http.ResponseWriter, e Error) { 16 | data := struct { 17 | Err Error `json:"error"` 18 | }{e} 19 | b, err := json.Marshal(data) 20 | if err != nil { 21 | http.Error(w, "Internal Server Error", 500) 22 | return 23 | } 24 | w.Header().Set("Content-Type", "application/json") 25 | w.WriteHeader(e.HTTPCode) 26 | fmt.Fprint(w, string(b)) 27 | } 28 | 29 | func displayError(w http.ResponseWriter, r *http.Request) { 30 | e := Error{ 31 | HTTPCode: http.StatusForbidden, 32 | Code: 123, 33 | Message: "An Error Occurred", 34 | } 35 | JSONError(w, e) 36 | } 37 | 38 | func main() { 39 | http.HandleFunc("/", displayError) 40 | http.ListenAndServe(":8080", nil) 41 | } 42 | -------------------------------------------------------------------------------- /chapter8/http_error.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "net/http" 4 | 5 | func displayError(w http.ResponseWriter, r *http.Request) { 6 | http.Error(w, "An Error Occurred", http.StatusForbidden) 7 | } 8 | 9 | func main() { 10 | http.HandleFunc("/", displayError) 11 | http.ListenAndServe(":8080", nil) 12 | } 13 | -------------------------------------------------------------------------------- /chapter8/http_timeout_handling.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | // "io" 5 | "fmt" 6 | "io" 7 | "net" 8 | "net/http" 9 | "net/url" 10 | "os" 11 | "strconv" 12 | "strings" 13 | "time" 14 | ) 15 | 16 | func main() { 17 | file, err := os.Create("ff.dmg") 18 | if err != nil { 19 | fmt.Println(err) 20 | return 21 | } 22 | defer file.Close() 23 | 24 | location := "https://download-installer.cdn.mozilla.net/pub/firefox/releases/40.0.3/mac/en-US/Firefox%2040.0.3.dmg" 25 | err = download(location, file, 100) 26 | if err != nil { 27 | fmt.Println(err) 28 | return 29 | } 30 | 31 | fi, err := file.Stat() 32 | if err != nil { 33 | fmt.Println(err) 34 | return 35 | } 36 | fmt.Printf("Got it with %v bytes downloaded", fi.Size()) 37 | } 38 | 39 | func download(location string, file *os.File, retries int64) error { 40 | req, err := http.NewRequest("GET", location, nil) 41 | if err != nil { 42 | return err 43 | } 44 | fi, err := file.Stat() 45 | if err != nil { 46 | return err 47 | } 48 | current := fi.Size() 49 | if current > 0 { 50 | start := strconv.FormatInt(current, 10) 51 | req.Header.Set("Range", "bytes="+start+"-") 52 | } 53 | 54 | cc := &http.Client{Timeout: 5 * time.Minute} 55 | res, err := cc.Do(req) 56 | if err != nil && hasTimedOut(err) { 57 | if retries > 0 { 58 | return download(location, file, retries-1) 59 | } 60 | return err 61 | } else if err != nil { 62 | return err 63 | } 64 | 65 | if res.StatusCode < 200 || res.StatusCode >= 300 { 66 | errFmt := "Unsuccess HTTP request. Status: %s" 67 | return fmt.Errorf(errFmt, res.Status) 68 | } 69 | 70 | if res.Header.Get("Accept-Ranges") != "bytes" { 71 | retries = 0 72 | } 73 | 74 | _, err = io.Copy(file, res.Body) 75 | if err != nil && hasTimedOut(err) { 76 | if retries > 0 { 77 | return download(location, file, retries-1) 78 | } 79 | return err 80 | } else if err != nil { 81 | return err 82 | } 83 | 84 | return nil 85 | } 86 | 87 | func hasTimedOut(err error) bool { 88 | switch err := err.(type) { 89 | case *url.Error: 90 | if err, ok := err.Err.(net.Error); ok && err.Timeout() { 91 | return true 92 | } 93 | case net.Error: 94 | if err.Timeout() { 95 | return true 96 | } 97 | case *net.OpError: 98 | if err.Timeout() { 99 | return true 100 | } 101 | } 102 | 103 | errTxt := "use of closed network connection" 104 | if err != nil && strings.Contains(err.Error(), errTxt) { 105 | return true 106 | } 107 | 108 | return false 109 | } 110 | -------------------------------------------------------------------------------- /chapter8/parse_arbitrary_json.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | var ks = []byte(`{ 10 | "firstName": "Jean", 11 | "lastName": "Bartik", 12 | "age": 86, 13 | "education": [ 14 | { 15 | "institution": "Northwest Missouri State Teachers College", 16 | "degree": "Bachelor of Science in Mathematics" 17 | }, 18 | { 19 | "institution": "University of Pennsylvania", 20 | "degree": "Masters in English" 21 | } 22 | ], 23 | "spouse": "William Bartik", 24 | "children": [ 25 | "Timothy John Bartik", 26 | "Jane Helen Bartik", 27 | "Mary Ruth Bartik" 28 | ] 29 | }`) 30 | 31 | func main() { 32 | var f interface{} 33 | err := json.Unmarshal(ks, &f) 34 | if err != nil { 35 | fmt.Println(err) 36 | os.Exit(1) 37 | } 38 | 39 | fmt.Println(f) 40 | 41 | m := f.(map[string]interface{}) 42 | fmt.Println(m["firstName"]) 43 | 44 | fmt.Print("interface{} ") 45 | printJSON(f) 46 | } 47 | 48 | func printJSON(v interface{}) { 49 | switch vv := v.(type) { 50 | case string: 51 | fmt.Println("is string", vv) 52 | case float64: 53 | fmt.Println("is float64", vv) 54 | case []interface{}: 55 | fmt.Println("is an array:") 56 | for i, u := range vv { 57 | fmt.Print(i, " ") 58 | printJSON(u) 59 | } 60 | case map[string]interface{}: 61 | fmt.Println("is an object:") 62 | for i, u := range vv { 63 | fmt.Print(i, " ") 64 | printJSON(u) 65 | } 66 | default: 67 | fmt.Println("Unknown type") 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /chapter8/semantic_version_api.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | ) 8 | 9 | func main() { 10 | http.HandleFunc("/test", displayTest) 11 | http.ListenAndServe(":8080", nil) 12 | } 13 | 14 | func displayTest(w http.ResponseWriter, r *http.Request) { 15 | t := r.Header.Get("Accept") 16 | var err error 17 | var b []byte 18 | var ct string 19 | switch t { 20 | case "application/vnd.mytodos.json; version=2.0": 21 | data := testMessageV2{"Version 2"} 22 | b, err = json.Marshal(data) 23 | ct = "application/vnd.mytodos.json; version=2.0" 24 | case "application/vnd.mytodos.json; version=1.0": 25 | fallthrough 26 | default: 27 | data := testMessageV1{"Version 1"} 28 | b, err = json.Marshal(data) 29 | ct = "application/vnd.mytodos.json; version=1.0" 30 | } 31 | 32 | if err != nil { 33 | http.Error(w, "Internal Server Error", 500) 34 | return 35 | } 36 | w.Header().Set("Content-Type", ct) 37 | fmt.Fprint(w, string(b)) 38 | } 39 | 40 | type testMessageV1 struct { 41 | Message string `json:"message"` 42 | } 43 | 44 | type testMessageV2 struct { 45 | Info string `json:"info"` 46 | } 47 | -------------------------------------------------------------------------------- /chapter8/simple_get.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | ) 8 | 9 | func main() { 10 | res, _ := http.Get("http://goinpracticebook.com") 11 | b, _ := ioutil.ReadAll(res.Body) 12 | res.Body.Close() 13 | fmt.Printf("%s", b) 14 | } 15 | -------------------------------------------------------------------------------- /chapter8/simple_json_parser.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | type Person struct { 9 | Name string `json:"name"` 10 | } 11 | 12 | var JSON = `{ 13 | "name": "Miracle Max" 14 | }` 15 | 16 | func main() { 17 | var p Person 18 | err := json.Unmarshal([]byte(JSON), &p) 19 | if err != nil { 20 | fmt.Println(err) 21 | return 22 | } 23 | 24 | fmt.Println(p) 25 | } 26 | -------------------------------------------------------------------------------- /chapter8/versioned_url_api.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | ) 8 | 9 | type testMessage struct { 10 | Message string `json:"message"` 11 | } 12 | 13 | func displayTest(w http.ResponseWriter, r *http.Request) { 14 | data := testMessage{"A test message."} 15 | b, err := json.Marshal(data) 16 | if err != nil { 17 | http.Error(w, "Internal Server Error", 500) 18 | return 19 | } 20 | w.Header().Set("Content-Type", "application/json") 21 | fmt.Fprint(w, string(b)) 22 | } 23 | 24 | func main() { 25 | http.HandleFunc("/api/v1/test", displayTest) 26 | http.ListenAndServe(":8080", nil) 27 | } 28 | -------------------------------------------------------------------------------- /chapter9/api_interface.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "io/ioutil" 8 | "os" 9 | "path/filepath" 10 | ) 11 | 12 | type File interface { 13 | Load(string) (io.ReadCloser, error) 14 | Save(string, io.ReadSeeker) error 15 | } 16 | 17 | type LocalFile struct { 18 | Base string 19 | } 20 | 21 | func (l LocalFile) Load(path string) (io.ReadCloser, error) { 22 | p := filepath.Join(l.Base, path) 23 | return os.Open(p) 24 | } 25 | 26 | func (l LocalFile) Save(path string, body io.ReadSeeker) error { 27 | p := filepath.Join(l.Base, path) 28 | d := filepath.Dir(p) 29 | err := os.MkdirAll(d, os.ModeDir|os.ModePerm) 30 | if err != nil { 31 | return err 32 | } 33 | 34 | f, err := os.Create(p) 35 | if err != nil { 36 | return err 37 | } 38 | defer f.Close() 39 | 40 | _, err = io.Copy(f, body) 41 | return err 42 | } 43 | 44 | func fileStore() (File, error) { 45 | return &LocalFile{Base: "/Users/mfarina/store"}, nil 46 | } 47 | 48 | func main() { 49 | content := `Lorem ipsum dolor sit amet, consectetur` + 50 | `adipiscing elit. Donec a diam lectus.Sed sit` + 51 | `amet ipsum mauris. Maecenascongue ligula ac` + 52 | `quam viverra nec consectetur ante hendrerit.` 53 | body := bytes.NewReader([]byte(content)) 54 | 55 | store, err := fileStore() 56 | if err != nil { 57 | fmt.Println(err) 58 | os.Exit(1) 59 | } 60 | 61 | fmt.Println("Storing content...") 62 | err = store.Save("foo/bar", body) 63 | if err != nil { 64 | fmt.Println(err) 65 | os.Exit(1) 66 | } 67 | 68 | fmt.Println("Retrieving content...") 69 | c, err := store.Load("foo/bar") 70 | if err != nil { 71 | fmt.Println(err) 72 | os.Exit(1) 73 | } 74 | o, err := ioutil.ReadAll(c) 75 | if err != nil { 76 | fmt.Println(err) 77 | os.Exit(1) 78 | } 79 | fmt.Println(string(o)) 80 | } 81 | -------------------------------------------------------------------------------- /chapter9/api_interface_with_errors.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "log" 10 | "os" 11 | "path/filepath" 12 | ) 13 | 14 | var ( 15 | ErrFileNotFound = errors.New("File not found") 16 | ErrCannotLoadFile = errors.New("Unable to load file") 17 | ErrCannotSaveFile = errors.New("Unable to save file") 18 | ) 19 | 20 | type File interface { 21 | Load(string) (io.ReadCloser, error) 22 | Save(string, io.ReadSeeker) error 23 | } 24 | 25 | type LocalFile struct { 26 | Base string 27 | } 28 | 29 | func (l LocalFile) Load(path string) (io.ReadCloser, error) { 30 | p := filepath.Join(l.Base, path) 31 | var oerr error 32 | o, err := os.Open(p) 33 | if err != nil && os.IsNotExist(err) { 34 | log.Printf("Unable to find %s", path) 35 | oerr = ErrFileNotFound 36 | } else if err != nil { 37 | log.Printf("Error loading file %s, err: %s", path, err) 38 | oerr = ErrCannotLoadFile 39 | } 40 | return o, oerr 41 | } 42 | 43 | func (l LocalFile) Save(path string, body io.ReadSeeker) error { 44 | p := filepath.Join(l.Base, path) 45 | d := filepath.Dir(p) 46 | err := os.MkdirAll(d, os.ModeDir|os.ModePerm) 47 | if err != nil { 48 | log.Printf("Cannot make directory '%s', err: %s", d, err) 49 | return ErrCannotSaveFile 50 | } 51 | 52 | f, err := os.Create(p) 53 | if err != nil { 54 | log.Printf("Cannot create file '%s', err: %s", p, err) 55 | return ErrCannotSaveFile 56 | } 57 | defer f.Close() 58 | 59 | _, err = io.Copy(f, body) 60 | if err != nil { 61 | log.Printf("Cannot save file '%s', err: %s", path, err) 62 | return ErrCannotSaveFile 63 | } 64 | return nil 65 | } 66 | 67 | func fileStore() (File, error) { 68 | return &LocalFile{Base: "/Users/mfarina/store"}, nil 69 | } 70 | 71 | func main() { 72 | content := `Lorem ipsum dolor sit amet, consectetur` + 73 | `adipiscing elit. Donec a diam lectus.Sed sit` + 74 | `amet ipsum mauris. Maecenascongue ligula ac` + 75 | `quam viverra nec consectetur ante hendrerit.` 76 | body := bytes.NewReader([]byte(content)) 77 | 78 | store, err := fileStore() 79 | if err != nil { 80 | fmt.Println(err) 81 | os.Exit(1) 82 | } 83 | 84 | fmt.Println("Storing content...") 85 | err = store.Save("foo/bar", body) 86 | if err != nil { 87 | fmt.Println(err) 88 | os.Exit(1) 89 | } 90 | 91 | fmt.Println("Retrieving content...") 92 | c, err := store.Load("foo/bar") 93 | if err != nil { 94 | fmt.Println(err) 95 | os.Exit(1) 96 | } 97 | o, err := ioutil.ReadAll(c) 98 | if err != nil { 99 | fmt.Println(err) 100 | os.Exit(1) 101 | } 102 | fmt.Println(string(o)) 103 | } 104 | -------------------------------------------------------------------------------- /chapter9/detect_dep.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os/exec" 7 | ) 8 | 9 | func main() { 10 | err := checkDep("fortune") 11 | if err != nil { 12 | log.Fatalln(err) 13 | } 14 | 15 | fmt.Println("Time to get your fortunte") 16 | } 17 | 18 | func checkDep(name string) error { 19 | if _, err := exec.LookPath(name); err != nil { 20 | es := "Could not find '%s' in PATH: %s" 21 | return fmt.Errorf(es, name, err) 22 | } 23 | 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /chapter9/hostlookup.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "os" 7 | ) 8 | 9 | func main() { 10 | name, err := os.Hostname() 11 | if err != nil { 12 | fmt.Println(err) 13 | return 14 | } 15 | 16 | addrs, err := net.LookupHost(name) 17 | if err != nil { 18 | fmt.Println(err) 19 | return 20 | } 21 | 22 | for _, a := range addrs { 23 | fmt.Println(a) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /chapter9/runtime.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "runtime" 6 | "time" 7 | ) 8 | 9 | func monitorRuntime() { 10 | log.Println("Number of CPUs:", runtime.NumCPU()) 11 | m := &runtime.MemStats{} 12 | for { 13 | r := runtime.NumGoroutine() 14 | log.Println("Number of goroutines", r) 15 | 16 | runtime.ReadMemStats(m) 17 | log.Println("Allocated memory", m.Alloc) 18 | time.Sleep(10 * time.Second) 19 | } 20 | } 21 | 22 | func main() { 23 | go monitorRuntime() 24 | 25 | i := 0 26 | for i < 40 { 27 | go func() { 28 | time.Sleep(15 * time.Second) 29 | }() 30 | i = i + 1 31 | time.Sleep(1 * time.Second) 32 | } 33 | } 34 | --------------------------------------------------------------------------------