├── .gitignore ├── README.md ├── chapter1 └── channels │ └── hellochannels.go ├── chapter10 ├── listing01 │ └── listing01.go ├── listing02 │ └── listing02.go ├── listing03 │ └── listing03.go ├── listing04 │ └── listing04.go ├── listing05 │ └── listing05.go └── listing06 │ ├── listing06 │ ├── listing06.go │ └── pubsub │ └── pubsub.go ├── chapter2 └── sample │ ├── data │ └── data.json │ ├── main.go │ ├── matchers │ └── rss.go │ └── search │ ├── default.go │ ├── feed.go │ ├── match.go │ └── search.go ├── chapter3 ├── dbdriver │ ├── main.go │ └── postgres │ │ └── postgres.go ├── wordcount │ ├── gowords.txt │ └── wordcount.go └── words │ └── count.go ├── chapter5 ├── listing11 │ └── listing11.go ├── listing34 │ └── listing34.go ├── listing35 │ └── listing35.go ├── listing36 │ └── listing36.go ├── listing46 │ └── listing46.go ├── listing48 │ └── listing48.go ├── listing50 │ └── listing50.go ├── listing56 │ └── listing56.go ├── listing60 │ └── listing60.go ├── listing64 │ ├── counters │ │ └── counters.go │ └── listing64.go ├── listing68 │ ├── counters │ │ └── counters.go │ └── listing68.go ├── listing71 │ ├── entities │ │ └── entities.go │ └── listing71.go └── listing74 │ ├── entities │ └── entities.go │ └── listing74.go ├── chapter6 ├── listing01 │ └── listing01.go ├── listing04 │ └── listing04.go ├── listing07 │ └── listing07.go ├── listing09 │ └── listing09.go ├── listing13 │ └── listing13.go ├── listing15 │ └── listing15.go ├── listing16 │ └── listing16.go ├── listing20 │ └── listing20.go ├── listing22 │ └── listing22.go └── listing24 │ └── listing24.go ├── chapter7 └── patterns │ ├── pool │ ├── main │ │ └── main.go │ └── pool.go │ ├── runner │ ├── main │ │ └── main.go │ └── runner.go │ ├── search │ ├── main │ │ └── main.go │ ├── search.go │ └── searchers.go │ ├── semaphore │ └── semaphore.go │ └── work │ ├── main │ └── main.go │ └── work.go ├── chapter8 ├── listing03 │ └── listing03.go ├── listing11 │ └── listing11.go ├── listing24 │ └── listing24.go ├── listing27 │ └── listing27.go ├── listing29 │ └── listing29.go ├── listing31 │ └── listing31.go ├── listing37 │ └── listing37.go └── listing46 │ └── listing46.go └── chapter9 ├── listing01 └── listing01_test.go ├── listing08 └── listing08_test.go ├── listing12 └── listing12_test.go ├── listing17 ├── handlers │ ├── handlers.go │ ├── handlers_example_test.go │ └── handlers_test.go └── listing17.go └── listing28 └── listing28_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | code 2 | ==== 3 | 4 | Source Code for Go In Action examples 5 | -------------------------------------------------------------------------------- /chapter1/channels/hellochannels.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | var wg sync.WaitGroup 9 | 10 | func printer(ch chan int) { 11 | for i := range ch { 12 | fmt.Printf("Received %d ", i) 13 | } 14 | wg.Done() 15 | } 16 | 17 | // main is the entry point for the program. 18 | func main() { 19 | c := make(chan int) 20 | go printer(c) 21 | wg.Add(1) 22 | 23 | // Send 10 integers on the channel. 24 | for i := 1; i <= 10; i++ { 25 | c <- i 26 | } 27 | 28 | close(c) 29 | wg.Wait() 30 | } 31 | -------------------------------------------------------------------------------- /chapter10/listing01/listing01.go: -------------------------------------------------------------------------------- 1 | // Sample program demonstrating struct composition. 2 | package main 3 | 4 | import ( 5 | "errors" 6 | "fmt" 7 | "io" 8 | "math/rand" 9 | "time" 10 | ) 11 | 12 | func init() { 13 | rand.Seed(time.Now().UnixNano()) 14 | } 15 | 16 | // ============================================================================= 17 | 18 | // Data is the structure of the data we are copying. 19 | type Data struct { 20 | Line string 21 | } 22 | 23 | // ============================================================================= 24 | 25 | // Xenia is a system we need to pull data from. 26 | type Xenia struct{} 27 | 28 | // Pull knows how to pull data out of Xenia. 29 | func (Xenia) Pull(d *Data) error { 30 | switch rand.Intn(10) { 31 | case 1, 9: 32 | return io.EOF 33 | 34 | case 5: 35 | return errors.New("Error reading data from Xenia") 36 | 37 | default: 38 | d.Line = "Data" 39 | fmt.Println("In:", d.Line) 40 | return nil 41 | } 42 | } 43 | 44 | // Pillar is a system we need to store data into. 45 | type Pillar struct{} 46 | 47 | // Store knows how to store data into Pillar. 48 | func (Pillar) Store(d Data) error { 49 | fmt.Println("Out:", d.Line) 50 | return nil 51 | } 52 | 53 | // ============================================================================= 54 | 55 | // System wraps Xenia and Pillar together into a single system. 56 | type System struct { 57 | Xenia 58 | Pillar 59 | } 60 | 61 | // ============================================================================= 62 | 63 | // pull knows how to pull bulks of data from Xenia. 64 | func pull(x *Xenia, data []Data) (int, error) { 65 | for i := range data { 66 | if err := x.Pull(&data[i]); err != nil { 67 | return i, err 68 | } 69 | } 70 | 71 | return len(data), nil 72 | } 73 | 74 | // store knows how to store bulks of data into Pillar. 75 | func store(p *Pillar, data []Data) (int, error) { 76 | for i, d := range data { 77 | if err := p.Store(d); err != nil { 78 | return i, err 79 | } 80 | } 81 | 82 | return len(data), nil 83 | } 84 | 85 | // Copy knows how to pull and store data from the System. 86 | func Copy(sys *System, batch int) error { 87 | data := make([]Data, batch) 88 | 89 | for { 90 | i, err := pull(&sys.Xenia, data) 91 | if i > 0 { 92 | if _, err := store(&sys.Pillar, data[:i]); err != nil { 93 | return err 94 | } 95 | } 96 | 97 | if err != nil { 98 | return err 99 | } 100 | } 101 | } 102 | 103 | // ============================================================================= 104 | 105 | func main() { 106 | 107 | // Initialize the system for use. 108 | sys := System{ 109 | Xenia: Xenia{}, 110 | Pillar: Pillar{}, 111 | } 112 | 113 | if err := Copy(&sys, 3); err != io.EOF { 114 | fmt.Println(err) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /chapter10/listing02/listing02.go: -------------------------------------------------------------------------------- 1 | // Sample program demonstrating decoupling with interfaces. 2 | package main 3 | 4 | import ( 5 | "errors" 6 | "fmt" 7 | "io" 8 | "math/rand" 9 | "time" 10 | ) 11 | 12 | func init() { 13 | rand.Seed(time.Now().UnixNano()) 14 | } 15 | 16 | // ============================================================================= 17 | 18 | // Data is the structure of the data we are copying. 19 | type Data struct { 20 | Line string 21 | } 22 | 23 | // ============================================================================= 24 | 25 | // Puller declares behavior for pulling data. 26 | type Puller interface { 27 | Pull(d *Data) error 28 | } 29 | 30 | // Storer declares behavior for storing data. 31 | type Storer interface { 32 | Store(d Data) error 33 | } 34 | 35 | // ============================================================================= 36 | 37 | // Xenia is a system we need to pull data from. 38 | type Xenia struct{} 39 | 40 | // Pull knows how to pull data out of Xenia. 41 | func (Xenia) Pull(d *Data) error { 42 | switch rand.Intn(10) { 43 | case 1, 9: 44 | return io.EOF 45 | 46 | case 5: 47 | return errors.New("Error reading data from Xenia") 48 | 49 | default: 50 | d.Line = "Data" 51 | fmt.Println("In:", d.Line) 52 | return nil 53 | } 54 | } 55 | 56 | // Pillar is a system we need to store data into. 57 | type Pillar struct{} 58 | 59 | // Store knows how to store data into Pillar. 60 | func (Pillar) Store(d Data) error { 61 | fmt.Println("Out:", d.Line) 62 | return nil 63 | } 64 | 65 | // ============================================================================= 66 | 67 | // System wraps Xenia and Pillar together into a single system. 68 | type System struct { 69 | Xenia 70 | Pillar 71 | } 72 | 73 | // ============================================================================= 74 | 75 | // pull knows how to pull bulks of data from any Puller. 76 | func pull(p Puller, data []Data) (int, error) { 77 | for i := range data { 78 | if err := p.Pull(&data[i]); err != nil { 79 | return i, err 80 | } 81 | } 82 | 83 | return len(data), nil 84 | } 85 | 86 | // store knows how to store bulks of data from any Storer. 87 | func store(s Storer, data []Data) (int, error) { 88 | for i, d := range data { 89 | if err := s.Store(d); err != nil { 90 | return i, err 91 | } 92 | } 93 | 94 | return len(data), nil 95 | } 96 | 97 | // Copy knows how to pull and store data from the System. 98 | func Copy(sys *System, batch int) error { 99 | data := make([]Data, batch) 100 | 101 | for { 102 | i, err := pull(&sys.Xenia, data) 103 | if i > 0 { 104 | if _, err := store(&sys.Pillar, data[:i]); err != nil { 105 | return err 106 | } 107 | } 108 | 109 | if err != nil { 110 | return err 111 | } 112 | } 113 | } 114 | 115 | // ============================================================================= 116 | 117 | func main() { 118 | 119 | // Initialize the system for use. 120 | sys := System{ 121 | Xenia: Xenia{}, 122 | Pillar: Pillar{}, 123 | } 124 | 125 | if err := Copy(&sys, 3); err != io.EOF { 126 | fmt.Println(err) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /chapter10/listing03/listing03.go: -------------------------------------------------------------------------------- 1 | // Sample program demonstrating interface composition. 2 | package main 3 | 4 | import ( 5 | "errors" 6 | "fmt" 7 | "io" 8 | "math/rand" 9 | "time" 10 | ) 11 | 12 | func init() { 13 | rand.Seed(time.Now().UnixNano()) 14 | } 15 | 16 | // ============================================================================= 17 | 18 | // Data is the structure of the data we are copying. 19 | type Data struct { 20 | Line string 21 | } 22 | 23 | // ============================================================================= 24 | 25 | // Puller declares behavior for pulling data. 26 | type Puller interface { 27 | Pull(d *Data) error 28 | } 29 | 30 | // Storer declares behavior for storing data. 31 | type Storer interface { 32 | Store(d Data) error 33 | } 34 | 35 | // PullStorer declares behavior for both pulling and storing. 36 | type PullStorer interface { 37 | Puller 38 | Storer 39 | } 40 | 41 | // ============================================================================= 42 | 43 | // Xenia is a system we need to pull data from. 44 | type Xenia struct{} 45 | 46 | // Pull knows how to pull data out of Xenia. 47 | func (Xenia) Pull(d *Data) error { 48 | switch rand.Intn(10) { 49 | case 1, 9: 50 | return io.EOF 51 | 52 | case 5: 53 | return errors.New("Error reading data from Xenia") 54 | 55 | default: 56 | d.Line = "Data" 57 | fmt.Println("In:", d.Line) 58 | return nil 59 | } 60 | } 61 | 62 | // Pillar is a system we need to store data into. 63 | type Pillar struct{} 64 | 65 | // Store knows how to store data into Pillar. 66 | func (Pillar) Store(d Data) error { 67 | fmt.Println("Out:", d.Line) 68 | return nil 69 | } 70 | 71 | // ============================================================================= 72 | 73 | // System wraps Xenia and Pillar together into a single system. 74 | type System struct { 75 | Xenia 76 | Pillar 77 | } 78 | 79 | // ============================================================================= 80 | 81 | // pull knows how to pull bulks of data from any Puller. 82 | func pull(p Puller, data []Data) (int, error) { 83 | for i := range data { 84 | if err := p.Pull(&data[i]); err != nil { 85 | return i, err 86 | } 87 | } 88 | 89 | return len(data), nil 90 | } 91 | 92 | // store knows how to store bulks of data from any Storer. 93 | func store(s Storer, data []Data) (int, error) { 94 | for i, d := range data { 95 | if err := s.Store(d); err != nil { 96 | return i, err 97 | } 98 | } 99 | 100 | return len(data), nil 101 | } 102 | 103 | // Copy knows how to pull and store data from any System. 104 | func Copy(ps PullStorer, batch int) error { 105 | data := make([]Data, batch) 106 | 107 | for { 108 | i, err := pull(ps, data) 109 | if i > 0 { 110 | if _, err := store(ps, data[:i]); err != nil { 111 | return err 112 | } 113 | } 114 | 115 | if err != nil { 116 | return err 117 | } 118 | } 119 | } 120 | 121 | // ============================================================================= 122 | 123 | func main() { 124 | 125 | // Initialize the system for use. 126 | sys := System{ 127 | Xenia: Xenia{}, 128 | Pillar: Pillar{}, 129 | } 130 | 131 | if err := Copy(&sys, 3); err != io.EOF { 132 | fmt.Println(err) 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /chapter10/listing04/listing04.go: -------------------------------------------------------------------------------- 1 | // Sample program demonstrating decoupling with interface composition. 2 | package main 3 | 4 | import ( 5 | "errors" 6 | "fmt" 7 | "io" 8 | "math/rand" 9 | "time" 10 | ) 11 | 12 | func init() { 13 | rand.Seed(time.Now().UnixNano()) 14 | } 15 | 16 | // ============================================================================= 17 | 18 | // Data is the structure of the data we are copying. 19 | type Data struct { 20 | Line string 21 | } 22 | 23 | // ============================================================================= 24 | 25 | // Puller declares behavior for pulling data. 26 | type Puller interface { 27 | Pull(d *Data) error 28 | } 29 | 30 | // Storer declares behavior for storing data. 31 | type Storer interface { 32 | Store(d Data) error 33 | } 34 | 35 | // PullStorer declares behavior for both pulling and storing. 36 | type PullStorer interface { 37 | Puller 38 | Storer 39 | } 40 | 41 | // ============================================================================= 42 | 43 | // Xenia is a system we need to pull data from. 44 | type Xenia struct{} 45 | 46 | // Pull knows how to pull data out of Xenia. 47 | func (Xenia) Pull(d *Data) error { 48 | switch rand.Intn(10) { 49 | case 1, 9: 50 | return io.EOF 51 | 52 | case 5: 53 | return errors.New("Error reading data from Xenia") 54 | 55 | default: 56 | d.Line = "Data" 57 | fmt.Println("In:", d.Line) 58 | return nil 59 | } 60 | } 61 | 62 | // Pillar is a system we need to store data into. 63 | type Pillar struct{} 64 | 65 | // Store knows how to store data into Pillar. 66 | func (Pillar) Store(d Data) error { 67 | fmt.Println("Out:", d.Line) 68 | return nil 69 | } 70 | 71 | // ============================================================================= 72 | 73 | // System wraps Pullers and Stores together into a single system. 74 | type System struct { 75 | Puller 76 | Storer 77 | } 78 | 79 | // ============================================================================= 80 | 81 | // pull knows how to pull bulks of data from any Puller. 82 | func pull(p Puller, data []Data) (int, error) { 83 | for i := range data { 84 | if err := p.Pull(&data[i]); err != nil { 85 | return i, err 86 | } 87 | } 88 | 89 | return len(data), nil 90 | } 91 | 92 | // store knows how to store bulks of data from any Storer. 93 | func store(s Storer, data []Data) (int, error) { 94 | for i, d := range data { 95 | if err := s.Store(d); err != nil { 96 | return i, err 97 | } 98 | } 99 | 100 | return len(data), nil 101 | } 102 | 103 | // Copy knows how to pull and store data from any System. 104 | func Copy(ps PullStorer, batch int) error { 105 | data := make([]Data, batch) 106 | 107 | for { 108 | i, err := pull(ps, data) 109 | if i > 0 { 110 | if _, err := store(ps, data[:i]); err != nil { 111 | return err 112 | } 113 | } 114 | 115 | if err != nil { 116 | return err 117 | } 118 | } 119 | } 120 | 121 | // ============================================================================= 122 | 123 | func main() { 124 | 125 | // Initialize the system for use. 126 | sys := System{ 127 | Puller: Xenia{}, 128 | Storer: Pillar{}, 129 | } 130 | 131 | if err := Copy(&sys, 3); err != io.EOF { 132 | fmt.Println(err) 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /chapter10/listing05/listing05.go: -------------------------------------------------------------------------------- 1 | // Sample program demonstrating when implicit interface conversions 2 | // are provided by the compiler. 3 | package main 4 | 5 | import "fmt" 6 | 7 | // ============================================================================= 8 | 9 | // Mover provides support for moving things. 10 | type Mover interface { 11 | Move() 12 | } 13 | 14 | // Locker provides support for locking and unlocking things. 15 | type Locker interface { 16 | Lock() 17 | Unlock() 18 | } 19 | 20 | // MoveLocker provides support for moving and locking things. 21 | type MoveLocker interface { 22 | Mover 23 | Locker 24 | } 25 | 26 | // ============================================================================= 27 | 28 | // bike represents a concrete type for the example. 29 | type bike struct{} 30 | 31 | // Move can change the position of a bike. 32 | func (bike) Move() { 33 | fmt.Println("Moving the bike") 34 | } 35 | 36 | // Lock prevents a bike from moving. 37 | func (bike) Lock() { 38 | fmt.Println("Locking the bike") 39 | } 40 | 41 | // Unlock allows a bike to be moved. 42 | func (bike) Unlock() { 43 | fmt.Println("Unlocking the bike") 44 | } 45 | 46 | // ============================================================================= 47 | 48 | func main() { 49 | 50 | // Declare variables of the MoveLocker and Mover interfaces set to their 51 | // zero value. 52 | var ml MoveLocker 53 | var m Mover 54 | 55 | // Create a value of type bike and assign the value to the MoveLocker 56 | // interface value. 57 | ml = bike{} 58 | 59 | // An interface value of type MoveLocker can be implicitly converted into 60 | // a value of type Mover. They both declare a method named move. 61 | m = ml 62 | 63 | // prog.go:65: cannot use m (type Mover) as type MoveLocker in assignment: 64 | // Mover does not implement MoveLocker (missing Lock method) 65 | ml = m 66 | 67 | // Interface type Mover does not declare methods named lock and unlock. 68 | // Therefore, the compiler can't perform an implicit conversion to assign 69 | // a value of interface type Mover to an interface value of type MoveLocker. 70 | // It is irrelevant that the concrete type value of type bike that is stored 71 | // inside of the Mover interface value implements the MoveLocker interface. 72 | 73 | // We can perform a type assertion at runtime to support the assignment. 74 | 75 | // Perform a type assertion against the Mover interface value to access 76 | // a COPY of the concrete type value of type bike that was stored inside 77 | // of it. Then assign the COPY of the concrete type to the MoveLocker 78 | // interface. 79 | b := m.(bike) 80 | ml = b 81 | } 82 | -------------------------------------------------------------------------------- /chapter10/listing06/listing06: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goinaction/code/49fc99e6affb9aa48cab1889505aa6631ee31ace/chapter10/listing06/listing06 -------------------------------------------------------------------------------- /chapter10/listing06/listing06.go: -------------------------------------------------------------------------------- 1 | // Sample program to show how you can personally mock concrete types when 2 | // you need to for your own packages or tests. 3 | package main 4 | 5 | import ( 6 | "github.com/goinaction/code/chapter10/listing06/pubsub" 7 | ) 8 | 9 | // publisher is an interface to allow this package to mock the 10 | // pubsub package support. 11 | type publisher interface { 12 | Publish(key string, v interface{}) error 13 | Subscribe(key string) error 14 | } 15 | 16 | // mock is a concrete type to help support the mocking of the 17 | // pubsub package. 18 | type mock struct{} 19 | 20 | // Publish implements the publisher interface for the mock. 21 | func (m *mock) Publish(key string, v interface{}) error { 22 | 23 | // ADD YOUR MOCK FOR THE PUBLISH CALL. 24 | return nil 25 | } 26 | 27 | // Subscribe implements the publisher interface for the mock. 28 | func (m *mock) Subscribe(key string) error { 29 | 30 | // ADD YOUR MOCK FOR THE SUBSCRIBE CALL. 31 | return nil 32 | } 33 | 34 | func main() { 35 | 36 | // Create a slice of publisher interface values. Assign 37 | // the address of a pubsub.PubSub value and the address of 38 | // a mock value. 39 | pubs := []publisher{ 40 | pubsub.New("localhost"), 41 | &mock{}, 42 | } 43 | 44 | // Range over the interface value to see how the publisher 45 | // interface provides the level of decoupling the user needs. 46 | // The pubsub package did not need to provide the interface type. 47 | for _, p := range pubs { 48 | p.Publish("key", "value") 49 | p.Subscribe("key") 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /chapter10/listing06/pubsub/pubsub.go: -------------------------------------------------------------------------------- 1 | // Package pubsub simulates a package that provides 2 | // publication/subscription type services. 3 | package pubsub 4 | 5 | // PubSub provides access to a queue system. 6 | type PubSub struct { 7 | host string 8 | 9 | // PRETEND THERE ARE MORE FIELDS. 10 | } 11 | 12 | // New creates a pubsub value for use. 13 | func New(host string) *PubSub { 14 | ps := PubSub{ 15 | host: host, 16 | } 17 | 18 | // PRETEND THERE IS A SPECIFIC IMPLEMENTATION. 19 | 20 | return &ps 21 | } 22 | 23 | // Publish sends the data for the specified key. 24 | func (ps *PubSub) Publish(key string, v interface{}) error { 25 | 26 | // PRETEND THERE IS A SPECIFIC IMPLEMENTATION. 27 | return nil 28 | } 29 | 30 | // Subscribe sets up an request to receive messages for the 31 | // specified key. 32 | func (ps *PubSub) Subscribe(key string) error { 33 | 34 | // PRETEND THERE IS A SPECIFIC IMPLEMENTATION. 35 | return nil 36 | } 37 | -------------------------------------------------------------------------------- /chapter2/sample/data/data.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "site" : "npr", 4 | "link" : "http://www.npr.org/rss/rss.php?id=1001", 5 | "type" : "rss" 6 | }, 7 | { 8 | "site" : "npr", 9 | "link" : "http://www.npr.org/rss/rss.php?id=1008", 10 | "type" : "rss" 11 | }, 12 | { 13 | "site" : "npr", 14 | "link" : "http://www.npr.org/rss/rss.php?id=1006", 15 | "type" : "rss" 16 | }, 17 | { 18 | "site" : "npr", 19 | "link" : "http://www.npr.org/rss/rss.php?id=1007", 20 | "type" : "rss" 21 | }, 22 | { 23 | "site" : "npr", 24 | "link" : "http://www.npr.org/rss/rss.php?id=1057", 25 | "type" : "rss" 26 | }, 27 | { 28 | "site" : "npr", 29 | "link" : "http://www.npr.org/rss/rss.php?id=1021", 30 | "type" : "rss" 31 | }, 32 | { 33 | "site" : "npr", 34 | "link" : "http://www.npr.org/rss/rss.php?id=1012", 35 | "type" : "rss" 36 | }, 37 | { 38 | "site" : "npr", 39 | "link" : "http://www.npr.org/rss/rss.php?id=1003", 40 | "type" : "rss" 41 | }, 42 | { 43 | "site" : "npr", 44 | "link" : "http://www.npr.org/rss/rss.php?id=2", 45 | "type" : "rss" 46 | }, 47 | { 48 | "site" : "npr", 49 | "link" : "http://www.npr.org/rss/rss.php?id=3", 50 | "type" : "rss" 51 | }, 52 | { 53 | "site" : "npr", 54 | "link" : "http://www.npr.org/rss/rss.php?id=5", 55 | "type" : "rss" 56 | }, 57 | { 58 | "site" : "npr", 59 | "link" : "http://www.npr.org/rss/rss.php?id=13", 60 | "type" : "rss" 61 | }, 62 | { 63 | "site" : "npr", 64 | "link" : "http://www.npr.org/rss/rss.php?id=46", 65 | "type" : "rss" 66 | }, 67 | { 68 | "site" : "npr", 69 | "link" : "http://www.npr.org/rss/rss.php?id=7", 70 | "type" : "rss" 71 | }, 72 | { 73 | "site" : "npr", 74 | "link" : "http://www.npr.org/rss/rss.php?id=10", 75 | "type" : "rss" 76 | }, 77 | { 78 | "site" : "npr", 79 | "link" : "http://www.npr.org/rss/rss.php?id=39", 80 | "type" : "rss" 81 | }, 82 | { 83 | "site" : "npr", 84 | "link" : "http://www.npr.org/rss/rss.php?id=43", 85 | "type" : "rss" 86 | }, 87 | { 88 | "site" : "bbci", 89 | "link" : "http://feeds.bbci.co.uk/news/rss.xml", 90 | "type" : "rss" 91 | }, 92 | { 93 | "site" : "bbci", 94 | "link" : "http://feeds.bbci.co.uk/news/business/rss.xml", 95 | "type" : "rss" 96 | }, 97 | { 98 | "site" : "bbci", 99 | "link" : "http://feeds.bbci.co.uk/news/world/us_and_canada/rss.xml", 100 | "type" : "rss" 101 | }, 102 | { 103 | "site" : "cnn", 104 | "link" : "http://rss.cnn.com/rss/cnn_topstories.rss", 105 | "type" : "rss" 106 | }, 107 | { 108 | "site" : "cnn", 109 | "link" : "http://rss.cnn.com/rss/cnn_world.rss", 110 | "type" : "rss" 111 | }, 112 | { 113 | "site" : "cnn", 114 | "link" : "http://rss.cnn.com/rss/cnn_us.rss", 115 | "type" : "rss" 116 | }, 117 | { 118 | "site" : "cnn", 119 | "link" : "http://rss.cnn.com/rss/cnn_allpolitics.rss", 120 | "type" : "rss" 121 | }, 122 | { 123 | "site" : "cnn", 124 | "link" : "http://rss.cnn.com/rss/cnn_crime.rss", 125 | "type" : "rss" 126 | }, 127 | { 128 | "site" : "cnn", 129 | "link" : "http://rss.cnn.com/rss/cnn_tech.rss", 130 | "type" : "rss" 131 | }, 132 | { 133 | "site" : "cnn", 134 | "link" : "http://rss.cnn.com/rss/cnn_health.rss", 135 | "type" : "rss" 136 | }, 137 | { 138 | "site" : "cnn", 139 | "link" : "http://rss.cnn.com/rss/cnn_topstories.rss", 140 | "type" : "rss" 141 | }, 142 | { 143 | "site" : "foxnews", 144 | "link" : "http://feeds.foxnews.com/foxnews/opinion?format=xml", 145 | "type" : "rss" 146 | }, 147 | { 148 | "site" : "foxnews", 149 | "link" : "http://feeds.foxnews.com/foxnews/politics?format=xml", 150 | "type" : "rss" 151 | }, 152 | { 153 | "site" : "foxnews", 154 | "link" : "http://feeds.foxnews.com/foxnews/national?format=xml", 155 | "type" : "rss" 156 | }, 157 | { 158 | "site" : "foxnews", 159 | "link" : "http://feeds.foxnews.com/foxnews/world?format=xml", 160 | "type" : "rss" 161 | }, 162 | { 163 | "site" : "nbcnews", 164 | "link" : "http://feeds.nbcnews.com/feeds/topstories", 165 | "type" : "rss" 166 | }, 167 | { 168 | "site" : "nbcnews", 169 | "link" : "http://feeds.nbcnews.com/feeds/usnews", 170 | "type" : "rss" 171 | }, 172 | { 173 | "site" : "nbcnews", 174 | "link" : "http://rss.msnbc.msn.com/id/21491043/device/rss/rss.xml", 175 | "type" : "rss" 176 | }, 177 | { 178 | "site" : "nbcnews", 179 | "link" : "http://rss.msnbc.msn.com/id/21491571/device/rss/rss.xml", 180 | "type" : "rss" 181 | }, 182 | { 183 | "site" : "nbcnews", 184 | "link" : "http://rss.msnbc.msn.com/id/28180066/device/rss/rss.xml", 185 | "type" : "rss" 186 | } 187 | ] -------------------------------------------------------------------------------- /chapter2/sample/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | 7 | _ "github.com/goinaction/code/chapter2/sample/matchers" 8 | "github.com/goinaction/code/chapter2/sample/search" 9 | ) 10 | 11 | // init is called prior to main. 12 | func init() { 13 | // Change the device for logging to stdout. 14 | log.SetOutput(os.Stdout) 15 | } 16 | 17 | // main is the entry point for the program. 18 | func main() { 19 | // Perform the search for the specified term. 20 | search.Run("president") 21 | } 22 | -------------------------------------------------------------------------------- /chapter2/sample/matchers/rss.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "encoding/xml" 5 | "errors" 6 | "fmt" 7 | "log" 8 | "net/http" 9 | "regexp" 10 | 11 | "github.com/goinaction/code/chapter2/sample/search" 12 | ) 13 | 14 | type ( 15 | // item defines the fields associated with the item tag 16 | // in the rss document. 17 | item struct { 18 | XMLName xml.Name `xml:"item"` 19 | PubDate string `xml:"pubDate"` 20 | Title string `xml:"title"` 21 | Description string `xml:"description"` 22 | Link string `xml:"link"` 23 | GUID string `xml:"guid"` 24 | GeoRssPoint string `xml:"georss:point"` 25 | } 26 | 27 | // image defines the fields associated with the image tag 28 | // in the rss document. 29 | image struct { 30 | XMLName xml.Name `xml:"image"` 31 | URL string `xml:"url"` 32 | Title string `xml:"title"` 33 | Link string `xml:"link"` 34 | } 35 | 36 | // channel defines the fields associated with the channel tag 37 | // in the rss document. 38 | channel struct { 39 | XMLName xml.Name `xml:"channel"` 40 | Title string `xml:"title"` 41 | Description string `xml:"description"` 42 | Link string `xml:"link"` 43 | PubDate string `xml:"pubDate"` 44 | LastBuildDate string `xml:"lastBuildDate"` 45 | TTL string `xml:"ttl"` 46 | Language string `xml:"language"` 47 | ManagingEditor string `xml:"managingEditor"` 48 | WebMaster string `xml:"webMaster"` 49 | Image image `xml:"image"` 50 | Item []item `xml:"item"` 51 | } 52 | 53 | // rssDocument defines the fields associated with the rss document. 54 | rssDocument struct { 55 | XMLName xml.Name `xml:"rss"` 56 | Channel channel `xml:"channel"` 57 | } 58 | ) 59 | 60 | // rssMatcher implements the Matcher interface. 61 | type rssMatcher struct{} 62 | 63 | // init registers the matcher with the program. 64 | func init() { 65 | var matcher rssMatcher 66 | search.Register("rss", matcher) 67 | } 68 | 69 | // Search looks at the document for the specified search term. 70 | func (m rssMatcher) Search(feed *search.Feed, searchTerm string) ([]*search.Result, error) { 71 | var results []*search.Result 72 | 73 | log.Printf("Search Feed Type[%s] Site[%s] For URI[%s]\n", feed.Type, feed.Name, feed.URI) 74 | 75 | // Retrieve the data to search. 76 | document, err := m.retrieve(feed) 77 | if err != nil { 78 | return nil, err 79 | } 80 | 81 | for _, channelItem := range document.Channel.Item { 82 | // Check the title for the search term. 83 | matched, err := regexp.MatchString(searchTerm, channelItem.Title) 84 | if err != nil { 85 | return nil, err 86 | } 87 | 88 | // If we found a match save the result. 89 | if matched { 90 | results = append(results, &search.Result{ 91 | Field: "Title", 92 | Content: channelItem.Title, 93 | }) 94 | } 95 | 96 | // Check the description for the search term. 97 | matched, err = regexp.MatchString(searchTerm, channelItem.Description) 98 | if err != nil { 99 | return nil, err 100 | } 101 | 102 | // If we found a match save the result. 103 | if matched { 104 | results = append(results, &search.Result{ 105 | Field: "Description", 106 | Content: channelItem.Description, 107 | }) 108 | } 109 | } 110 | 111 | return results, nil 112 | } 113 | 114 | // retrieve performs a HTTP Get request for the rss feed and decodes the results. 115 | func (m rssMatcher) retrieve(feed *search.Feed) (*rssDocument, error) { 116 | if feed.URI == "" { 117 | return nil, errors.New("No rss feed uri provided") 118 | } 119 | 120 | // Retrieve the rss feed document from the web. 121 | resp, err := http.Get(feed.URI) 122 | if err != nil { 123 | return nil, err 124 | } 125 | 126 | // Close the response once we return from the function. 127 | defer resp.Body.Close() 128 | 129 | // Check the status code for a 200 so we know we have received a 130 | // proper response. 131 | if resp.StatusCode != 200 { 132 | return nil, fmt.Errorf("HTTP Response Error %d\n", resp.StatusCode) 133 | } 134 | 135 | // Decode the rss feed document into our struct type. 136 | // We don't need to check for errors, the caller can do this. 137 | var document rssDocument 138 | err = xml.NewDecoder(resp.Body).Decode(&document) 139 | return &document, err 140 | } 141 | -------------------------------------------------------------------------------- /chapter2/sample/search/default.go: -------------------------------------------------------------------------------- 1 | package search 2 | 3 | // defaultMatcher implements the default matcher. 4 | type defaultMatcher struct{} 5 | 6 | // init registers the default matcher with the program. 7 | func init() { 8 | var matcher defaultMatcher 9 | Register("default", matcher) 10 | } 11 | 12 | // Search implements the behavior for the default matcher. 13 | func (m defaultMatcher) Search(feed *Feed, searchTerm string) ([]*Result, error) { 14 | return nil, nil 15 | } 16 | -------------------------------------------------------------------------------- /chapter2/sample/search/feed.go: -------------------------------------------------------------------------------- 1 | package search 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | ) 7 | 8 | const dataFile = "data/data.json" 9 | 10 | // Feed contains information we need to process a feed. 11 | type Feed struct { 12 | Name string `json:"site"` 13 | URI string `json:"link"` 14 | Type string `json:"type"` 15 | } 16 | 17 | // RetrieveFeeds reads and unmarshals the feed data file. 18 | func RetrieveFeeds() ([]*Feed, error) { 19 | // Open the file. 20 | file, err := os.Open(dataFile) 21 | if err != nil { 22 | return nil, err 23 | } 24 | 25 | // Schedule the file to be closed once 26 | // the function returns. 27 | defer file.Close() 28 | 29 | // Decode the file into a slice of pointers 30 | // to Feed values. 31 | var feeds []*Feed 32 | err = json.NewDecoder(file).Decode(&feeds) 33 | 34 | // We don't need to check for errors, the caller can do this. 35 | return feeds, err 36 | } 37 | -------------------------------------------------------------------------------- /chapter2/sample/search/match.go: -------------------------------------------------------------------------------- 1 | package search 2 | 3 | import ( 4 | "log" 5 | ) 6 | 7 | // Result contains the result of a search. 8 | type Result struct { 9 | Field string 10 | Content string 11 | } 12 | 13 | // Matcher defines the behavior required by types that want 14 | // to implement a new search type. 15 | type Matcher interface { 16 | Search(feed *Feed, searchTerm string) ([]*Result, error) 17 | } 18 | 19 | // Match is launched as a goroutine for each individual feed to run 20 | // searches concurrently. 21 | func Match(matcher Matcher, feed *Feed, searchTerm string, results chan<- *Result) { 22 | // Perform the search against the specified matcher. 23 | searchResults, err := matcher.Search(feed, searchTerm) 24 | if err != nil { 25 | log.Println(err) 26 | return 27 | } 28 | 29 | // Write the results to the channel. 30 | for _, result := range searchResults { 31 | results <- result 32 | } 33 | } 34 | 35 | // Display writes results to the console window as they 36 | // are received by the individual goroutines. 37 | func Display(results chan *Result) { 38 | // The channel blocks until a result is written to the channel. 39 | // Once the channel is closed the for loop terminates. 40 | for result := range results { 41 | log.Printf("%s:\n%s\n\n", result.Field, result.Content) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /chapter2/sample/search/search.go: -------------------------------------------------------------------------------- 1 | package search 2 | 3 | import ( 4 | "log" 5 | "sync" 6 | ) 7 | 8 | // A map of registered matchers for searching. 9 | var matchers = make(map[string]Matcher) 10 | 11 | // Run performs the search logic. 12 | func Run(searchTerm string) { 13 | // Retrieve the list of feeds to search through. 14 | feeds, err := RetrieveFeeds() 15 | if err != nil { 16 | log.Fatal(err) 17 | } 18 | 19 | // Create an unbuffered channel to receive match results to display. 20 | results := make(chan *Result) 21 | 22 | // Setup a wait group so we can process all the feeds. 23 | var waitGroup sync.WaitGroup 24 | 25 | // Set the number of goroutines we need to wait for while 26 | // they process the individual feeds. 27 | waitGroup.Add(len(feeds)) 28 | 29 | // Launch a goroutine for each feed to find the results. 30 | for _, feed := range feeds { 31 | // Retrieve a matcher for the search. 32 | matcher, exists := matchers[feed.Type] 33 | if !exists { 34 | matcher = matchers["default"] 35 | } 36 | 37 | // Launch the goroutine to perform the search. 38 | go func(matcher Matcher, feed *Feed) { 39 | Match(matcher, feed, searchTerm, results) 40 | waitGroup.Done() 41 | }(matcher, feed) 42 | } 43 | 44 | // Launch a goroutine to monitor when all the work is done. 45 | go func() { 46 | // Wait for everything to be processed. 47 | waitGroup.Wait() 48 | 49 | // Close the channel to signal to the Display 50 | // function that we can exit the program. 51 | close(results) 52 | }() 53 | 54 | // Start displaying results as they are available and 55 | // return after the final result is displayed. 56 | Display(results) 57 | } 58 | 59 | // Register is called to register a matcher for use by the program. 60 | func Register(feedType string, matcher Matcher) { 61 | if _, exists := matchers[feedType]; exists { 62 | log.Fatalln(feedType, "Matcher already registered") 63 | } 64 | 65 | log.Println("Register", feedType, "matcher") 66 | matchers[feedType] = matcher 67 | } 68 | -------------------------------------------------------------------------------- /chapter3/dbdriver/main.go: -------------------------------------------------------------------------------- 1 | // Sample program to show how to show you how to briefly work 2 | // with the sql package. 3 | package main 4 | 5 | import ( 6 | "database/sql" 7 | 8 | _ "github.com/goinaction/code/chapter3/dbdriver/postgres" 9 | ) 10 | 11 | // main is the entry point for the application. 12 | func main() { 13 | sql.Open("postgres", "mydb") 14 | } 15 | -------------------------------------------------------------------------------- /chapter3/dbdriver/postgres/postgres.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | import ( 4 | "database/sql" 5 | "database/sql/driver" 6 | "errors" 7 | ) 8 | 9 | // PostgresDriver provides our implementation for the 10 | // sql package. 11 | type PostgresDriver struct{} 12 | 13 | // Open provides a connection to the database. 14 | func (dr PostgresDriver) Open(string) (driver.Conn, error) { 15 | return nil, errors.New("Unimplemented") 16 | } 17 | 18 | var d *PostgresDriver 19 | 20 | // init is called prior to main. 21 | func init() { 22 | d = new(PostgresDriver) 23 | sql.Register("postgres", d) 24 | } 25 | -------------------------------------------------------------------------------- /chapter3/wordcount/gowords.txt: -------------------------------------------------------------------------------- 1 | Now is the time for all good gophers to write code. 2 | -------------------------------------------------------------------------------- /chapter3/wordcount/wordcount.go: -------------------------------------------------------------------------------- 1 | // Sample program to show how to show you how to briefly work with io. 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "io/ioutil" 7 | "os" 8 | 9 | "github.com/goinaction/code/chapter3/words" 10 | ) 11 | 12 | // main is the entry point for the application. 13 | func main() { 14 | filename := os.Args[1] 15 | 16 | contents, err := ioutil.ReadFile(filename) 17 | if err != nil { 18 | fmt.Println("There was an error opening the file:", err) 19 | return 20 | } 21 | 22 | text := string(contents) 23 | 24 | count := words.CountWords(text) 25 | fmt.Printf("There are %d words in your text. \n", count) 26 | } 27 | -------------------------------------------------------------------------------- /chapter3/words/count.go: -------------------------------------------------------------------------------- 1 | // Package words provides support for counting words. 2 | package words 3 | 4 | import "strings" 5 | 6 | // CountWords counts the number of words in the specified 7 | // string and returns the count. 8 | func CountWords(text string) (count int) { 9 | count = len(strings.Fields(text)) 10 | return 11 | } 12 | -------------------------------------------------------------------------------- /chapter5/listing11/listing11.go: -------------------------------------------------------------------------------- 1 | // Sample program to show how to declare methods and how the Go 2 | // compiler supports them. 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | // user defines a user in the program. 10 | type user struct { 11 | name string 12 | email string 13 | } 14 | 15 | // notify implements a method with a value receiver. 16 | func (u user) notify() { 17 | fmt.Printf("Sending User Email To %s<%s>\n", 18 | u.name, 19 | u.email) 20 | } 21 | 22 | // changeEmail implements a method with a pointer receiver. 23 | func (u *user) changeEmail(email string) { 24 | u.email = email 25 | } 26 | 27 | // main is the entry point for the application. 28 | func main() { 29 | // Values of type user can be used to call methods 30 | // declared with a value receiver. 31 | bill := user{"Bill", "bill@email.com"} 32 | bill.notify() 33 | 34 | // Pointers of type user can also be used to call methods 35 | // declared with a value receiver. 36 | lisa := &user{"Lisa", "lisa@email.com"} 37 | lisa.notify() 38 | 39 | // Values of type user can be used to call methods 40 | // declared with a pointer receiver. 41 | bill.changeEmail("bill@newdomain.com") 42 | bill.notify() 43 | 44 | // Pointers of type user can be used to call methods 45 | // declared with a pointer receiver. 46 | lisa.changeEmail("lisa@newdomain.com") 47 | lisa.notify() 48 | } 49 | -------------------------------------------------------------------------------- /chapter5/listing34/listing34.go: -------------------------------------------------------------------------------- 1 | // Sample program to show how to write a simple version of curl using 2 | // the io.Reader and io.Writer interface support. 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "io" 8 | "net/http" 9 | "os" 10 | ) 11 | 12 | // init is called before main. 13 | func init() { 14 | if len(os.Args) != 2 { 15 | fmt.Println("Usage: ./example2 ") 16 | os.Exit(-1) 17 | } 18 | } 19 | 20 | // main is the entry point for the application. 21 | func main() { 22 | // Get a response from the web server. 23 | r, err := http.Get(os.Args[1]) 24 | if err != nil { 25 | fmt.Println(err) 26 | return 27 | } 28 | 29 | // Copies from the Body to Stdout. 30 | io.Copy(os.Stdout, r.Body) 31 | if err := r.Body.Close(); err != nil { 32 | fmt.Println(err) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /chapter5/listing35/listing35.go: -------------------------------------------------------------------------------- 1 | // Sample program to show how a bytes.Buffer can also be used 2 | // with the io.Copy function. 3 | package main 4 | 5 | import ( 6 | "bytes" 7 | "fmt" 8 | "io" 9 | "os" 10 | ) 11 | 12 | // main is the entry point for the application. 13 | func main() { 14 | var b bytes.Buffer 15 | 16 | // Write a string to the buffer. 17 | b.Write([]byte("Hello")) 18 | 19 | // Use Fprintf to concatenate a string to the Buffer. 20 | fmt.Fprintf(&b, "World!") 21 | 22 | // Write the content of the Buffer to stdout. 23 | io.Copy(os.Stdout, &b) 24 | } 25 | -------------------------------------------------------------------------------- /chapter5/listing36/listing36.go: -------------------------------------------------------------------------------- 1 | // Sample program to show how to use an interface in Go. 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | ) 7 | 8 | // notifier is an interface that defined notification 9 | // type behavior. 10 | type notifier interface { 11 | notify() 12 | } 13 | 14 | // user defines a user in the program. 15 | type user struct { 16 | name string 17 | email string 18 | } 19 | 20 | // notify implements a method with a pointer receiver. 21 | func (u *user) notify() { 22 | fmt.Printf("Sending user email to %s<%s>\n", 23 | u.name, 24 | u.email) 25 | } 26 | 27 | // main is the entry point for the application. 28 | func main() { 29 | // Create a value of type User and send a notification. 30 | u := user{"Bill", "bill@email.com"} 31 | 32 | sendNotification(u) 33 | 34 | // ./listing36.go:32: cannot use u (type user) as type 35 | // notifier in argument to sendNotification: 36 | // user does not implement notifier 37 | // (notify method has pointer receiver) 38 | } 39 | 40 | // sendNotification accepts values that implement the notifier 41 | // interface and sends notifications. 42 | func sendNotification(n notifier) { 43 | n.notify() 44 | } 45 | -------------------------------------------------------------------------------- /chapter5/listing46/listing46.go: -------------------------------------------------------------------------------- 1 | // Sample program to show how you can't always get the 2 | // address of a value. 3 | package main 4 | 5 | import "fmt" 6 | 7 | // duration is a type with a base type of int. 8 | type duration int 9 | 10 | // format pretty-prints the duration value. 11 | func (d *duration) pretty() string { 12 | return fmt.Sprintf("Duration: %d", *d) 13 | } 14 | 15 | // main is the entry point for the application. 16 | func main() { 17 | duration(42).pretty() 18 | 19 | // ./listing46.go:17: cannot call pointer method on duration(42) 20 | // ./listing46.go:17: cannot take the address of duration(42) 21 | } 22 | -------------------------------------------------------------------------------- /chapter5/listing48/listing48.go: -------------------------------------------------------------------------------- 1 | // Sample program to show how polymorphic behavior with interfaces. 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | ) 7 | 8 | // notifier is an interface that defines notification 9 | // type behavior. 10 | type notifier interface { 11 | notify() 12 | } 13 | 14 | // user defines a user in the program. 15 | type user struct { 16 | name string 17 | email string 18 | } 19 | 20 | // notify implements the notifier interface with a pointer receiver. 21 | func (u *user) notify() { 22 | fmt.Printf("Sending user email to %s<%s>\n", 23 | u.name, 24 | u.email) 25 | } 26 | 27 | // admin defines a admin in the program. 28 | type admin struct { 29 | name string 30 | email string 31 | } 32 | 33 | // notify implements the notifier interface with a pointer receiver. 34 | func (a *admin) notify() { 35 | fmt.Printf("Sending admin email to %s<%s>\n", 36 | a.name, 37 | a.email) 38 | } 39 | 40 | // main is the entry point for the application. 41 | func main() { 42 | // Create a user value and pass it to sendNotification. 43 | bill := user{"Bill", "bill@email.com"} 44 | sendNotification(&bill) 45 | 46 | // Create an admin value and pass it to sendNotification. 47 | lisa := admin{"Lisa", "lisa@email.com"} 48 | sendNotification(&lisa) 49 | } 50 | 51 | // sendNotification accepts values that implement the notifier 52 | // interface and sends notifications. 53 | func sendNotification(n notifier) { 54 | n.notify() 55 | } 56 | -------------------------------------------------------------------------------- /chapter5/listing50/listing50.go: -------------------------------------------------------------------------------- 1 | // Sample program to show how to embed a type into another type and 2 | // the relationship between the inner and outer type. 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | // user defines a user in the program. 10 | type user struct { 11 | name string 12 | email string 13 | } 14 | 15 | // notify implements a method that can be called via 16 | // a value of type user. 17 | func (u *user) notify() { 18 | fmt.Printf("Sending user email to %s<%s>\n", 19 | u.name, 20 | u.email) 21 | } 22 | 23 | // admin represents an admin user with privileges. 24 | type admin struct { 25 | user // Embedded Type 26 | level string 27 | } 28 | 29 | // main is the entry point for the application. 30 | func main() { 31 | // Create an admin user. 32 | ad := admin{ 33 | user: user{ 34 | name: "john smith", 35 | email: "john@yahoo.com", 36 | }, 37 | level: "super", 38 | } 39 | 40 | // We can access the inner type's method directly. 41 | ad.user.notify() 42 | 43 | // The inner type's method is promoted. 44 | ad.notify() 45 | } 46 | -------------------------------------------------------------------------------- /chapter5/listing56/listing56.go: -------------------------------------------------------------------------------- 1 | // Sample program to show how embedded types work with interfaces. 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | ) 7 | 8 | // notifier is an interface that defined notification 9 | // type behavior. 10 | type notifier interface { 11 | notify() 12 | } 13 | 14 | // user defines a user in the program. 15 | type user struct { 16 | name string 17 | email string 18 | } 19 | 20 | // notify implements a method that can be called via 21 | // a value of type user. 22 | func (u *user) notify() { 23 | fmt.Printf("Sending user email to %s<%s>\n", 24 | u.name, 25 | u.email) 26 | } 27 | 28 | // admin represents an admin user with privileges. 29 | type admin struct { 30 | user 31 | level string 32 | } 33 | 34 | // main is the entry point for the application. 35 | func main() { 36 | // Create an admin user. 37 | ad := admin{ 38 | user: user{ 39 | name: "john smith", 40 | email: "john@yahoo.com", 41 | }, 42 | level: "super", 43 | } 44 | 45 | // Send the admin user a notification. 46 | // The embedded inner type's implementation of the 47 | // interface is "promoted" to the outer type. 48 | sendNotification(&ad) 49 | } 50 | 51 | // sendNotification accepts values that implement the notifier 52 | // interface and sends notifications. 53 | func sendNotification(n notifier) { 54 | n.notify() 55 | } 56 | -------------------------------------------------------------------------------- /chapter5/listing60/listing60.go: -------------------------------------------------------------------------------- 1 | // Sample program to show what happens when the outer and inner 2 | // type implement the same interface. 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | // notifier is an interface that defined notification 10 | // type behavior. 11 | type notifier interface { 12 | notify() 13 | } 14 | 15 | // user defines a user in the program. 16 | type user struct { 17 | name string 18 | email string 19 | } 20 | 21 | // notify implements a method that can be called via 22 | // a value of type user. 23 | func (u *user) notify() { 24 | fmt.Printf("Sending user email to %s<%s>\n", 25 | u.name, 26 | u.email) 27 | } 28 | 29 | // admin represents an admin user with privileges. 30 | type admin struct { 31 | user 32 | level string 33 | } 34 | 35 | // notify implements a method that can be called via 36 | // a value of type Admin. 37 | func (a *admin) notify() { 38 | fmt.Printf("Sending admin email to %s<%s>\n", 39 | a.name, 40 | a.email) 41 | } 42 | 43 | // main is the entry point for the application. 44 | func main() { 45 | // Create an admin user. 46 | ad := admin{ 47 | user: user{ 48 | name: "john smith", 49 | email: "john@yahoo.com", 50 | }, 51 | level: "super", 52 | } 53 | 54 | // Send the admin user a notification. 55 | // The embedded inner type's implementation of the 56 | // interface is NOT "promoted" to the outer type. 57 | sendNotification(&ad) 58 | 59 | // We can access the inner type's method directly. 60 | ad.user.notify() 61 | 62 | // The inner type's method is NOT promoted. 63 | ad.notify() 64 | } 65 | 66 | // sendNotification accepts values that implement the notifier 67 | // interface and sends notifications. 68 | func sendNotification(n notifier) { 69 | n.notify() 70 | } 71 | -------------------------------------------------------------------------------- /chapter5/listing64/counters/counters.go: -------------------------------------------------------------------------------- 1 | // Package counters provides alert counter support. 2 | package counters 3 | 4 | // alertCounter is an unexported type that 5 | // contains an integer counter for alerts. 6 | type alertCounter int 7 | -------------------------------------------------------------------------------- /chapter5/listing64/listing64.go: -------------------------------------------------------------------------------- 1 | // Sample program to show how the program can't access an 2 | // unexported identifier from another package. 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/goinaction/code/chapter5/listing64/counters" 9 | ) 10 | 11 | // main is the entry point for the application. 12 | func main() { 13 | // Create a variable of the unexported type and initialize 14 | // the value to 10. 15 | counter := counters.alertCounter(10) 16 | 17 | // ./listing64.go:15: cannot refer to unexported name 18 | // counters.alertCounter 19 | // ./listing64.go:15: undefined: counters.alertCounter 20 | 21 | fmt.Printf("Counter: %d\n", counter) 22 | } 23 | -------------------------------------------------------------------------------- /chapter5/listing68/counters/counters.go: -------------------------------------------------------------------------------- 1 | // Package counters provides alert counter support. 2 | package counters 3 | 4 | // alertCounter is an unexported type that 5 | // contains an integer counter for alerts. 6 | type alertCounter int 7 | 8 | // New creates and returns values of the unexported 9 | // type alertCounter. 10 | func New(value int) alertCounter { 11 | return alertCounter(value) 12 | } 13 | -------------------------------------------------------------------------------- /chapter5/listing68/listing68.go: -------------------------------------------------------------------------------- 1 | // Sample program to show how the program can access a value 2 | // of an unexported identifier from another package. 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/goinaction/code/chapter5/listing68/counters" 9 | ) 10 | 11 | // main is the entry point for the application. 12 | func main() { 13 | // Create a variable of the unexported type using the exported 14 | // New function from the package counters. 15 | counter := counters.New(10) 16 | 17 | fmt.Printf("Counter: %d\n", counter) 18 | } 19 | -------------------------------------------------------------------------------- /chapter5/listing71/entities/entities.go: -------------------------------------------------------------------------------- 1 | // Package entities contains support for types of 2 | // people in the system. 3 | package entities 4 | 5 | // User defines a user in the program. 6 | type User struct { 7 | Name string 8 | email string 9 | } 10 | -------------------------------------------------------------------------------- /chapter5/listing71/listing71.go: -------------------------------------------------------------------------------- 1 | // Sample program to show how unexported fields from an exported 2 | // struct type can't be accessed directly. 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/goinaction/code/chapter5/listing71/entities" 9 | ) 10 | 11 | // main is the entry point for the application. 12 | func main() { 13 | // Create a value of type User from the entities package. 14 | u := entities.User{ 15 | Name: "Bill", 16 | email: "bill@email.com", 17 | } 18 | 19 | // ./example71.go:16: unknown entities.User field 'email' in 20 | // struct literal 21 | 22 | fmt.Printf("User: %v\n", u) 23 | } 24 | -------------------------------------------------------------------------------- /chapter5/listing74/entities/entities.go: -------------------------------------------------------------------------------- 1 | // Package entities contains support for types of 2 | // people in the system. 3 | package entities 4 | 5 | // user defines a user in the program. 6 | type user struct { 7 | Name string 8 | Email string 9 | } 10 | 11 | // Admin defines an admin in the program. 12 | type Admin struct { 13 | user // The embedded type is unexported. 14 | Rights int 15 | } 16 | -------------------------------------------------------------------------------- /chapter5/listing74/listing74.go: -------------------------------------------------------------------------------- 1 | // Sample program to show how unexported fields from an exported 2 | // struct type can't be accessed directly. 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/goinaction/code/chapter5/listing74/entities" 9 | ) 10 | 11 | // main is the entry point for the application. 12 | func main() { 13 | // Create a value of type Admin from the entities package. 14 | a := entities.Admin{ 15 | Rights: 10, 16 | } 17 | 18 | // Set the exported fields from the unexported 19 | // inner type. 20 | a.Name = "Bill" 21 | a.Email = "bill@email.com" 22 | 23 | fmt.Printf("User: %v\n", a) 24 | } 25 | -------------------------------------------------------------------------------- /chapter6/listing01/listing01.go: -------------------------------------------------------------------------------- 1 | // This sample program demonstrates how to create goroutines and 2 | // how the scheduler behaves. 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "runtime" 8 | "sync" 9 | ) 10 | 11 | // main is the entry point for all Go programs. 12 | func main() { 13 | // Allocate 1 logical processor for the scheduler to use. 14 | runtime.GOMAXPROCS(1) 15 | 16 | // wg is used to wait for the program to finish. 17 | // Add a count of two, one for each goroutine. 18 | var wg sync.WaitGroup 19 | wg.Add(2) 20 | 21 | fmt.Println("Start Goroutines") 22 | 23 | // Declare an anonymous function and create a goroutine. 24 | go func() { 25 | // Schedule the call to Done to tell main we are done. 26 | defer wg.Done() 27 | 28 | // Display the alphabet three times 29 | for count := 0; count < 3; count++ { 30 | for char := 'a'; char < 'a'+26; char++ { 31 | fmt.Printf("%c ", char) 32 | } 33 | } 34 | }() 35 | 36 | // Declare an anonymous function and create a goroutine. 37 | go func() { 38 | // Schedule the call to Done to tell main we are done. 39 | defer wg.Done() 40 | 41 | // Display the alphabet three times 42 | for count := 0; count < 3; count++ { 43 | for char := 'A'; char < 'A'+26; char++ { 44 | fmt.Printf("%c ", char) 45 | } 46 | } 47 | }() 48 | 49 | // Wait for the goroutines to finish. 50 | fmt.Println("Waiting To Finish") 51 | wg.Wait() 52 | 53 | fmt.Println("\nTerminating Program") 54 | } 55 | -------------------------------------------------------------------------------- /chapter6/listing04/listing04.go: -------------------------------------------------------------------------------- 1 | // This sample program demonstrates how the goroutine scheduler 2 | // will time slice goroutines on a single thread. 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "runtime" 8 | "sync" 9 | ) 10 | 11 | // wg is used to wait for the program to finish. 12 | var wg sync.WaitGroup 13 | 14 | // main is the entry point for all Go programs. 15 | func main() { 16 | // Allocate 1 logical processors for the scheduler to use. 17 | runtime.GOMAXPROCS(1) 18 | 19 | // Add a count of two, one for each goroutine. 20 | wg.Add(2) 21 | 22 | // Create two goroutines. 23 | fmt.Println("Create Goroutines") 24 | go printPrime("A") 25 | go printPrime("B") 26 | 27 | // Wait for the goroutines to finish. 28 | fmt.Println("Waiting To Finish") 29 | wg.Wait() 30 | 31 | fmt.Println("Terminating Program") 32 | } 33 | 34 | // printPrime displays prime numbers for the first 5000 numbers. 35 | func printPrime(prefix string) { 36 | // Schedule the call to Done to tell main we are done. 37 | defer wg.Done() 38 | 39 | next: 40 | for outer := 2; outer < 5000; outer++ { 41 | for inner := 2; inner < outer; inner++ { 42 | if outer%inner == 0 { 43 | continue next 44 | } 45 | } 46 | fmt.Printf("%s:%d\n", prefix, outer) 47 | } 48 | fmt.Println("Completed", prefix) 49 | } 50 | -------------------------------------------------------------------------------- /chapter6/listing07/listing07.go: -------------------------------------------------------------------------------- 1 | // This sample program demonstrates how to create goroutines and 2 | // how the goroutine scheduler behaves with two logical processor. 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "runtime" 8 | "sync" 9 | ) 10 | 11 | // main is the entry point for all Go programs. 12 | func main() { 13 | // Allocate two logical processors for the scheduler to use. 14 | runtime.GOMAXPROCS(2) 15 | 16 | // wg is used to wait for the program to finish. 17 | // Add a count of two, one for each goroutine. 18 | var wg sync.WaitGroup 19 | wg.Add(2) 20 | 21 | fmt.Println("Start Goroutines") 22 | 23 | // Declare an anonymous function and create a goroutine. 24 | go func() { 25 | // Schedule the call to Done to tell main we are done. 26 | defer wg.Done() 27 | 28 | // Display the alphabet three times. 29 | for count := 0; count < 3; count++ { 30 | for char := 'a'; char < 'a'+26; char++ { 31 | fmt.Printf("%c ", char) 32 | } 33 | } 34 | }() 35 | 36 | // Declare an anonymous function and create a goroutine. 37 | go func() { 38 | // Schedule the call to Done to tell main we are done. 39 | defer wg.Done() 40 | 41 | // Display the alphabet three times. 42 | for count := 0; count < 3; count++ { 43 | for char := 'A'; char < 'A'+26; char++ { 44 | fmt.Printf("%c ", char) 45 | } 46 | } 47 | }() 48 | 49 | // Wait for the goroutines to finish. 50 | fmt.Println("Waiting To Finish") 51 | wg.Wait() 52 | 53 | fmt.Println("\nTerminating Program") 54 | } 55 | -------------------------------------------------------------------------------- /chapter6/listing09/listing09.go: -------------------------------------------------------------------------------- 1 | // This sample program demonstrates how to create race 2 | // conditions in our programs. We don't want to do this. 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "runtime" 8 | "sync" 9 | ) 10 | 11 | var ( 12 | // counter is a variable incremented by all goroutines. 13 | counter int 14 | 15 | // wg is used to wait for the program to finish. 16 | wg sync.WaitGroup 17 | ) 18 | 19 | // main is the entry point for all Go programs. 20 | func main() { 21 | // Add a count of two, one for each goroutine. 22 | wg.Add(2) 23 | 24 | // Create two goroutines. 25 | go incCounter(1) 26 | go incCounter(2) 27 | 28 | // Wait for the goroutines to finish. 29 | wg.Wait() 30 | fmt.Println("Final Counter:", counter) 31 | } 32 | 33 | // incCounter increments the package level counter variable. 34 | func incCounter(id int) { 35 | // Schedule the call to Done to tell main we are done. 36 | defer wg.Done() 37 | 38 | for count := 0; count < 2; count++ { 39 | // Capture the value of Counter. 40 | value := counter 41 | 42 | // Yield the thread and be placed back in queue. 43 | runtime.Gosched() 44 | 45 | // Increment our local value of Counter. 46 | value++ 47 | 48 | // Store the value back into Counter. 49 | counter = value 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /chapter6/listing13/listing13.go: -------------------------------------------------------------------------------- 1 | // This sample program demonstrates how to use the atomic 2 | // package to provide safe access to numeric types. 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "runtime" 8 | "sync" 9 | "sync/atomic" 10 | ) 11 | 12 | var ( 13 | // counter is a variable incremented by all goroutines. 14 | counter int64 15 | 16 | // wg is used to wait for the program to finish. 17 | wg sync.WaitGroup 18 | ) 19 | 20 | // main is the entry point for all Go programs. 21 | func main() { 22 | // Add a count of two, one for each goroutine. 23 | wg.Add(2) 24 | 25 | // Create two goroutines. 26 | go incCounter(1) 27 | go incCounter(2) 28 | 29 | // Wait for the goroutines to finish. 30 | wg.Wait() 31 | 32 | // Display the final value. 33 | fmt.Println("Final Counter:", counter) 34 | } 35 | 36 | // incCounter increments the package level counter variable. 37 | func incCounter(id int) { 38 | // Schedule the call to Done to tell main we are done. 39 | defer wg.Done() 40 | 41 | for count := 0; count < 2; count++ { 42 | // Safely Add One To Counter. 43 | atomic.AddInt64(&counter, 1) 44 | 45 | // Yield the thread and be placed back in queue. 46 | runtime.Gosched() 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /chapter6/listing15/listing15.go: -------------------------------------------------------------------------------- 1 | // This sample program demonstrates how to use the atomic 2 | // package functions Store and Load to provide safe access 3 | // to numeric types. 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "sync" 9 | "sync/atomic" 10 | "time" 11 | ) 12 | 13 | var ( 14 | // shutdown is a flag to alert running goroutines to shutdown. 15 | shutdown int64 16 | 17 | // wg is used to wait for the program to finish. 18 | wg sync.WaitGroup 19 | ) 20 | 21 | // main is the entry point for all Go programs. 22 | func main() { 23 | // Add a count of two, one for each goroutine. 24 | wg.Add(2) 25 | 26 | // Create two goroutines. 27 | go doWork("A") 28 | go doWork("B") 29 | 30 | // Give the goroutines time to run. 31 | time.Sleep(1 * time.Second) 32 | 33 | // Safely flag it is time to shutdown. 34 | fmt.Println("Shutdown Now") 35 | atomic.StoreInt64(&shutdown, 1) 36 | 37 | // Wait for the goroutines to finish. 38 | wg.Wait() 39 | } 40 | 41 | // doWork simulates a goroutine performing work and 42 | // checking the Shutdown flag to terminate early. 43 | func doWork(name string) { 44 | // Schedule the call to Done to tell main we are done. 45 | defer wg.Done() 46 | 47 | for { 48 | fmt.Printf("Doing %s Work\n", name) 49 | time.Sleep(250 * time.Millisecond) 50 | 51 | // Do we need to shutdown. 52 | if atomic.LoadInt64(&shutdown) == 1 { 53 | fmt.Printf("Shutting %s Down\n", name) 54 | break 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /chapter6/listing16/listing16.go: -------------------------------------------------------------------------------- 1 | // This sample program demonstrates how to use a mutex 2 | // to define critical sections of code that need synchronous 3 | // access. 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "runtime" 9 | "sync" 10 | ) 11 | 12 | var ( 13 | // counter is a variable incremented by all goroutines. 14 | counter int 15 | 16 | // wg is used to wait for the program to finish. 17 | wg sync.WaitGroup 18 | 19 | // mutex is used to define a critical section of code. 20 | mutex sync.Mutex 21 | ) 22 | 23 | // main is the entry point for all Go programs. 24 | func main() { 25 | // Add a count of two, one for each goroutine. 26 | wg.Add(2) 27 | 28 | // Create two goroutines. 29 | go incCounter(1) 30 | go incCounter(2) 31 | 32 | // Wait for the goroutines to finish. 33 | wg.Wait() 34 | fmt.Printf("Final Counter: %d\n", counter) 35 | } 36 | 37 | // incCounter increments the package level Counter variable 38 | // using the Mutex to synchronize and provide safe access. 39 | func incCounter(id int) { 40 | // Schedule the call to Done to tell main we are done. 41 | defer wg.Done() 42 | 43 | for count := 0; count < 2; count++ { 44 | // Only allow one goroutine through this 45 | // critical section at a time. 46 | mutex.Lock() 47 | { 48 | // Capture the value of counter. 49 | value := counter 50 | 51 | // Yield the thread and be placed back in queue. 52 | runtime.Gosched() 53 | 54 | // Increment our local value of counter. 55 | value++ 56 | 57 | // Store the value back into counter. 58 | counter = value 59 | } 60 | mutex.Unlock() 61 | // Release the lock and allow any 62 | // waiting goroutine through. 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /chapter6/listing20/listing20.go: -------------------------------------------------------------------------------- 1 | // This sample program demonstrates how to use an unbuffered 2 | // channel to simulate a game of tennis between two goroutines. 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "math/rand" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | // wg is used to wait for the program to finish. 13 | var wg sync.WaitGroup 14 | 15 | func init() { 16 | rand.Seed(time.Now().UnixNano()) 17 | } 18 | 19 | // main is the entry point for all Go programs. 20 | func main() { 21 | // Create an unbuffered channel. 22 | court := make(chan int) 23 | 24 | // Add a count of two, one for each goroutine. 25 | wg.Add(2) 26 | 27 | // Launch two players. 28 | go player("Nadal", court) 29 | go player("Djokovic", court) 30 | 31 | // Start the set. 32 | court <- 1 33 | 34 | // Wait for the game to finish. 35 | wg.Wait() 36 | } 37 | 38 | // player simulates a person playing the game of tennis. 39 | func player(name string, court chan int) { 40 | // Schedule the call to Done to tell main we are done. 41 | defer wg.Done() 42 | 43 | for { 44 | // Wait for the ball to be hit back to us. 45 | ball, ok := <-court 46 | if !ok { 47 | // If the channel was closed we won. 48 | fmt.Printf("Player %s Won\n", name) 49 | return 50 | } 51 | 52 | // Pick a random number and see if we miss the ball. 53 | n := rand.Intn(100) 54 | if n%13 == 0 { 55 | fmt.Printf("Player %s Missed\n", name) 56 | 57 | // Close the channel to signal we lost. 58 | close(court) 59 | return 60 | } 61 | 62 | // Display and then increment the hit count by one. 63 | fmt.Printf("Player %s Hit %d\n", name, ball) 64 | ball++ 65 | 66 | // Hit the ball back to the opposing player. 67 | court <- ball 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /chapter6/listing22/listing22.go: -------------------------------------------------------------------------------- 1 | // This sample program demonstrates how to use an unbuffered 2 | // channel to simulate a relay race between four goroutines. 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "sync" 8 | "time" 9 | ) 10 | 11 | // wg is used to wait for the program to finish. 12 | var wg sync.WaitGroup 13 | 14 | // main is the entry point for all Go programs. 15 | func main() { 16 | // Create an unbuffered channel. 17 | baton := make(chan int) 18 | 19 | // Add a count of one for the last runner. 20 | wg.Add(1) 21 | 22 | // First runner to his mark. 23 | go Runner(baton) 24 | 25 | // Start the race. 26 | baton <- 1 27 | 28 | // Wait for the race to finish. 29 | wg.Wait() 30 | } 31 | 32 | // Runner simulates a person running in the relay race. 33 | func Runner(baton chan int) { 34 | var newRunner int 35 | 36 | // Wait to receive the baton. 37 | runner := <-baton 38 | 39 | // Start running around the track. 40 | fmt.Printf("Runner %d Running With Baton\n", runner) 41 | 42 | // New runner to the line. 43 | if runner != 4 { 44 | newRunner = runner + 1 45 | fmt.Printf("Runner %d To The Line\n", newRunner) 46 | go Runner(baton) 47 | } 48 | 49 | // Running around the track. 50 | time.Sleep(100 * time.Millisecond) 51 | 52 | // Is the race over. 53 | if runner == 4 { 54 | fmt.Printf("Runner %d Finished, Race Over\n", runner) 55 | wg.Done() 56 | return 57 | } 58 | 59 | // Exchange the baton for the next runner. 60 | fmt.Printf("Runner %d Exchange With Runner %d\n", 61 | runner, 62 | newRunner) 63 | 64 | baton <- newRunner 65 | } 66 | -------------------------------------------------------------------------------- /chapter6/listing24/listing24.go: -------------------------------------------------------------------------------- 1 | // This sample program demonstrates how to use a buffered 2 | // channel to work on multiple tasks with a predefined number 3 | // of goroutines. 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "math/rand" 9 | "sync" 10 | "time" 11 | ) 12 | 13 | const ( 14 | numberGoroutines = 4 // Number of goroutines to use. 15 | taskLoad = 10 // Amount of work to process. 16 | ) 17 | 18 | // wg is used to wait for the program to finish. 19 | var wg sync.WaitGroup 20 | 21 | // init is called to initialize the package by the 22 | // Go runtime prior to any other code being executed. 23 | func init() { 24 | // Seed the random number generator. 25 | rand.Seed(time.Now().Unix()) 26 | } 27 | 28 | // main is the entry point for all Go programs. 29 | func main() { 30 | // Create a buffered channel to manage the task load. 31 | tasks := make(chan string, taskLoad) 32 | 33 | // Launch goroutines to handle the work. 34 | wg.Add(numberGoroutines) 35 | for gr := 1; gr <= numberGoroutines; gr++ { 36 | go worker(tasks, gr) 37 | } 38 | 39 | // Add a bunch of work to get done. 40 | for post := 1; post <= taskLoad; post++ { 41 | tasks <- fmt.Sprintf("Task : %d", post) 42 | } 43 | 44 | // Close the channel so the goroutines will quit 45 | // when all the work is done. 46 | close(tasks) 47 | 48 | // Wait for all the work to get done. 49 | wg.Wait() 50 | } 51 | 52 | // worker is launched as a goroutine to process work from 53 | // the buffered channel. 54 | func worker(tasks chan string, worker int) { 55 | // Report that we just returned. 56 | defer wg.Done() 57 | 58 | for { 59 | // Wait for work to be assigned. 60 | task, ok := <-tasks 61 | if !ok { 62 | // This means the channel is empty and closed. 63 | fmt.Printf("Worker: %d : Shutting Down\n", worker) 64 | return 65 | } 66 | 67 | // Display we are starting the work. 68 | fmt.Printf("Worker: %d : Started %s\n", worker, task) 69 | 70 | // Randomly wait to simulate work time. 71 | sleep := rand.Int63n(100) 72 | time.Sleep(time.Duration(sleep) * time.Millisecond) 73 | 74 | // Display we finished the work. 75 | fmt.Printf("Worker: %d : Completed %s\n", worker, task) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /chapter7/patterns/pool/main/main.go: -------------------------------------------------------------------------------- 1 | // This sample program demonstrates how to use the pool package 2 | // to share a simulated set of database connections. 3 | package main 4 | 5 | import ( 6 | "io" 7 | "log" 8 | "math/rand" 9 | "sync" 10 | "sync/atomic" 11 | "time" 12 | 13 | "github.com/goinaction/code/chapter7/patterns/pool" 14 | ) 15 | 16 | const ( 17 | maxGoroutines = 25 // the number of routines to use. 18 | pooledResources = 2 // number of resources in the pool 19 | ) 20 | 21 | // dbConnection simulates a resource to share. 22 | type dbConnection struct { 23 | ID int32 24 | } 25 | 26 | // Close implements the io.Closer interface so dbConnection 27 | // can be managed by the pool. Close performs any resource 28 | // release management. 29 | func (dbConn *dbConnection) Close() error { 30 | log.Println("Close: Connection", dbConn.ID) 31 | return nil 32 | } 33 | 34 | // idCounter provides support for giving each connection a unique id. 35 | var idCounter int32 36 | 37 | // createConnection is a factory method that will be called by 38 | // the pool when a new connection is needed. 39 | func createConnection() (io.Closer, error) { 40 | id := atomic.AddInt32(&idCounter, 1) 41 | log.Println("Create: New Connection", id) 42 | 43 | return &dbConnection{id}, nil 44 | } 45 | 46 | // main is the entry point for all Go programs. 47 | func main() { 48 | var wg sync.WaitGroup 49 | wg.Add(maxGoroutines) 50 | 51 | // Create the pool to manage our connections. 52 | p, err := pool.New(createConnection, pooledResources) 53 | if err != nil { 54 | log.Println(err) 55 | } 56 | 57 | // Perform queries using connections from the pool. 58 | for query := 0; query < maxGoroutines; query++ { 59 | // Each goroutine needs its own copy of the query 60 | // value else they will all be sharing the same query 61 | // variable. 62 | go func(q int) { 63 | performQueries(q, p) 64 | wg.Done() 65 | }(query) 66 | } 67 | 68 | // Wait for the goroutines to finish. 69 | wg.Wait() 70 | 71 | // Close the pool. 72 | log.Println("Shutdown Program.") 73 | p.Close() 74 | } 75 | 76 | // performQueries tests the resource pool of connections. 77 | func performQueries(query int, p *pool.Pool) { 78 | // Acquire a connection from the pool. 79 | conn, err := p.Acquire() 80 | if err != nil { 81 | log.Println(err) 82 | return 83 | } 84 | 85 | // Release the connection back to the pool. 86 | defer p.Release(conn) 87 | 88 | // Wait to simulate a query response. 89 | time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond) 90 | log.Printf("Query: QID[%d] CID[%d]\n", query, conn.(*dbConnection).ID) 91 | } 92 | -------------------------------------------------------------------------------- /chapter7/patterns/pool/pool.go: -------------------------------------------------------------------------------- 1 | // Example provided with help from Fatih Arslan and Gabriel Aszalos. 2 | // Package pool manages a user defined set of resources. 3 | package pool 4 | 5 | import ( 6 | "errors" 7 | "io" 8 | "log" 9 | "sync" 10 | ) 11 | 12 | // Pool manages a set of resources that can be shared safely by 13 | // multiple goroutines. The resource being managed must implement 14 | // the io.Closer interface. 15 | type Pool struct { 16 | m sync.Mutex 17 | resources chan io.Closer 18 | factory func() (io.Closer, error) 19 | closed bool 20 | } 21 | 22 | // ErrPoolClosed is returned when an Acquire returns on a 23 | // closed pool. 24 | var ErrPoolClosed = errors.New("Pool has been closed.") 25 | 26 | // New creates a pool that manages resources. A pool requires a 27 | // function that can allocate a new resource and the size of 28 | // the pool. 29 | func New(fn func() (io.Closer, error), size uint) (*Pool, error) { 30 | if size <= 0 { 31 | return nil, errors.New("Size value too small.") 32 | } 33 | 34 | return &Pool{ 35 | factory: fn, 36 | resources: make(chan io.Closer, size), 37 | }, nil 38 | } 39 | 40 | // Acquire retrieves a resource from the pool. 41 | func (p *Pool) Acquire() (io.Closer, error) { 42 | select { 43 | // Check for a free resource. 44 | case r, ok := <-p.resources: 45 | log.Println("Acquire:", "Shared Resource") 46 | if !ok { 47 | return nil, ErrPoolClosed 48 | } 49 | return r, nil 50 | 51 | // Provide a new resource since there are none available. 52 | default: 53 | log.Println("Acquire:", "New Resource") 54 | return p.factory() 55 | } 56 | } 57 | 58 | // Release places a new resource onto the pool. 59 | func (p *Pool) Release(r io.Closer) { 60 | // Secure this operation with the Close operation. 61 | p.m.Lock() 62 | defer p.m.Unlock() 63 | 64 | // If the pool is closed, discard the resource. 65 | if p.closed { 66 | r.Close() 67 | return 68 | } 69 | 70 | select { 71 | // Attempt to place the new resource on the queue. 72 | case p.resources <- r: 73 | log.Println("Release:", "In Queue") 74 | 75 | // If the queue is already at cap we close the resource. 76 | default: 77 | log.Println("Release:", "Closing") 78 | r.Close() 79 | } 80 | } 81 | 82 | // Close will shutdown the pool and close all existing resources. 83 | func (p *Pool) Close() { 84 | // Secure this operation with the Release operation. 85 | p.m.Lock() 86 | defer p.m.Unlock() 87 | 88 | // If the pool is already close, don't do anything. 89 | if p.closed { 90 | return 91 | } 92 | 93 | // Set the pool as closed. 94 | p.closed = true 95 | 96 | // Close the channel before we drain the channel of its 97 | // resources. If we don't do this, we will have a deadlock. 98 | close(p.resources) 99 | 100 | // Close the resources 101 | for r := range p.resources { 102 | r.Close() 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /chapter7/patterns/runner/main/main.go: -------------------------------------------------------------------------------- 1 | // This sample program demonstrates how to use a channel to 2 | // monitor the amount of time the program is running and terminate 3 | // the program if it runs too long. 4 | package main 5 | 6 | import ( 7 | "log" 8 | "os" 9 | "time" 10 | 11 | "github.com/goinaction/code/chapter7/patterns/runner" 12 | ) 13 | 14 | // timeout is the number of second the program has to finish. 15 | const timeout = 3 * time.Second 16 | 17 | // main is the entry point for the program. 18 | func main() { 19 | log.Println("Starting work.") 20 | 21 | // Create a new timer value for this run. 22 | r := runner.New(timeout) 23 | 24 | // Add the tasks to be run. 25 | r.Add(createTask(), createTask(), createTask()) 26 | 27 | // Run the tasks and handle the result. 28 | if err := r.Start(); err != nil { 29 | switch err { 30 | case runner.ErrTimeout: 31 | log.Println("Terminating due to timeout.") 32 | os.Exit(1) 33 | case runner.ErrInterrupt: 34 | log.Println("Terminating due to interrupt.") 35 | os.Exit(2) 36 | } 37 | } 38 | 39 | log.Println("Process ended.") 40 | } 41 | 42 | // createTask returns an example task that sleeps for the specified 43 | // number of seconds based on the id. 44 | func createTask() func(int) { 45 | return func(id int) { 46 | log.Printf("Processor - Task #%d.", id) 47 | time.Sleep(time.Duration(id) * time.Second) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /chapter7/patterns/runner/runner.go: -------------------------------------------------------------------------------- 1 | // Example is provided with help by Gabriel Aszalos. 2 | // Package runner manages the running and lifetime of a process. 3 | package runner 4 | 5 | import ( 6 | "errors" 7 | "os" 8 | "os/signal" 9 | "time" 10 | ) 11 | 12 | // Runner runs a set of tasks within a given timeout and can be 13 | // shut down on an operating system interrupt. 14 | type Runner struct { 15 | // interrupt channel reports a signal from the 16 | // operating system. 17 | interrupt chan os.Signal 18 | 19 | // complete channel reports that processing is done. 20 | complete chan error 21 | 22 | // timeout reports that time has run out. 23 | timeout <-chan time.Time 24 | 25 | // tasks holds a set of functions that are executed 26 | // synchronously in index order. 27 | tasks []func(int) 28 | } 29 | 30 | // ErrTimeout is returned when a value is received on the timeout channel. 31 | var ErrTimeout = errors.New("received timeout") 32 | 33 | // ErrInterrupt is returned when an event from the OS is received. 34 | var ErrInterrupt = errors.New("received interrupt") 35 | 36 | // New returns a new ready-to-use Runner. 37 | func New(d time.Duration) *Runner { 38 | return &Runner{ 39 | interrupt: make(chan os.Signal, 1), 40 | complete: make(chan error), 41 | timeout: time.After(d), 42 | } 43 | } 44 | 45 | // Add attaches tasks to the Runner. A task is a function that 46 | // takes an int ID. 47 | func (r *Runner) Add(tasks ...func(int)) { 48 | r.tasks = append(r.tasks, tasks...) 49 | } 50 | 51 | // Start runs all tasks and monitors channel events. 52 | func (r *Runner) Start() error { 53 | // We want to receive all interrupt based signals. 54 | signal.Notify(r.interrupt, os.Interrupt) 55 | 56 | // Run the different tasks on a different goroutine. 57 | go func() { 58 | r.complete <- r.run() 59 | }() 60 | 61 | select { 62 | // Signaled when processing is done. 63 | case err := <-r.complete: 64 | return err 65 | 66 | // Signaled when we run out of time. 67 | case <-r.timeout: 68 | return ErrTimeout 69 | } 70 | } 71 | 72 | // run executes each registered task. 73 | func (r *Runner) run() error { 74 | for id, task := range r.tasks { 75 | // Check for an interrupt signal from the OS. 76 | if r.gotInterrupt() { 77 | return ErrInterrupt 78 | } 79 | 80 | // Execute the registered task. 81 | task(id) 82 | } 83 | 84 | return nil 85 | } 86 | 87 | // gotInterrupt verifies if the interrupt signal has been issued. 88 | func (r *Runner) gotInterrupt() bool { 89 | select { 90 | // Signaled when an interrupt event is sent. 91 | case <-r.interrupt: 92 | // Stop receiving any further signals. 93 | signal.Stop(r.interrupt) 94 | return true 95 | 96 | // Continue running as normal. 97 | default: 98 | return false 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /chapter7/patterns/search/main/main.go: -------------------------------------------------------------------------------- 1 | // This sample program demonstrates how to implement a pattern for 2 | // concurrent requesting results from different systems and either 3 | // wait for all the results to return or just the first one. 4 | package main 5 | 6 | import ( 7 | "log" 8 | 9 | "github.com/goinaction/code/chapter7/patterns/search" 10 | ) 11 | 12 | // main is the entry point for all Go programs. 13 | func main() { 14 | // Submit the search and display the results. 15 | results := search.Submit( 16 | "golang", 17 | search.OnlyFirst, 18 | search.Google, 19 | search.Bing, 20 | search.Yahoo, 21 | ) 22 | 23 | for _, result := range results { 24 | log.Printf("main : Results : Info : %+v\n", result) 25 | } 26 | 27 | // This time we want to wait for all the results. 28 | results = search.Submit( 29 | "golang", 30 | search.Google, 31 | search.Bing, 32 | search.Yahoo, 33 | ) 34 | 35 | for _, result := range results { 36 | log.Printf("main : Results : Info : %+v\n", result) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /chapter7/patterns/search/search.go: -------------------------------------------------------------------------------- 1 | // Package search : search.go manages the searching of results 2 | // against Google, Yahoo and Bing. 3 | package search 4 | 5 | import "log" 6 | 7 | // Result represents a search result that was found. 8 | type Result struct { 9 | Engine string 10 | Title string 11 | Description string 12 | Link string 13 | } 14 | 15 | // Searcher declares an interface used to leverage different 16 | // search engines to find results. 17 | type Searcher interface { 18 | Search(searchTerm string, searchResults chan<- []Result) 19 | } 20 | 21 | // searchSession holds information about the current search submission. 22 | // It contains options, searchers and a channel down which we will receive 23 | // results. 24 | type searchSession struct { 25 | searchers map[string]Searcher 26 | first bool 27 | resultChan chan []Result 28 | } 29 | 30 | // Google search will be added to the search session if this option 31 | // is provided. 32 | func Google(s *searchSession) { 33 | log.Println("search : Submit : Info : Adding Google") 34 | s.searchers["google"] = google{} 35 | } 36 | 37 | // Bing search will be added to this search session if this option 38 | // is provided. 39 | func Bing(s *searchSession) { 40 | log.Println("search : Submit : Info : Adding Bing") 41 | s.searchers["bing"] = bing{} 42 | } 43 | 44 | // Yahoo search will be enabled if this option is provided as an argument 45 | // to Submit. 46 | func Yahoo(s *searchSession) { 47 | log.Println("search : Submit : Info : Adding Yahoo") 48 | s.searchers["yahoo"] = yahoo{} 49 | } 50 | 51 | // OnlyFirst is an option that will restrict the search session to just the 52 | // first result. 53 | func OnlyFirst(s *searchSession) { s.first = true } 54 | 55 | // Submit uses goroutines and channels to perform a search against the three 56 | // leading search engines concurrently. 57 | func Submit(query string, options ...func(*searchSession)) []Result { 58 | var session searchSession 59 | session.searchers = make(map[string]Searcher) 60 | session.resultChan = make(chan []Result) 61 | 62 | for _, opt := range options { 63 | opt(&session) 64 | } 65 | 66 | // Perform the searches concurrently. Using a map because 67 | // it returns the searchers in a random order every time. 68 | for _, s := range session.searchers { 69 | go s.Search(query, session.resultChan) 70 | } 71 | 72 | var results []Result 73 | 74 | // Wait for the results to come back. 75 | for search := 0; search < len(session.searchers); search++ { 76 | // If we just want the first result, don't wait any longer by 77 | // concurrently discarding the remaining searchResults. 78 | // Failing to do so will leave the Searchers blocked forever. 79 | if session.first && search > 0 { 80 | go func() { 81 | r := <-session.resultChan 82 | log.Printf("search : Submit : Info : Results Discarded : Results[%d]\n", len(r)) 83 | }() 84 | continue 85 | } 86 | 87 | // Wait to recieve results. 88 | log.Println("search : Submit : Info : Waiting For Results...") 89 | result := <-session.resultChan 90 | 91 | // Save the results to the final slice. 92 | log.Printf("search : Submit : Info : Results Used : Results[%d]\n", len(result)) 93 | results = append(results, result...) 94 | } 95 | 96 | log.Printf("search : Submit : Completed : Found [%d] Results\n", len(results)) 97 | return results 98 | } 99 | -------------------------------------------------------------------------------- /chapter7/patterns/search/searchers.go: -------------------------------------------------------------------------------- 1 | // Package search : seachers.go contains all the different implementations 2 | // for the existing searchers. 3 | package search 4 | 5 | import ( 6 | "log" 7 | "math/rand" 8 | "time" 9 | ) 10 | 11 | // init is called before main. 12 | func init() { 13 | rand.Seed(time.Now().UnixNano()) 14 | } 15 | 16 | // Google provides support for Google searches. 17 | type google struct{} 18 | 19 | // Search implements the Searcher interface. It performs a search 20 | // against Google. 21 | func (g google) Search(term string, results chan<- []Result) { 22 | log.Printf("Google : Search : Started : search term[%s]\n", term) 23 | 24 | // Slice for the results. 25 | var r []Result 26 | 27 | // Simulate an amount of time for the search. 28 | time.Sleep(time.Millisecond * time.Duration(rand.Int63n(900))) 29 | 30 | // Simulate a result for the search. 31 | r = append(r, Result{ 32 | Engine: "Google", 33 | Title: "The Go Programming Language", 34 | Description: "The Go Programming Language", 35 | Link: "https://golang.org/", 36 | }) 37 | 38 | log.Printf("Google : Search : Completed : Found[%d]\n", len(r)) 39 | results <- r 40 | } 41 | 42 | // Bing provides support for Bing searches. 43 | type bing struct{} 44 | 45 | // Search implements the Searcher interface. It performs a search 46 | // against Bing. 47 | func (b bing) Search(term string, results chan<- []Result) { 48 | log.Printf("Bing : Search : Started : search term [%s]\n", term) 49 | 50 | // Slice for the results. 51 | var r []Result 52 | 53 | // Simulate an amount of time for the search. 54 | time.Sleep(time.Millisecond * time.Duration(rand.Int63n(900))) 55 | 56 | // Simulate a result for the search. 57 | r = append(r, Result{ 58 | Engine: "Bing", 59 | Title: "A Tour of Go", 60 | Description: "Welcome to a tour of the Go programming language.", 61 | Link: "http://tour.golang.org/", 62 | }) 63 | 64 | log.Printf("Bing : Search : Completed : Found[%d]\n", len(r)) 65 | results <- r 66 | } 67 | 68 | // Yahoo provides support for Yahoo searches. 69 | type yahoo struct{} 70 | 71 | // Search implements the Searcher interface. It performs a search 72 | // against Yahoo. 73 | func (y yahoo) Search(term string, results chan<- []Result) { 74 | log.Printf("Yahoo : Search : Started : search term [%s]\n", term) 75 | 76 | // Slice for the results. 77 | var r []Result 78 | 79 | // Simulate an amount of time for the search. 80 | time.Sleep(time.Millisecond * time.Duration(rand.Int63n(900))) 81 | 82 | // Simulate a result for the search. 83 | r = append(r, Result{ 84 | Engine: "Yahoo", 85 | Title: "Go Playground", 86 | Description: "The Go Playground is a web service that runs on golang.org's servers", 87 | Link: "http://play.golang.org/", 88 | }) 89 | 90 | log.Printf("Yahoo : Search : Completed : Found[%d]\n", len(r)) 91 | results <- r 92 | } 93 | -------------------------------------------------------------------------------- /chapter7/patterns/semaphore/semaphore.go: -------------------------------------------------------------------------------- 1 | // This sample program demonstrates how to implement a semaphore using 2 | // channels that can allow multiple reads but a single write. 3 | // 4 | // It uses the generator pattern to create channels and goroutines. 5 | // 6 | // Multiple reader/writers can be created and run concurrently. Then after 7 | // a timeout period, the program shutdowns cleanly. 8 | // 9 | // http://www.golangpatterns.info/concurrency/semaphores 10 | package main 11 | 12 | import ( 13 | "log" 14 | "math/rand" 15 | "sync" 16 | "sync/atomic" 17 | "time" 18 | ) 19 | 20 | type ( 21 | // semaphore type represents a channel that implements the semaphore pattern. 22 | semaphore chan struct{} 23 | ) 24 | 25 | type ( 26 | // readerWriter provides a structure for safely reading and writing a shared resource. 27 | // It supports multiple readers and a single writer goroutine using a semaphore construct. 28 | readerWriter struct { 29 | // The name of this object. 30 | name string 31 | 32 | // write forces reading to stop to allow the write to occur safely. 33 | write sync.WaitGroup 34 | 35 | // readerControl is a semaphore that allows a fixed number of reader goroutines 36 | // to read at the same time. This is our semaphore. 37 | readerControl semaphore 38 | 39 | // shutdown is used to signal to running goroutines to shutdown. 40 | shutdown chan struct{} 41 | 42 | // reportShutdown is used by the goroutines to report they are shutdown. 43 | reportShutdown sync.WaitGroup 44 | 45 | // maxReads defined the maximum number of reads that can occur at a time. 46 | maxReads int 47 | 48 | // maxReaders defines the number of goroutines launched to perform read operations. 49 | maxReaders int 50 | 51 | // currentReads keeps a safe count of the current number of reads occurring 52 | // at any give time. 53 | currentReads int32 54 | } 55 | ) 56 | 57 | // init is called when the package is initialized. This code runs first. 58 | func init() { 59 | // Seed the random number generator 60 | rand.Seed(time.Now().Unix()) 61 | } 62 | 63 | // main is the entry point for the application 64 | func main() { 65 | log.Println("Starting Process") 66 | 67 | // Create a new readerWriter with a max of 3 reads at a time 68 | // and a total of 6 reader goroutines. 69 | first := start("First", 3, 6) 70 | 71 | // Create a new readerWriter with a max of 1 reads at a time 72 | // and a total of 1 reader goroutines. 73 | second := start("Second", 2, 2) 74 | 75 | // Let the program run for 2 seconds. 76 | time.Sleep(2 * time.Second) 77 | 78 | // Shutdown both of the readerWriter processes. 79 | shutdown(first, second) 80 | 81 | log.Println("Process Ended") 82 | return 83 | } 84 | 85 | // start uses the generator pattern to create the readerWriter value. It launches 86 | // goroutines to process the work, returning the created ReaderWriter value. 87 | func start(name string, maxReads int, maxReaders int) *readerWriter { 88 | // Create a value of readerWriter and initialize. 89 | rw := readerWriter{ 90 | name: name, 91 | shutdown: make(chan struct{}), 92 | maxReads: maxReads, 93 | maxReaders: maxReaders, 94 | readerControl: make(semaphore, maxReads), 95 | } 96 | 97 | // Launch a number of reader goroutines and let them start reading. 98 | rw.reportShutdown.Add(maxReaders) 99 | for goroutine := 0; goroutine < maxReaders; goroutine++ { 100 | go rw.reader(goroutine) 101 | } 102 | 103 | // Launch the single writer goroutine and let it start writing. 104 | rw.reportShutdown.Add(1) 105 | go rw.writer() 106 | 107 | return &rw 108 | } 109 | 110 | // shutdown stops all of the existing readerWriter processes concurrently. 111 | func shutdown(readerWriters ...*readerWriter) { 112 | // Create a WaitGroup to track the shutdowns. 113 | var waitShutdown sync.WaitGroup 114 | waitShutdown.Add(len(readerWriters)) 115 | 116 | // Launch each call to the stop method as a goroutine. 117 | for _, readerWriter := range readerWriters { 118 | go readerWriter.stop(&waitShutdown) 119 | } 120 | 121 | // Wait for all the goroutines to report they are done. 122 | waitShutdown.Wait() 123 | } 124 | 125 | // stop signals to all goroutines to shutdown and reports back 126 | // when that is complete 127 | func (rw *readerWriter) stop(waitShutdown *sync.WaitGroup) { 128 | // Schedule the call to Done for once the method returns. 129 | defer waitShutdown.Done() 130 | 131 | log.Printf("%s\t: #####> Stop", rw.name) 132 | 133 | // Close the channel which will causes all the goroutines waiting on 134 | // this channel to receive the notification to shutdown. 135 | close(rw.shutdown) 136 | 137 | // Wait for all the goroutine to call Done on the waitgroup we 138 | // are waiting on. 139 | rw.reportShutdown.Wait() 140 | 141 | log.Printf("%s\t: #####> Stopped", rw.name) 142 | } 143 | 144 | // reader is a goroutine that listens on the shutdown channel and 145 | // performs reads until the channel is signaled. 146 | func (rw *readerWriter) reader(reader int) { 147 | // Schedule the call to Done for once the method returns. 148 | defer rw.reportShutdown.Done() 149 | 150 | for { 151 | select { 152 | case <-rw.shutdown: 153 | log.Printf("%s\t: #> Reader Shutdown", rw.name) 154 | return 155 | 156 | default: 157 | rw.performRead(reader) 158 | } 159 | } 160 | } 161 | 162 | // performRead performs the actual reading work. 163 | func (rw *readerWriter) performRead(reader int) { 164 | // Get a read lock for this critical section. 165 | rw.ReadLock(reader) 166 | 167 | // Safely increment the current reads counter 168 | count := atomic.AddInt32(&rw.currentReads, 1) 169 | 170 | // Simulate some reading work 171 | log.Printf("%s\t: [%d] Start\t- [%d] Reads\n", rw.name, reader, count) 172 | time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond) 173 | 174 | // Safely decrement the current reads counter 175 | count = atomic.AddInt32(&rw.currentReads, -1) 176 | log.Printf("%s\t: [%d] Finish\t- [%d] Reads\n", rw.name, reader, count) 177 | 178 | // Release the read lock for this critical section. 179 | rw.ReadUnlock(reader) 180 | } 181 | 182 | // writer is a goroutine that listens on the shutdown channel and 183 | // performs writes until the channel is signaled. 184 | func (rw *readerWriter) writer() { 185 | // Schedule the call to Done for once the method returns. 186 | defer rw.reportShutdown.Done() 187 | 188 | for { 189 | select { 190 | case <-rw.shutdown: 191 | log.Printf("%s\t: #> Writer Shutdown", rw.name) 192 | return 193 | default: 194 | rw.performWrite() 195 | } 196 | } 197 | } 198 | 199 | // performWrite performs the actual write work. 200 | func (rw *readerWriter) performWrite() { 201 | // Wait a random number of milliseconds before we write again. 202 | time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond) 203 | 204 | log.Printf("%s\t: *****> Writing Pending\n", rw.name) 205 | 206 | // Get a write lock for this critical section. 207 | rw.WriteLock() 208 | 209 | // Simulate some writing work. 210 | log.Printf("%s\t: *****> Writing Start", rw.name) 211 | time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond) 212 | log.Printf("%s\t: *****> Writing Finish", rw.name) 213 | 214 | // Release the write lock for this critical section. 215 | rw.WriteUnlock() 216 | } 217 | 218 | // ReadLock guarantees only the maximum number of goroutines can read at a time. 219 | func (rw *readerWriter) ReadLock(reader int) { 220 | // If a write is occurring, wait for it to complete. 221 | rw.write.Wait() 222 | 223 | // Acquire a buffer from the semaphore channel. 224 | rw.readerControl.Acquire(1) 225 | } 226 | 227 | // ReadUnlock gives other goroutines waiting to read their opporunity. 228 | func (rw *readerWriter) ReadUnlock(reader int) { 229 | // Release the buffer back into the semaphore channel. 230 | rw.readerControl.Release(1) 231 | } 232 | 233 | // WriteLock blocks all reading so the write can happen safely. 234 | func (rw *readerWriter) WriteLock() { 235 | // Add 1 to the waitGroup so reads will block 236 | rw.write.Add(1) 237 | 238 | // Acquire all the buffers from the semaphore channel. 239 | rw.readerControl.Acquire(rw.maxReads) 240 | } 241 | 242 | // WriteUnlock releases the write lock and allows reads to occur. 243 | func (rw *readerWriter) WriteUnlock() { 244 | // Release all the buffers back into the semaphore channel. 245 | rw.readerControl.Release(rw.maxReads) 246 | 247 | // Release the write lock. 248 | rw.write.Done() 249 | } 250 | 251 | // Acquire attempts to secure the specified number of buffers from the 252 | // semaphore channel. 253 | func (s semaphore) Acquire(buffers int) { 254 | var e struct{} 255 | 256 | // Write data to secure each buffer. 257 | for buffer := 0; buffer < buffers; buffer++ { 258 | s <- e 259 | } 260 | } 261 | 262 | // Release returns the specified number of buffers back into the semaphore channel. 263 | func (s semaphore) Release(buffers int) { 264 | // Read the data from the channel to release buffers. 265 | for buffer := 0; buffer < buffers; buffer++ { 266 | <-s 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /chapter7/patterns/work/main/main.go: -------------------------------------------------------------------------------- 1 | // This sample program demonstrates how to use the work package 2 | // to use a pool of goroutines to get work done. 3 | package main 4 | 5 | import ( 6 | "log" 7 | "sync" 8 | "time" 9 | 10 | "github.com/goinaction/code/chapter7/patterns/work" 11 | ) 12 | 13 | // names provides a set of names to display. 14 | var names = []string{ 15 | "steve", 16 | "bob", 17 | "mary", 18 | "therese", 19 | "jason", 20 | } 21 | 22 | // namePrinter provides special support for printing names. 23 | type namePrinter struct { 24 | name string 25 | } 26 | 27 | // Task implements the Worker interface. 28 | func (m *namePrinter) Task() { 29 | log.Println(m.name) 30 | time.Sleep(time.Second) 31 | } 32 | 33 | // main is the entry point for all Go programs. 34 | func main() { 35 | // Create a work pool with 2 goroutines. 36 | p := work.New(2) 37 | 38 | var wg sync.WaitGroup 39 | wg.Add(100 * len(names)) 40 | 41 | for i := 0; i < 100; i++ { 42 | // Iterate over the slice of names. 43 | for _, name := range names { 44 | // Create a namePrinter and provide the 45 | // specific name. 46 | np := namePrinter{ 47 | name: name, 48 | } 49 | 50 | go func() { 51 | // Submit the task to be worked on. When RunTask 52 | // returns we know it is being handled. 53 | p.Run(&np) 54 | wg.Done() 55 | }() 56 | } 57 | } 58 | 59 | wg.Wait() 60 | 61 | // Shutdown the work pool and wait for all existing work 62 | // to be completed. 63 | p.Shutdown() 64 | } 65 | -------------------------------------------------------------------------------- /chapter7/patterns/work/work.go: -------------------------------------------------------------------------------- 1 | // Example provided with help from Jason Waldrip. 2 | // Package work manages a pool of goroutines to perform work. 3 | package work 4 | 5 | import "sync" 6 | 7 | // Worker must be implemented by types that want to use 8 | // the work pool. 9 | type Worker interface { 10 | Task() 11 | } 12 | 13 | // Pool provides a pool of goroutines that can execute any Worker 14 | // tasks that are submitted. 15 | type Pool struct { 16 | work chan Worker 17 | wg sync.WaitGroup 18 | } 19 | 20 | // New creates a new work pool. 21 | func New(maxGoroutines int) *Pool { 22 | p := Pool{ 23 | work: make(chan Worker), 24 | } 25 | 26 | p.wg.Add(maxGoroutines) 27 | for i := 0; i < maxGoroutines; i++ { 28 | go func() { 29 | for w := range p.work { 30 | w.Task() 31 | } 32 | p.wg.Done() 33 | }() 34 | } 35 | 36 | return &p 37 | } 38 | 39 | // Run submits work to the pool. 40 | func (p *Pool) Run(w Worker) { 41 | p.work <- w 42 | } 43 | 44 | // Shutdown waits for all the goroutines to shutdown. 45 | func (p *Pool) Shutdown() { 46 | close(p.work) 47 | p.wg.Wait() 48 | } 49 | -------------------------------------------------------------------------------- /chapter8/listing03/listing03.go: -------------------------------------------------------------------------------- 1 | // This sample program demonstrates how to use the base log package. 2 | package main 3 | 4 | import ( 5 | "log" 6 | ) 7 | 8 | func init() { 9 | log.SetPrefix("TRACE: ") 10 | log.SetFlags(log.Ldate | log.Lmicroseconds | log.Llongfile) 11 | } 12 | 13 | func main() { 14 | // Println writes to the standard logger. 15 | log.Println("message") 16 | 17 | // Fatalln is Println() followed by a call to os.Exit(1). 18 | log.Fatalln("fatal message") 19 | 20 | // Panicln is Println() followed by a call to panic(). 21 | log.Panicln("panic message") 22 | } 23 | -------------------------------------------------------------------------------- /chapter8/listing11/listing11.go: -------------------------------------------------------------------------------- 1 | // This sample program demonstrates how to create customized loggers. 2 | package main 3 | 4 | import ( 5 | "io" 6 | "io/ioutil" 7 | "log" 8 | "os" 9 | ) 10 | 11 | var ( 12 | Trace *log.Logger // Just about anything 13 | Info *log.Logger // Important information 14 | Warning *log.Logger // Be concerned 15 | Error *log.Logger // Critical problem 16 | ) 17 | 18 | func init() { 19 | file, err := os.OpenFile("errors.txt", 20 | os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) 21 | if err != nil { 22 | log.Fatalln("Failed to open error log file:", err) 23 | } 24 | 25 | Trace = log.New(ioutil.Discard, 26 | "TRACE: ", 27 | log.Ldate|log.Ltime|log.Lshortfile) 28 | 29 | Info = log.New(os.Stdout, 30 | "INFO: ", 31 | log.Ldate|log.Ltime|log.Lshortfile) 32 | 33 | Warning = log.New(os.Stdout, 34 | "WARNING: ", 35 | log.Ldate|log.Ltime|log.Lshortfile) 36 | 37 | Error = log.New(io.MultiWriter(file, os.Stderr), 38 | "ERROR: ", 39 | log.Ldate|log.Ltime|log.Lshortfile) 40 | } 41 | 42 | func main() { 43 | Trace.Println("I have something standard to say") 44 | Info.Println("Special Information") 45 | Warning.Println("There is something you need to know about") 46 | Error.Println("Something has failed") 47 | } 48 | -------------------------------------------------------------------------------- /chapter8/listing24/listing24.go: -------------------------------------------------------------------------------- 1 | // This sample program demonstrates how to decode a JSON response 2 | // using the json package and NewDecoder function. 3 | package main 4 | 5 | import ( 6 | "encoding/json" 7 | "fmt" 8 | "log" 9 | "net/http" 10 | ) 11 | 12 | type ( 13 | // gResult maps to the result document received from the search. 14 | gResult struct { 15 | GsearchResultClass string `json:"GsearchResultClass"` 16 | UnescapedURL string `json:"unescapedUrl"` 17 | URL string `json:"url"` 18 | VisibleURL string `json:"visibleUrl"` 19 | CacheURL string `json:"cacheUrl"` 20 | Title string `json:"title"` 21 | TitleNoFormatting string `json:"titleNoFormatting"` 22 | Content string `json:"content"` 23 | } 24 | 25 | // gResponse contains the top level document. 26 | gResponse struct { 27 | ResponseData struct { 28 | Results []gResult `json:"results"` 29 | } `json:"responseData"` 30 | } 31 | ) 32 | 33 | func main() { 34 | uri := "http://ajax.googleapis.com/ajax/services/search/web?v=1.0&rsz=8&q=golang" 35 | 36 | // Issue the search against Google. 37 | resp, err := http.Get(uri) 38 | if err != nil { 39 | log.Println("ERROR:", err) 40 | return 41 | } 42 | defer resp.Body.Close() 43 | 44 | // Decode the JSON response into our struct type. 45 | var gr gResponse 46 | err = json.NewDecoder(resp.Body).Decode(&gr) 47 | if err != nil { 48 | log.Println("ERROR:", err) 49 | return 50 | } 51 | 52 | fmt.Println(gr) 53 | 54 | // Marshal the struct type into a pretty print 55 | // version of the JSON document. 56 | pretty, err := json.MarshalIndent(gr, "", " ") 57 | if err != nil { 58 | log.Println("ERROR:", err) 59 | return 60 | } 61 | 62 | fmt.Println(string(pretty)) 63 | } 64 | -------------------------------------------------------------------------------- /chapter8/listing27/listing27.go: -------------------------------------------------------------------------------- 1 | // This sample program demonstrates how to decode a JSON string. 2 | package main 3 | 4 | import ( 5 | "encoding/json" 6 | "fmt" 7 | "log" 8 | ) 9 | 10 | // Contact represents our JSON string. 11 | type Contact struct { 12 | Name string `json:"name"` 13 | Title string `json:"title"` 14 | Contact struct { 15 | Home string `json:"home"` 16 | Cell string `json:"cell"` 17 | } `json:"contact"` 18 | } 19 | 20 | // JSON contains a sample string to unmarshal. 21 | var JSON = `{ 22 | "name": "Gopher", 23 | "title": "programmer", 24 | "contact": { 25 | "home": "415.333.3333", 26 | "cell": "415.555.5555" 27 | } 28 | }` 29 | 30 | func main() { 31 | // Unmarshal the JSON string into our variable. 32 | var c Contact 33 | err := json.Unmarshal([]byte(JSON), &c) 34 | if err != nil { 35 | log.Println("ERROR:", err) 36 | return 37 | } 38 | 39 | fmt.Println(c) 40 | } 41 | -------------------------------------------------------------------------------- /chapter8/listing29/listing29.go: -------------------------------------------------------------------------------- 1 | // This sample program demonstrates how to decode a JSON string. 2 | package main 3 | 4 | import ( 5 | "encoding/json" 6 | "fmt" 7 | "log" 8 | ) 9 | 10 | // JSON contains a sample string to unmarshal. 11 | var JSON = `{ 12 | "name": "Gopher", 13 | "title": "programmer", 14 | "contact": { 15 | "home": "415.333.3333", 16 | "cell": "415.555.5555" 17 | } 18 | }` 19 | 20 | func main() { 21 | // Unmarshal the JSON string into our map variable. 22 | var c map[string]interface{} 23 | err := json.Unmarshal([]byte(JSON), &c) 24 | if err != nil { 25 | log.Println("ERROR:", err) 26 | return 27 | } 28 | 29 | fmt.Println("Name:", c["name"]) 30 | fmt.Println("Title:", c["title"]) 31 | fmt.Println("Contact") 32 | fmt.Println("H:", c["contact"].(map[string]interface{})["home"]) 33 | fmt.Println("C:", c["contact"].(map[string]interface{})["cell"]) 34 | } 35 | -------------------------------------------------------------------------------- /chapter8/listing31/listing31.go: -------------------------------------------------------------------------------- 1 | // This sample program demonstrates how to marshal a JSON string. 2 | package main 3 | 4 | import ( 5 | "encoding/json" 6 | "fmt" 7 | "log" 8 | ) 9 | 10 | func main() { 11 | // Create a map of key/value pairs. 12 | c := make(map[string]interface{}) 13 | c["name"] = "Gopher" 14 | c["title"] = "programmer" 15 | c["contact"] = map[string]interface{}{ 16 | "home": "415.333.3333", 17 | "cell": "415.555.5555", 18 | } 19 | 20 | // Marshal the map into a JSON string. 21 | data, err := json.MarshalIndent(c, "", " ") 22 | if err != nil { 23 | log.Println("ERROR:", err) 24 | return 25 | } 26 | 27 | fmt.Println(string(data)) 28 | } 29 | -------------------------------------------------------------------------------- /chapter8/listing37/listing37.go: -------------------------------------------------------------------------------- 1 | // Sample program to show how different functions from the 2 | // standard library use the io.Writer interface. 3 | package main 4 | 5 | import ( 6 | "bytes" 7 | "fmt" 8 | "os" 9 | ) 10 | 11 | // main is the entry point for the application. 12 | func main() { 13 | // Create a Buffer value and write a string to the buffer. 14 | // Using the Write method that implements io.Writer. 15 | var b bytes.Buffer 16 | b.Write([]byte("Hello ")) 17 | 18 | // Use Fprintf to concatenate a string to the Buffer. 19 | // Passing the address of a bytes.Buffer value for io.Writer. 20 | fmt.Fprintf(&b, "World!") 21 | 22 | // Write the content of the Buffer to the stdout device. 23 | // Passing the address of a os.File value for io.Writer. 24 | b.WriteTo(os.Stdout) 25 | } 26 | -------------------------------------------------------------------------------- /chapter8/listing46/listing46.go: -------------------------------------------------------------------------------- 1 | // Sample program to show how to write a simple version of curl using 2 | // the io.Reader and io.Writer interface support. 3 | package main 4 | 5 | import ( 6 | "io" 7 | "log" 8 | "net/http" 9 | "os" 10 | ) 11 | 12 | // main is the entry point for the application. 13 | func main() { 14 | // r here is a response, and r.Body is an io.Reader. 15 | r, err := http.Get(os.Args[1]) 16 | if err != nil { 17 | log.Fatalln(err) 18 | } 19 | 20 | // Create a file to persist the response. 21 | file, err := os.Create(os.Args[2]) 22 | if err != nil { 23 | log.Fatalln(err) 24 | } 25 | defer file.Close() 26 | 27 | // Use MultiWriter so we can write to stdout and 28 | // a file on the same write operation. 29 | dest := io.MultiWriter(os.Stdout, file) 30 | 31 | // Read the response and write to both destinations. 32 | io.Copy(dest, r.Body) 33 | if err := r.Body.Close(); err != nil { 34 | log.Println(err) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /chapter9/listing01/listing01_test.go: -------------------------------------------------------------------------------- 1 | // Sample test to show how to write a basic unit test. 2 | package listing01 3 | 4 | import ( 5 | "net/http" 6 | "testing" 7 | ) 8 | 9 | const checkMark = "\u2713" 10 | const ballotX = "\u2717" 11 | 12 | // TestDownload validates the http Get function can download content. 13 | func TestDownload(t *testing.T) { 14 | url := "http://www.goinggo.net/feeds/posts/default?alt=rss" 15 | statusCode := 200 16 | 17 | t.Log("Given the need to test downloading content.") 18 | { 19 | t.Logf("\tWhen checking \"%s\" for status code \"%d\"", 20 | url, statusCode) 21 | { 22 | resp, err := http.Get(url) 23 | if err != nil { 24 | t.Fatal("\t\tShould be able to make the Get call.", 25 | ballotX, err) 26 | } 27 | t.Log("\t\tShould be able to make the Get call.", 28 | checkMark) 29 | 30 | defer resp.Body.Close() 31 | 32 | if resp.StatusCode == statusCode { 33 | t.Logf("\t\tShould receive a \"%d\" status. %v", 34 | statusCode, checkMark) 35 | } else { 36 | t.Errorf("\t\tShould receive a \"%d\" status. %v %v", 37 | statusCode, ballotX, resp.StatusCode) 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /chapter9/listing08/listing08_test.go: -------------------------------------------------------------------------------- 1 | // Sample test to show how to write a basic unit table test. 2 | package listing08 3 | 4 | import ( 5 | "net/http" 6 | "testing" 7 | ) 8 | 9 | const checkMark = "\u2713" 10 | const ballotX = "\u2717" 11 | 12 | // TestDownload validates the http Get function can download 13 | // content and handles different status conditions properly. 14 | func TestDownload(t *testing.T) { 15 | var urls = []struct { 16 | url string 17 | statusCode int 18 | }{ 19 | { 20 | "http://www.goinggo.net/feeds/posts/default?alt=rss", 21 | http.StatusOK, 22 | }, 23 | { 24 | "http://rss.cnn.com/rss/cnn_topstbadurl.rss", 25 | http.StatusNotFound, 26 | }, 27 | } 28 | 29 | t.Log("Given the need to test downloading different content.") 30 | { 31 | for _, u := range urls { 32 | t.Logf("\tWhen checking \"%s\" for status code \"%d\"", 33 | u.url, u.statusCode) 34 | { 35 | resp, err := http.Get(u.url) 36 | if err != nil { 37 | t.Fatal("\t\tShould be able to Get the url.", 38 | ballotX, err) 39 | } 40 | t.Log("\t\tShould be able to Get the url.", 41 | checkMark) 42 | 43 | defer resp.Body.Close() 44 | 45 | if resp.StatusCode == u.statusCode { 46 | t.Logf("\t\tShould have a \"%d\" status. %v", 47 | u.statusCode, checkMark) 48 | } else { 49 | t.Errorf("\t\tShould have a \"%d\" status. %v %v", 50 | u.statusCode, ballotX, resp.StatusCode) 51 | } 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /chapter9/listing12/listing12_test.go: -------------------------------------------------------------------------------- 1 | // Sample test to show how to mock an HTTP GET call internally. 2 | // Differs slightly from the book to show more. 3 | package listing12 4 | 5 | import ( 6 | "encoding/xml" 7 | "fmt" 8 | "net/http" 9 | "net/http/httptest" 10 | "testing" 11 | ) 12 | 13 | const checkMark = "\u2713" 14 | const ballotX = "\u2717" 15 | 16 | // feed is mocking the XML document we except to receive. 17 | var feed = ` 18 | 19 | 20 | Going Go Programming 21 | Golang : https://github.com/goinggo 22 | http://www.goinggo.net/ 23 | 24 | Sun, 15 Mar 2015 15:04:00 +0000 25 | Object Oriented Programming Mechanics 26 | Go is an object oriented language. 27 | http://www.goinggo.net/2015/03/object-oriented 28 | 29 | 30 | ` 31 | 32 | // mockServer returns a pointer to a server to handle the get call. 33 | func mockServer() *httptest.Server { 34 | f := func(w http.ResponseWriter, r *http.Request) { 35 | w.WriteHeader(200) 36 | w.Header().Set("Content-Type", "application/xml") 37 | fmt.Fprintln(w, feed) 38 | } 39 | 40 | return httptest.NewServer(http.HandlerFunc(f)) 41 | } 42 | 43 | // TestDownload validates the http Get function can download content 44 | // and the content can be unmarshaled and clean. 45 | func TestDownload(t *testing.T) { 46 | statusCode := http.StatusOK 47 | 48 | server := mockServer() 49 | defer server.Close() 50 | 51 | t.Log("Given the need to test downloading content.") 52 | { 53 | t.Logf("\tWhen checking \"%s\" for status code \"%d\"", 54 | server.URL, statusCode) 55 | { 56 | resp, err := http.Get(server.URL) 57 | if err != nil { 58 | t.Fatal("\t\tShould be able to make the Get call.", 59 | ballotX, err) 60 | } 61 | t.Log("\t\tShould be able to make the Get call.", 62 | checkMark) 63 | 64 | defer resp.Body.Close() 65 | 66 | if resp.StatusCode != statusCode { 67 | t.Fatalf("\t\tShould receive a \"%d\" status. %v %v", 68 | statusCode, ballotX, resp.StatusCode) 69 | } 70 | t.Logf("\t\tShould receive a \"%d\" status. %v", 71 | statusCode, checkMark) 72 | 73 | var d Document 74 | if err := xml.NewDecoder(resp.Body).Decode(&d); err != nil { 75 | t.Fatal("\t\tShould be able to unmarshal the response.", 76 | ballotX, err) 77 | } 78 | t.Log("\t\tShould be able to unmarshal the response.", 79 | checkMark) 80 | 81 | if len(d.Channel.Items) == 1 { 82 | t.Log("\t\tShould have \"1\" item in the feed.", 83 | checkMark) 84 | } else { 85 | t.Error("\t\tShould have \"1\" item in the feed.", 86 | ballotX, len(d.Channel.Items)) 87 | } 88 | } 89 | } 90 | } 91 | 92 | // Item defines the fields associated with the item tag in 93 | // the buoy RSS document. 94 | type Item struct { 95 | XMLName xml.Name `xml:"item"` 96 | Title string `xml:"title"` 97 | Description string `xml:"description"` 98 | Link string `xml:"link"` 99 | } 100 | 101 | // Channel defines the fields associated with the channel tag in 102 | // the buoy RSS document. 103 | type Channel struct { 104 | XMLName xml.Name `xml:"channel"` 105 | Title string `xml:"title"` 106 | Description string `xml:"description"` 107 | Link string `xml:"link"` 108 | PubDate string `xml:"pubDate"` 109 | Items []Item `xml:"item"` 110 | } 111 | 112 | // Document defines the fields associated with the buoy RSS document. 113 | type Document struct { 114 | XMLName xml.Name `xml:"rss"` 115 | Channel Channel `xml:"channel"` 116 | URI string 117 | } 118 | -------------------------------------------------------------------------------- /chapter9/listing17/handlers/handlers.go: -------------------------------------------------------------------------------- 1 | // Package handlers provides the endpoints for the web service. 2 | package handlers 3 | 4 | import ( 5 | "encoding/json" 6 | "net/http" 7 | ) 8 | 9 | // Routes sets the routes for the web service. 10 | func Routes() { 11 | http.HandleFunc("/sendjson", SendJSON) 12 | } 13 | 14 | // SendJSON returns a simple JSON document. 15 | func SendJSON(rw http.ResponseWriter, r *http.Request) { 16 | u := struct { 17 | Name string 18 | Email string 19 | }{ 20 | Name: "Bill", 21 | Email: "bill@ardanstudios.com", 22 | } 23 | 24 | rw.Header().Set("Content-Type", "application/json") 25 | rw.WriteHeader(200) 26 | json.NewEncoder(rw).Encode(&u) 27 | } 28 | -------------------------------------------------------------------------------- /chapter9/listing17/handlers/handlers_example_test.go: -------------------------------------------------------------------------------- 1 | // Sample test to show how to write a basic example. 2 | package handlers_test 3 | 4 | import ( 5 | "encoding/json" 6 | "fmt" 7 | "log" 8 | "net/http" 9 | "net/http/httptest" 10 | ) 11 | 12 | // ExampleSendJSON provides a basic example. 13 | func ExampleSendJSON() { 14 | r, _ := http.NewRequest("GET", "/sendjson", nil) 15 | w := httptest.NewRecorder() 16 | http.DefaultServeMux.ServeHTTP(w, r) 17 | 18 | var u struct { 19 | Name string 20 | Email string 21 | } 22 | 23 | if err := json.NewDecoder(w.Body).Decode(&u); err != nil { 24 | log.Println("ERROR:", err) 25 | } 26 | 27 | fmt.Println(u) 28 | // Output: 29 | // {Bill bill@ardanstudios.com} 30 | } 31 | -------------------------------------------------------------------------------- /chapter9/listing17/handlers/handlers_test.go: -------------------------------------------------------------------------------- 1 | // Sample test to show how to test the execution of an 2 | // internal endpoint. 3 | package handlers_test 4 | 5 | import ( 6 | "encoding/json" 7 | "net/http" 8 | "net/http/httptest" 9 | "testing" 10 | 11 | "github.com/goinaction/code/chapter9/listing17/handlers" 12 | ) 13 | 14 | const checkMark = "\u2713" 15 | const ballotX = "\u2717" 16 | 17 | func init() { 18 | handlers.Routes() 19 | } 20 | 21 | // TestSendJSON testing the sendjson internal endpoint. 22 | func TestSendJSON(t *testing.T) { 23 | t.Log("Given the need to test the SendJSON endpoint.") 24 | { 25 | req, err := http.NewRequest("GET", "/sendjson", nil) 26 | if err != nil { 27 | t.Fatal("\tShould be able to create a request.", 28 | ballotX, err) 29 | } 30 | t.Log("\tShould be able to create a request.", 31 | checkMark) 32 | 33 | rw := httptest.NewRecorder() 34 | http.DefaultServeMux.ServeHTTP(rw, req) 35 | 36 | if rw.Code != 200 { 37 | t.Fatal("\tShould receive \"200\"", ballotX, rw.Code) 38 | } 39 | t.Log("\tShould receive \"200\"", checkMark) 40 | 41 | u := struct { 42 | Name string 43 | Email string 44 | }{} 45 | 46 | if err := json.NewDecoder(rw.Body).Decode(&u); err != nil { 47 | t.Fatal("\tShould decode the response.", ballotX) 48 | } 49 | t.Log("\tShould decode the response.", checkMark) 50 | 51 | if u.Name == "Bill" { 52 | t.Log("\tShould have a Name.", checkMark) 53 | } else { 54 | t.Error("\tShould have a Name.", ballotX, u.Name) 55 | } 56 | 57 | if u.Email == "bill@ardanstudios.com" { 58 | t.Log("\tShould have an Email.", checkMark) 59 | } else { 60 | t.Error("\tShould have an for Email.", ballotX, u.Email) 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /chapter9/listing17/listing17.go: -------------------------------------------------------------------------------- 1 | // This sample code implement a simple web service. 2 | package main 3 | 4 | import ( 5 | "log" 6 | "net/http" 7 | 8 | "github.com/goinaction/code/chapter9/listing17/handlers" 9 | ) 10 | 11 | // main is the entry point for the application. 12 | func main() { 13 | handlers.Routes() 14 | 15 | log.Println("listener : Started : Listening on :4000") 16 | http.ListenAndServe(":4000", nil) 17 | } 18 | -------------------------------------------------------------------------------- /chapter9/listing28/listing28_test.go: -------------------------------------------------------------------------------- 1 | // Sample benchmarks to test which function is better for converting 2 | // an integer into a string. First using the fmt.Sprintf function, 3 | // then the strconv.FormatInt function and then strconv.Itoa. 4 | package listing05_test 5 | 6 | import ( 7 | "fmt" 8 | "strconv" 9 | "testing" 10 | ) 11 | 12 | // BenchmarkSprintf provides performance numbers for the 13 | // fmt.Sprintf function. 14 | func BenchmarkSprintf(b *testing.B) { 15 | number := 10 16 | 17 | b.ResetTimer() 18 | 19 | for i := 0; i < b.N; i++ { 20 | fmt.Sprintf("%d", number) 21 | } 22 | } 23 | 24 | // BenchmarkFormat provides performance numbers for the 25 | // strconv.FormatInt function. 26 | func BenchmarkFormat(b *testing.B) { 27 | number := int64(10) 28 | 29 | b.ResetTimer() 30 | 31 | for i := 0; i < b.N; i++ { 32 | strconv.FormatInt(number, 10) 33 | } 34 | } 35 | 36 | // BenchmarkItoa provides performance numbers for the 37 | // strconv.Itoa function. 38 | func BenchmarkItoa(b *testing.B) { 39 | number := 10 40 | 41 | b.ResetTimer() 42 | 43 | for i := 0; i < b.N; i++ { 44 | strconv.Itoa(number) 45 | } 46 | } 47 | --------------------------------------------------------------------------------