From 5688b5dfb44a2b22b35b1d32819be5e9e17d643d Mon Sep 17 00:00:00 2001 From: Irshad Ajmal Ahmed Date: Mon, 15 Apr 2019 12:59:01 -0500 Subject: [PATCH 01/11] refactored exercises to conform to enova go standards --- exercise-003-web/exhibit-e/server.go | 6 +----- exercise-003-web/exhibit-e/server_test.go | 4 +++- exercise-004-cars/exhibit-a/server.go | 6 +----- exercise-005-sql/README.md | 2 +- exercise-005-sql/exhibit-a/db.go | 24 +++++++++++++---------- exercise-007-json/exhibit-d/app.go | 13 +++++------- 6 files changed, 25 insertions(+), 30 deletions(-) diff --git a/exercise-003-web/exhibit-e/server.go b/exercise-003-web/exhibit-e/server.go index 132d3ca..86e1d67 100644 --- a/exercise-003-web/exhibit-e/server.go +++ b/exercise-003-web/exhibit-e/server.go @@ -7,16 +7,12 @@ import ( var homeT *template.Template -func setup(dir string) { - homeT = template.Must(template.ParseFiles(dir + "/exhibit-e/home.html")) -} - func home(w http.ResponseWriter, r *http.Request) { homeT.Execute(w, nil) } func main() { - setup(".") + homeT = template.Must(template.ParseFiles("./exhibit-e/home.html")) http.HandleFunc("/home", home) http.ListenAndServe(":8080", nil) } diff --git a/exercise-003-web/exhibit-e/server_test.go b/exercise-003-web/exhibit-e/server_test.go index b53fd6a..79ca0fb 100644 --- a/exercise-003-web/exhibit-e/server_test.go +++ b/exercise-003-web/exhibit-e/server_test.go @@ -1,14 +1,16 @@ package main import ( + "html/template" "net/http" "net/http/httptest" "testing" + "github.com/stretchr/testify/assert" ) func init() { - setup("../") + homeT = template.Must(template.ParseFiles("../exhibit-e/home.html")) } func TestServer(t *testing.T) { diff --git a/exercise-004-cars/exhibit-a/server.go b/exercise-004-cars/exhibit-a/server.go index 3d002f9..a2c4a57 100644 --- a/exercise-004-cars/exhibit-a/server.go +++ b/exercise-004-cars/exhibit-a/server.go @@ -6,12 +6,8 @@ import ( "time" ) -func inOneYear() time.Time { - return time.Now().AddDate(1, 0, 0) -} - func poke(w http.ResponseWriter, r *http.Request) { - cookie := http.Cookie{Name: "username", Value: "gopher", Expires: inOneYear()} + cookie := http.Cookie{Name: "username", Value: "gopher", Expires: time.Now().AddDate(1, 0, 0)} http.SetCookie(w, &cookie) fmt.Fprintf(w, "Just set cookie named 'username' set to 'gopher'") } diff --git a/exercise-005-sql/README.md b/exercise-005-sql/README.md index 1ed673a..dacdfa2 100644 --- a/exercise-005-sql/README.md +++ b/exercise-005-sql/README.md @@ -5,7 +5,7 @@ Learn to work with SQL in Go! This exercise assumes the learner is familiar with basic SQL. If you are new to SQL then you must first learn the basics before proceeding. Saigo uses [PostgreSQL](http://www.tutorialspoint.com/postgresql/) for all database work. -Set up a local PostgreSQL server on your deveopment machine and create a database called `test`. Connect to the database +Set up a local PostgreSQL server on your development machine, and create a database called `test`. Connect to the database (`$ psql test`) and copy the code in `migration_up.sql` to create the `people` table in your `test` database: ``` diff --git a/exercise-005-sql/exhibit-a/db.go b/exercise-005-sql/exhibit-a/db.go index f9ee480..80a5335 100644 --- a/exercise-005-sql/exhibit-a/db.go +++ b/exercise-005-sql/exhibit-a/db.go @@ -7,31 +7,35 @@ import ( _ "github.com/lib/pq" ) -func PanicOn(err error) { +func main() { + db, err := sql.Open("postgres", "dbname=test sslmode=disable") if err != nil { panic(err) } -} - -func main() { - db, err := sql.Open("postgres", "dbname=test sslmode=disable") - PanicOn(err) _, err = db.Exec("INSERT INTO people(name, ssn) VALUES ($1, $2)", "Bruce Leroy", 111223333) - PanicOn(err) + if err != nil { + panic(err) + } _, err = db.Exec("INSERT INTO people(name, ssn) VALUES ($1, $2)", "Sho 'Nuff", 444556666) - PanicOn(err) + if err != nil { + panic(err) + } rows, err := db.Query("SELECT person_id, name, ssn FROM people") - PanicOn(err) + if err != nil { + panic(err) + } for rows.Next() { var id int var name string var ssn int err := rows.Scan(&id, &name, &ssn) - PanicOn(err) + if err != nil { + panic(err) + } fmt.Printf("Person %5d %-15s %9d\n", id, name, ssn) } diff --git a/exercise-007-json/exhibit-d/app.go b/exercise-007-json/exhibit-d/app.go index 2916b39..f8434a2 100644 --- a/exercise-007-json/exhibit-d/app.go +++ b/exercise-007-json/exhibit-d/app.go @@ -15,7 +15,11 @@ type Phone struct { var allPhones []Phone -func setup() { +func phones(w http.ResponseWriter, r *http.Request) { + json.NewEncoder(w).Encode(allPhones) +} + +func main() { data, err := ioutil.ReadFile("phones.json") if err != nil { fmt.Println("Error reading phones.json") @@ -26,14 +30,7 @@ func setup() { if err != nil { fmt.Println("Error in unmarshalling phones") } -} -func phones(w http.ResponseWriter, r *http.Request) { - json.NewEncoder(w).Encode(allPhones) -} - -func main() { - setup() http.HandleFunc("/phones", phones) http.ListenAndServe(":8080", nil) } From fa9cac2992a8e8a66ebc7271ac89fe09621767fe Mon Sep 17 00:00:00 2001 From: Kevin Kelley Date: Mon, 22 Apr 2019 16:42:11 -0500 Subject: [PATCH 02/11] Corrected a typo in exercise_006 README --- exercise-006-models/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercise-006-models/README.md b/exercise-006-models/README.md index 2498514..4c4c920 100644 --- a/exercise-006-models/README.md +++ b/exercise-006-models/README.md @@ -1,5 +1,5 @@ ## Description -A client has asked you to implement a Go package to handle the persistent storage and retrieval of _customers_ and there _orders_. +A client has asked you to implement a Go package to handle the persistent storage and retrieval of _customers_ and their _orders_. The client's back-end servers run PostgreSQL 9.x, and a database engineer has already written the required migrations. Your job is to complete the implementation. From 8da55946e3d0a92dda038b1560707a827f1ec96f Mon Sep 17 00:00:00 2001 From: Irshad Ajmal Ahmed Date: Tue, 7 May 2019 09:26:50 -0500 Subject: [PATCH 03/11] changed phones JSON field name to prevent DB conflict --- exercise-007-json/exhibit-d/phones.json | 40 ++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/exercise-007-json/exhibit-d/phones.json b/exercise-007-json/exhibit-d/phones.json index 6a993f5..4c06790 100755 --- a/exercise-007-json/exhibit-d/phones.json +++ b/exercise-007-json/exhibit-d/phones.json @@ -1,14 +1,14 @@ [ { "age": 0, - "id": "motorola-xoom-with-wi-fi", + "identifier": "motorola-xoom-with-wi-fi", "imageUrl": "img/phones/motorola-xoom-with-wi-fi.0.jpg", "name": "Motorola XOOM\u2122 with Wi-Fi", "snippet": "The Next, Next Generation\r\n\r\nExperience the future with Motorola XOOM with Wi-Fi, the world's first tablet powered by Android 3.0 (Honeycomb)." }, { "age": 1, - "id": "motorola-xoom", + "identifier": "motorola-xoom", "imageUrl": "img/phones/motorola-xoom.0.jpg", "name": "MOTOROLA XOOM\u2122", "snippet": "The Next, Next Generation\n\nExperience the future with MOTOROLA XOOM, the world's first tablet powered by Android 3.0 (Honeycomb)." @@ -16,14 +16,14 @@ { "age": 2, "carrier": "AT&T", - "id": "motorola-atrix-4g", + "identifier": "motorola-atrix-4g", "imageUrl": "img/phones/motorola-atrix-4g.0.jpg", "name": "MOTOROLA ATRIX\u2122 4G", "snippet": "MOTOROLA ATRIX 4G the world's most powerful smartphone." }, { "age": 3, - "id": "dell-streak-7", + "identifier": "dell-streak-7", "imageUrl": "img/phones/dell-streak-7.0.jpg", "name": "Dell Streak 7", "snippet": "Introducing Dell\u2122 Streak 7. Share photos, videos and movies together. It\u2019s small enough to carry around, big enough to gather around." @@ -31,7 +31,7 @@ { "age": 4, "carrier": "Cellular South", - "id": "samsung-gem", + "identifier": "samsung-gem", "imageUrl": "img/phones/samsung-gem.0.jpg", "name": "Samsung Gem\u2122", "snippet": "The Samsung Gem\u2122 brings you everything that you would expect and more from a touch display smart phone \u2013 more apps, more features and a more affordable price." @@ -39,7 +39,7 @@ { "age": 5, "carrier": "Dell", - "id": "dell-venue", + "identifier": "dell-venue", "imageUrl": "img/phones/dell-venue.0.jpg", "name": "Dell Venue", "snippet": "The Dell Venue; Your Personal Express Lane to Everything" @@ -47,7 +47,7 @@ { "age": 6, "carrier": "Best Buy", - "id": "nexus-s", + "identifier": "nexus-s", "imageUrl": "img/phones/nexus-s.0.jpg", "name": "Nexus S", "snippet": "Fast just got faster with Nexus S. A pure Google experience, Nexus S is the first phone to run Gingerbread (Android 2.3), the fastest version of Android yet." @@ -55,14 +55,14 @@ { "age": 7, "carrier": "Cellular South", - "id": "lg-axis", + "identifier": "lg-axis", "imageUrl": "img/phones/lg-axis.0.jpg", "name": "LG Axis", "snippet": "Android Powered, Google Maps Navigation, 5 Customizable Home Screens" }, { "age": 8, - "id": "samsung-galaxy-tab", + "identifier": "samsung-galaxy-tab", "imageUrl": "img/phones/samsung-galaxy-tab.0.jpg", "name": "Samsung Galaxy Tab\u2122", "snippet": "Feel Free to Tab\u2122. The Samsung Galaxy Tab\u2122 brings you an ultra-mobile entertainment experience through its 7\u201d display, high-power processor and Adobe\u00ae Flash\u00ae Player compatibility." @@ -70,7 +70,7 @@ { "age": 9, "carrier": "Cellular South", - "id": "samsung-showcase-a-galaxy-s-phone", + "identifier": "samsung-showcase-a-galaxy-s-phone", "imageUrl": "img/phones/samsung-showcase-a-galaxy-s-phone.0.jpg", "name": "Samsung Showcase\u2122 a Galaxy S\u2122 phone", "snippet": "The Samsung Showcase\u2122 delivers a cinema quality experience like you\u2019ve never seen before. Its innovative 4\u201d touch display technology provides rich picture brilliance, even outdoors" @@ -78,7 +78,7 @@ { "age": 10, "carrier": "Verizon", - "id": "droid-2-global-by-motorola", + "identifier": "droid-2-global-by-motorola", "imageUrl": "img/phones/droid-2-global-by-motorola.0.jpg", "name": "DROID\u2122 2 Global by Motorola", "snippet": "The first smartphone with a 1.2 GHz processor and global capabilities." @@ -86,7 +86,7 @@ { "age": 11, "carrier": "Verizon", - "id": "droid-pro-by-motorola", + "identifier": "droid-pro-by-motorola", "imageUrl": "img/phones/droid-pro-by-motorola.0.jpg", "name": "DROID\u2122 Pro by Motorola", "snippet": "The next generation of DOES." @@ -94,7 +94,7 @@ { "age": 12, "carrier": "AT&T", - "id": "motorola-bravo-with-motoblur", + "identifier": "motorola-bravo-with-motoblur", "imageUrl": "img/phones/motorola-bravo-with-motoblur.0.jpg", "name": "MOTOROLA BRAVO\u2122 with MOTOBLUR\u2122", "snippet": "An experience to cheer about." @@ -102,7 +102,7 @@ { "age": 13, "carrier": "T-Mobile", - "id": "motorola-defy-with-motoblur", + "identifier": "motorola-defy-with-motoblur", "imageUrl": "img/phones/motorola-defy-with-motoblur.0.jpg", "name": "Motorola DEFY\u2122 with MOTOBLUR\u2122", "snippet": "Are you ready for everything life throws your way?" @@ -110,7 +110,7 @@ { "age": 14, "carrier": "T-Mobile", - "id": "t-mobile-mytouch-4g", + "identifier": "t-mobile-mytouch-4g", "imageUrl": "img/phones/t-mobile-mytouch-4g.0.jpg", "name": "T-Mobile myTouch 4G", "snippet": "The T-Mobile myTouch 4G is a premium smartphone designed to deliver blazing fast 4G speeds so that you can video chat from practically anywhere, with or without Wi-Fi." @@ -118,7 +118,7 @@ { "age": 15, "carrier": "US Cellular", - "id": "samsung-mesmerize-a-galaxy-s-phone", + "identifier": "samsung-mesmerize-a-galaxy-s-phone", "imageUrl": "img/phones/samsung-mesmerize-a-galaxy-s-phone.0.jpg", "name": "Samsung Mesmerize\u2122 a Galaxy S\u2122 phone", "snippet": "The Samsung Mesmerize\u2122 delivers a cinema quality experience like you\u2019ve never seen before. Its innovative 4\u201d touch display technology provides rich picture brilliance,even outdoors" @@ -126,28 +126,28 @@ { "age": 16, "carrier": "Sprint", - "id": "sanyo-zio", + "identifier": "sanyo-zio", "imageUrl": "img/phones/sanyo-zio.0.jpg", "name": "SANYO ZIO", "snippet": "The Sanyo Zio by Kyocera is an Android smartphone with a combination of ultra-sleek styling, strong performance and unprecedented value." }, { "age": 17, - "id": "samsung-transform", + "identifier": "samsung-transform", "imageUrl": "img/phones/samsung-transform.0.jpg", "name": "Samsung Transform\u2122", "snippet": "The Samsung Transform\u2122 brings you a fun way to customize your Android powered touch screen phone to just the way you like it through your favorite themed \u201cSprint ID Service Pack\u201d." }, { "age": 18, - "id": "t-mobile-g2", + "identifier": "t-mobile-g2", "imageUrl": "img/phones/t-mobile-g2.0.jpg", "name": "T-Mobile G2", "snippet": "The T-Mobile G2 with Google is the first smartphone built for 4G speeds on T-Mobile's new network. Get the information you need, faster than you ever thought possible." }, { "age": 19, - "id": "motorola-charm-with-motoblur", + "identifier": "motorola-charm-with-motoblur", "imageUrl": "img/phones/motorola-charm-with-motoblur.0.jpg", "name": "Motorola CHARM\u2122 with MOTOBLUR\u2122", "snippet": "Motorola CHARM fits easily in your pocket or palm. Includes MOTOBLUR service." From bab605f6e43bf9ded910471b9ac7b285b7db4d36 Mon Sep 17 00:00:00 2001 From: Irshad Ajmal Ahmed Date: Wed, 3 Jul 2019 11:38:42 -0500 Subject: [PATCH 04/11] incorporated the bonus section of exercise 8 into the mandatory section --- exercise-008-iface/README.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/exercise-008-iface/README.md b/exercise-008-iface/README.md index 2198ba4..7088504 100644 --- a/exercise-008-iface/README.md +++ b/exercise-008-iface/README.md @@ -83,11 +83,8 @@ implements interfaces. What is it? ## Engineering Task -Expand on `exhibit-c` by adding another shape or two. - -## Bonus - -Add a shape-factory that can create a shape given a type and a list of lengths: +1. Expand on `exhibit-c` by adding another shape or two. +1. Add a shape-factory that can create a shape given a type and a list of lengths: ``` func Build(shape string, parameters ...float64) Shape From 97994be66f1c93e68abe6635aebfad8dce49bf9d Mon Sep 17 00:00:00 2001 From: Alexandre Amado Date: Thu, 4 Jul 2019 08:39:10 -0300 Subject: [PATCH 05/11] Resolved Go Lint issues --- exercise-006-models/src/models/customer.go | 2 +- exercise-006-models/src/models/order.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/exercise-006-models/src/models/customer.go b/exercise-006-models/src/models/customer.go index 6ce8de6..ed087dc 100644 --- a/exercise-006-models/src/models/customer.go +++ b/exercise-006-models/src/models/customer.go @@ -24,7 +24,7 @@ func (c *Customer) Refresh(db *sqlx.DB) error { } // NewCustomer ... -func NewCustomer(db *sqlx.DB, email string, first_name string, last_name string, birth_date time.Time) (*Customer, error) { +func NewCustomer(db *sqlx.DB, email string, firstName string, lastName string, birthDate time.Time) (*Customer, error) { return nil, nil } diff --git a/exercise-006-models/src/models/order.go b/exercise-006-models/src/models/order.go index c5053b0..4f97ff5 100644 --- a/exercise-006-models/src/models/order.go +++ b/exercise-006-models/src/models/order.go @@ -21,7 +21,7 @@ func NewOrder(db *sqlx.DB, customerID int, productID int, quantity int) error { return nil } -// Update Order ... +// UpdateOrder ... func UpdateOrder(db *sqlx.DB, o *Order) error { return nil } From 38b309de5bfaa30c4ace672a5d985ee06ec29605 Mon Sep 17 00:00:00 2001 From: Alexandre Amado Date: Thu, 4 Jul 2019 08:39:24 -0300 Subject: [PATCH 06/11] Added missing model for startup implementation --- exercise-006-models/src/models/product.go | 31 +++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 exercise-006-models/src/models/product.go diff --git a/exercise-006-models/src/models/product.go b/exercise-006-models/src/models/product.go new file mode 100644 index 0000000..f438595 --- /dev/null +++ b/exercise-006-models/src/models/product.go @@ -0,0 +1,31 @@ +package models + +import ( + "github.com/jmoiron/sqlx" +) + +// Product ... +type Product struct { + ID int + Product string +} + +// NewProduct ... +func NewProduct(db *sqlx.DB, productName string) error { + return nil +} + +// UpdateProduct ... +func UpdateProduct(db *sqlx.DB, o *Product) error { + return nil +} + +// DeleteProduct ... +func DeleteProduct(db *sqlx.DB, productID int) error { + return nil +} + +// FindProduct ... +func FindProduct(db *sqlx.DB, productName string) (*Product, error) { + return nil, nil +} From 615f3575520d3f03db097d15089528bd95212f8d Mon Sep 17 00:00:00 2001 From: Irshad Ajmal Ahmed Date: Tue, 6 Aug 2019 13:33:00 -0500 Subject: [PATCH 07/11] removed sqlx package in favor of using golang's built-in sql package --- exercise-006-models/README.md | 29 +++++++++------------- exercise-006-models/src/models/customer.go | 17 ++++++------- exercise-006-models/src/models/order.go | 9 +++---- exercise-006-models/src/models/product.go | 10 ++++---- 4 files changed, 29 insertions(+), 36 deletions(-) diff --git a/exercise-006-models/README.md b/exercise-006-models/README.md index 4c4c920..72b1844 100644 --- a/exercise-006-models/README.md +++ b/exercise-006-models/README.md @@ -37,24 +37,24 @@ type Order struct { Along with these structures the client has added a collection of stubbed functions: ```go -func (c *Customer) Refresh(db *sqlx.DB) error +func (c *Customer) Refresh(db *sql.DB) error -func NewCustomer(db *sqlx.DB, email, first_name, last_name string, birth_date time.Time) (*Customer, error) -func DeleteCustomer(db *sqlx.DB, id int) error -func UpdateCustomer(db *sqlx.DB, u *Customer) error -func FindCustomerByEmail(db *sqlx.DB, email string) (*Customer, error) -func FindCustomerByID(db *sqlx.DB, id int) (*Customer, error) -func AllCustomers(db *sqlx.DB) ([]*Customer, error) +func NewCustomer(db *sql.DB, email, first_name, last_name string, birth_date time.Time) (*Customer, error) +func DeleteCustomer(db *sql.DB, id int) error +func UpdateCustomer(db *sql.DB, u *Customer) error +func FindCustomerByEmail(db *sql.DB, email string) (*Customer, error) +func FindCustomerByID(db *sql.DB, id int) (*Customer, error) +func AllCustomers(db *sql.DB) ([]*Customer, error) -func FindProduct(db *sqlx.DB, product string) (*Product, error) +func FindProduct(db *sql.DB, product string) (*Product, error) -func NewOrder(db *sqlx.DB, custID int, product string, quantity int) error -func UpdateOrder(db *sqlx.DB, o *Order) error -func DeleteOrder(db *sqlx.DB, orderID int) error +func NewOrder(db *sql.DB, custID int, product string, quantity int) error +func UpdateOrder(db *sql.DB, o *Order) error +func DeleteOrder(db *sql.DB, orderID int) error ``` Each of these functions can hit the database hence they all accept a DB-connection as their first argument -(`db *sqlx.DB`). The function names should be sufficiently descriptive except possibly `Refresh()`. The `Refresh()` +(`db *sql.DB`). The function names should be sufficiently descriptive except possibly `Refresh()`. The `Refresh()` method should update the values of the receiving `Customer` instance with the latest values from the database. As mentioned early on in the course, try to schedule a session with an instructor to go over the requirements of this exercise. You don't want to waste time implementing the wrong functionality. @@ -67,8 +67,3 @@ You must: - `src/models/customer_test.go` - `src/models/product_test.go` - `src/models/order_test.go` - -## Do I have to use `sqlx`? -No. If you prefer not to use the [sqlx](http://github.com/jmoiron/sqlx) package and would instead prefer to work with -the standard `database/sql` package, that is completely fine. Please feel free to replace all instances of `sqlx` in the -code with `sql` and code away! diff --git a/exercise-006-models/src/models/customer.go b/exercise-006-models/src/models/customer.go index ed087dc..50916ec 100644 --- a/exercise-006-models/src/models/customer.go +++ b/exercise-006-models/src/models/customer.go @@ -1,9 +1,8 @@ package models import ( + "database/sql" "time" - - "github.com/jmoiron/sqlx" ) // Customer ... @@ -19,36 +18,36 @@ type Customer struct { } // Refresh ... -func (c *Customer) Refresh(db *sqlx.DB) error { +func (c *Customer) Refresh(db *sql.DB) error { return nil } // NewCustomer ... -func NewCustomer(db *sqlx.DB, email string, firstName string, lastName string, birthDate time.Time) (*Customer, error) { +func NewCustomer(db *sql.DB, email string, firstName string, lastName string, birthDate time.Time) (*Customer, error) { return nil, nil } // DeleteCustomer ... -func DeleteCustomer(db *sqlx.DB, id int) error { +func DeleteCustomer(db *sql.DB, id int) error { return nil } // UpdateCustomer ... -func UpdateCustomer(db *sqlx.DB, u *Customer) error { +func UpdateCustomer(db *sql.DB, u *Customer) error { return nil } // FindCustomerByEmail ... -func FindCustomerByEmail(db *sqlx.DB, email string) (*Customer, error) { +func FindCustomerByEmail(db *sql.DB, email string) (*Customer, error) { return nil, nil } // FindCustomerByID ... -func FindCustomerByID(db *sqlx.DB, id int) (*Customer, error) { +func FindCustomerByID(db *sql.DB, id int) (*Customer, error) { return nil, nil } // AllCustomers ... -func AllCustomers(db *sqlx.DB) ([]*Customer, error) { +func AllCustomers(db *sql.DB) ([]*Customer, error) { return nil, nil } diff --git a/exercise-006-models/src/models/order.go b/exercise-006-models/src/models/order.go index 4f97ff5..af21a9f 100644 --- a/exercise-006-models/src/models/order.go +++ b/exercise-006-models/src/models/order.go @@ -1,9 +1,8 @@ package models import ( + "database/sql" "time" - - "github.com/jmoiron/sqlx" ) // Order ... @@ -17,16 +16,16 @@ type Order struct { } // NewOrder ... -func NewOrder(db *sqlx.DB, customerID int, productID int, quantity int) error { +func NewOrder(db *sql.DB, customerID int, productID int, quantity int) error { return nil } // UpdateOrder ... -func UpdateOrder(db *sqlx.DB, o *Order) error { +func UpdateOrder(db *sql.DB, o *Order) error { return nil } // DeleteOrder ... -func DeleteOrder(db *sqlx.DB, orderID int) error { +func DeleteOrder(db *sql.DB, orderID int) error { return nil } diff --git a/exercise-006-models/src/models/product.go b/exercise-006-models/src/models/product.go index f438595..5963ab0 100644 --- a/exercise-006-models/src/models/product.go +++ b/exercise-006-models/src/models/product.go @@ -1,7 +1,7 @@ package models import ( - "github.com/jmoiron/sqlx" + "database/sql" ) // Product ... @@ -11,21 +11,21 @@ type Product struct { } // NewProduct ... -func NewProduct(db *sqlx.DB, productName string) error { +func NewProduct(db *sql.DB, productName string) error { return nil } // UpdateProduct ... -func UpdateProduct(db *sqlx.DB, o *Product) error { +func UpdateProduct(db *sql.DB, o *Product) error { return nil } // DeleteProduct ... -func DeleteProduct(db *sqlx.DB, productID int) error { +func DeleteProduct(db *sql.DB, productID int) error { return nil } // FindProduct ... -func FindProduct(db *sqlx.DB, productName string) (*Product, error) { +func FindProduct(db *sql.DB, productName string) (*Product, error) { return nil, nil } From 5b664e2100f8fbf8688bd9d39fa7eac16052cf07 Mon Sep 17 00:00:00 2001 From: Irshad Ajmal Ahmed Date: Fri, 4 Oct 2019 11:07:03 -0500 Subject: [PATCH 08/11] removed pointers to Player struct --- exercise-009-rock/src/rock/game.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercise-009-rock/src/rock/game.go b/exercise-009-rock/src/rock/game.go index d6a4e87..3981347 100644 --- a/exercise-009-rock/src/rock/game.go +++ b/exercise-009-rock/src/rock/game.go @@ -15,12 +15,12 @@ const ( // Game ... type Game struct { - players []*Player + players []Player points []int } // Add adds a player to the game -func (g *Game) Add(p *Player) { +func (g *Game) Add(p Player) { g.players = append(g.players, p) g.points = append(g.points, 0) } From 0dee877a0c441f69ef0708645a88b000a33e313b Mon Sep 17 00:00:00 2001 From: Stephen Delaney Date: Mon, 14 Oct 2019 12:09:49 -0500 Subject: [PATCH 09/11] update path for golint --- exercise-002-tools/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercise-002-tools/README.md b/exercise-002-tools/README.md index 2278dfe..216a163 100644 --- a/exercise-002-tools/README.md +++ b/exercise-002-tools/README.md @@ -23,7 +23,7 @@ Golint does not emit errors or warnings, but “suggestions”: These suggestion Install and run golint over your current directory: ```bash -$ go get github.com/golang/lint/golint +$ go get -u golang.org/x/lint/golint $ golint ./... ``` From ac2538e687b799cd6aee1ab5c4dd41dce30bada8 Mon Sep 17 00:00:00 2001 From: Irshad Ajmal Ahmed Date: Mon, 9 Dec 2019 13:09:40 -0600 Subject: [PATCH 10/11] updated instructions --- README.md | 14 +++++++------- exercise-007-json/README.md | 4 +--- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 68e6390..efe4e5b 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ # Saigo -A series of (hopefully cool!) exercises for those eager to learn Go +A series of (hopefully cool!) exercises for those eager to learn Go. ## Setting Up Your Go Environment -As new versions of the Go suite are released you will want an +As new versions of the Go suite are released, you will want an easy way to stay up to date. So please follow the [Setup](setup-environment.md) guide to install Go and build your workspace. -It is best to get this right the first time around so if you have -trouble please ask for help! +It is best to get this right the first time around; so if you have +trouble, please ask for help! ## Exercises @@ -18,7 +18,7 @@ The Saigo exercises are intended to be a tool for the instructor. Experienced de to use them as a way to jump right in the pool. However, to get the most out of them it is recommended that learners find an instructor. -Some of the exercises may require serveral days to complete. Learners should consider building solutions incrementally and meeting with their instructor between iterations. +Some of the exercises may require serveral days to complete. Learners should consider building solutions incrementally and meeting with their instructor between iterations. The [first](https://github.com/enova/saigo/tree/master/exercise-000-prep) exercise asks learners to go through Caleb Doxsey's book [An Introduction to Programming in Go](https://www.golang-book.com/books/intro). Learners should schedule regular @@ -40,11 +40,11 @@ an instructor. ### Engineering Tasks Engineering tasks will ask you to write some code, usually an application of some sort. -As mentioned above, learners should routinely schedule brief (ten-minute) meetings with instructors +As mentioned above, when unsure about their approach, learners may schedule brief meetings with instructors while working on engineering-tasks. You will want to avoid situations where you write 150 lines of code only to find your solution has issues. Even learning can be agile. -Be ready to demo your application when it is completed. Instructors want to see it in action! +Once your task is complete, create a PR so one of the saigo bucketeers can take a look. If changes are requested, address them and push up a new commit onto the same PR. ## Recommended Resources diff --git a/exercise-007-json/README.md b/exercise-007-json/README.md index 50573ac..d045fec 100644 --- a/exercise-007-json/README.md +++ b/exercise-007-json/README.md @@ -111,6 +111,4 @@ func main() { Only one attribute (`Name`) is being unmarshalled. Your job is to expand the `Phone` structure so that the unmarshalling will read the other attributes from the file and store them in the `Phone` slice. -### Optional - -Create a database table for phones. When the server is started, if the table is empty, seed the table with the phones from the json file. The /phones endpoint should then return whatever records are found in the phones table. +Next, you must create a database table for phones. When the server is started, if the table is empty, seed the table with the phones from the json file. The /phones endpoint should then return whatever records are found in the phones table. From c4b408d1da49c0ac6070aece2b60728f85e7069e Mon Sep 17 00:00:00 2001 From: Luke McKechnie Date: Fri, 17 Jan 2020 11:33:23 -0600 Subject: [PATCH 11/11] Functionality complete. Tests Complete. --- exercise-001-corpus/build.sh | 8 + exercise-001-corpus/corpus/corpus.go | 79 +++++++ exercise-001-corpus/corpus/corpus_test.go | 274 ++++++++++++++++++++++ exercise-001-corpus/corpus/pairedlist.go | 71 ++++++ exercise-001-corpus/corpus/test1.txt | 1 + exercise-001-corpus/word_count.go | 19 ++ 6 files changed, 452 insertions(+) create mode 100755 exercise-001-corpus/build.sh create mode 100644 exercise-001-corpus/corpus/corpus.go create mode 100644 exercise-001-corpus/corpus/corpus_test.go create mode 100644 exercise-001-corpus/corpus/pairedlist.go create mode 100644 exercise-001-corpus/corpus/test1.txt create mode 100644 exercise-001-corpus/word_count.go diff --git a/exercise-001-corpus/build.sh b/exercise-001-corpus/build.sh new file mode 100755 index 0000000..abf56b6 --- /dev/null +++ b/exercise-001-corpus/build.sh @@ -0,0 +1,8 @@ +gofmt -w . +golint ./... +go build word_count.go +pushd corpus +go test +go test -bench=. +popd +./word_count -file 7oldsamr.txt diff --git a/exercise-001-corpus/corpus/corpus.go b/exercise-001-corpus/corpus/corpus.go new file mode 100644 index 0000000..0c4eb3f --- /dev/null +++ b/exercise-001-corpus/corpus/corpus.go @@ -0,0 +1,79 @@ +package corpus + +import ( + "errors" + "io/ioutil" + "log" + "regexp" + "sort" + "strings" +) + +func WordCountFromFileSorted(file string, reverse bool) (PairedList, error) { + contents, err := StringFromFile(file) + if err != nil { + return nil, err + } + words := WordCountMapFromString(contents) + plist := SortedMapPairedList(words, reverse) + return plist, nil +} + +func SortedMapPairedList(unsorted map[string]int, reverse bool) PairedList { + if len(unsorted) == 0 { + return nil + } + sortable := mapToPairedList(unsorted) + if reverse { + sort.Sort(sort.Reverse(sortable)) + } else { + sort.Sort(sortable) + } + return sortable +} + +func mapToPairedList(unsorted map[string]int) PairedList { + sortable := make(PairedList, len(unsorted)) + i := 0 + for k, v := range unsorted { + sortable[i] = Pair{k, v} + i++ + } + return sortable +} + +func WordCountFromFile(file string) (PairedList, error) { + contents, err := StringFromFile(file) + if err != nil { + return nil, err + } + if len(contents) > 0 { + words := WordCountMapFromString(contents) + wordsPL := mapToPairedList(words) + return wordsPL, nil + } else { + return nil, errors.New("Empty File") + } +} + +func StringFromFile(file string) (string, error) { + contents, err := ioutil.ReadFile(file) + if err != nil { + return "", err + } + return string(contents), nil +} + +func WordCountMapFromString(str string) map[string]int { + words := strings.Fields(str) + reg, err := regexp.Compile("[^a-zA-Z]+") + if err != nil { + log.Fatal(err) + } + wordMap := make(map[string]int) + for _, word := range words { + prettyWord := strings.ToLower(reg.ReplaceAllString(word, "")) + wordMap[prettyWord] += 1 + } + return wordMap +} diff --git a/exercise-001-corpus/corpus/corpus_test.go b/exercise-001-corpus/corpus/corpus_test.go new file mode 100644 index 0000000..0e0857d --- /dev/null +++ b/exercise-001-corpus/corpus/corpus_test.go @@ -0,0 +1,274 @@ +package corpus + +import ( + "testing" +) + +type testpair struct { + value string + result PairedList +} + +var wordCountFromFileTests = []testpair{ + {"test1.txt", PairedList{Pair{Key: "test", Value: 3}}}, +} + +func TestWordCountFromFile(t *testing.T) { + for _, pair := range wordCountFromFileTests { + v, _ := WordCountFromFile(pair.value) + if !v.Equals(pair.result) { + t.Error( + "For", pair.value, + "expected", pair.result, + "got", v) + } + } +} + +type stringTestPair struct { + value string + result string +} + +var stringFromFileTests = []stringTestPair{ + {"test1.txt", "test test test"}, +} + +func TestStringFromFile(t *testing.T) { + for _, pair := range stringFromFileTests { + v, _ := StringFromFile(pair.value) + if v != pair.result { + t.Error("For", pair.value, + "expected", pair.result, + "got", v) + } + } +} + +type mapStringTestPair struct { + result map[string]int + value string +} + +var wordCountMapFromStringTests = []mapStringTestPair{ + {map[string]int{"test": 3}, "test test test"}, +} + +func mapContainsAll(a, b map[string]int) bool { + for k, v := range a { + if val, ok := b[k]; ok { + if val != v { + return false + } + } else { + return false + } + } + return true +} + +func TestWordCountMapFromString(t *testing.T) { + for _, pair := range wordCountMapFromStringTests { + v := WordCountMapFromString((pair.value)) + if !mapContainsAll(pair.result, v) { + t.Error("For", pair.value, + "expected", pair.result, + "got", v) + } + } +} + +type mapToPairedListPair struct { + value map[string]int + result PairedList +} + +var sortedMapPairedListTests = []mapToPairedListPair{ + {map[string]int{"test": 3, "sample": 2}, + PairedList{Pair{"test", 3}, Pair{"sample", 2}}}, +} + +func TestSortedMapPairedList(t *testing.T) { + for _, pair := range sortedMapPairedListTests { + v := SortedMapPairedList(pair.value, true) + if !v.Equals(pair.result) { + t.Error("For", pair.value, + "expected", pair.result, + "got", v) + } + } +} + +var wordCountFromFileSortedTests = []testpair{ + {"test1.txt", + PairedList{ + Pair{ + Key: "test", + Value: 3, + }}}, +} + +func TestWordCountFromFileSorted(t *testing.T) { + for _, pair := range wordCountFromFileSortedTests { + v, _ := WordCountFromFileSorted(pair.value, true) + if !v.Equals(pair.result) { + t.Error("For", pair.value, + "expected", pair.result, + "got", v) + } + } +} + +func BenchmarkWordCountFromFileSorted20(b *testing.B) { + for n := 0; n < b.N; n++ { + for _, pair := range wordCountFromFileSortedTests { + WordCountFromFileSorted(pair.value, true) + } + } +} + +type pairedListContainsPair struct { + value PairedList + contents Pair + result bool +} + +var pairedListContainsTests = []pairedListContainsPair{ + {PairedList{Pair{"test", 3}}, + Pair{"test", 3}, + true}, +} + +func TestPairedList_Contains(t *testing.T) { + for _, pair := range pairedListContainsTests { + v := pair.value.Contains(pair.contents) + if v != pair.result { + t.Error("For", pair.value, + "for contents", pair.contents, + "expected", pair.result, + "got", v) + } + } +} + +type pairedListCountPairs struct { + value PairedList + counted Pair + result int +} + +var pairedListCountTests = []pairedListCountPairs{ + {PairedList{Pair{ + Key: "test", + Value: 3, + }, + Pair{ + Key: "test", + Value: 3, + }}, + Pair{ + Key: "test", + Value: 3, + }, + 2}, +} + +func TestPairedList_Count(t *testing.T) { + for _, pair := range pairedListCountTests { + v := pair.value.Count(pair.counted) + if v != pair.result { + t.Error("For", pair.value, + "counted", pair.counted, + "expected", pair.result, + "got", v) + } + } +} + +type pairedListEqualsPair struct { + lista PairedList + listb PairedList + result bool +} + +var pairedListEqualsPairs = []pairedListEqualsPair{ + {PairedList{Pair{ + Key: "test", + Value: 25, + }}, + PairedList{Pair{ + Key: "test", + Value: 25, + }}, + true}, +} + +func TestPairedList_Equals(t *testing.T) { + for _, pair := range pairedListEqualsPairs { + v := pair.lista.Equals(pair.listb) + if v != pair.result { + t.Error("For", pair.lista, + "and", pair.listb, + "expected", pair.result, + "got", v) + } + } +} + +type pairedListLenPair struct { + list PairedList + length int +} + +var pairedListLenTests = []pairedListLenPair{ + {PairedList{Pair{ + Key: "test", + Value: 3, + }, Pair{ + Key: "sample", + Value: 1, + }, Pair{ + Key: "example", + Value: 45, + }}, 3}, +} + +func TestPairedList_Len(t *testing.T) { + for _, pair := range pairedListLenTests { + v := pair.list.Len() + if v != pair.length { + t.Error("For", pair.list, + "expected", pair.length, + "got", v) + } + } +} + +type pairedListLessPair struct { + list PairedList + indexa int + indexb int + result bool +} + +var pairedListLessTests = []pairedListLessPair{ + {PairedList{Pair{ + Key: "test", + Value: 3, + }, Pair{Key: "sample", Value: 2}}, + 0, + 1, + false}, +} + +func TestPairedList_Less(t *testing.T) { + for _, pair := range pairedListLessTests { + v := pair.list.Less(pair.indexa, pair.indexb) + if v != pair.result { + t.Error("For", pair.list, + "index", pair.indexa, "and", pair.indexb, + "expected", pair.result, + "got", v) + } + } +} diff --git a/exercise-001-corpus/corpus/pairedlist.go b/exercise-001-corpus/corpus/pairedlist.go new file mode 100644 index 0000000..05b9d94 --- /dev/null +++ b/exercise-001-corpus/corpus/pairedlist.go @@ -0,0 +1,71 @@ +package corpus + +import ( + "fmt" + "strings" +) + +// Pair is a struct for PairedList, relating String Key to Int Value +type Pair struct { + Key string + Value int +} + +// PairedList is an ordered list of String, Int pairs +type PairedList []Pair + +// ContainsAll checks that all elements exist in both PairedLists. Does not check for duplicates +func (p PairedList) ContainsAll(q PairedList) bool { + for _, pair := range p { + if !q.Contains(pair) { + return false + } + } + return true +} + +// Equals checks that the pairs are the same and in the same order +func (p PairedList) Equals(q PairedList) bool { + if len(p) != len(q) { + return false + } + for i, pair := range p { + if q[i].Value != pair.Value && q[i].Key != pair.Key { + return false + } + } + return true +} + +// Count gets the count of matching pairs +func (p PairedList) Count(pair Pair) int { + count := 0 + for _, pval := range p { + if pair.Key == pval.Key && pair.Value == pval.Value { + count++ + } + } + return count +} + +// Contains checks if the supplied pair exists in the list +func (p PairedList) Contains(pair Pair) bool { + for _, p := range p { + if p.Key == pair.Key && p.Value == pair.Value { + return true + } + } + return false +} + +func (p PairedList) String() string { + var sb strings.Builder + for _, pair := range p { + sb.WriteString(fmt.Sprintf("%s %d", pair.Key, pair.Value)) + } + return sb.String() +} + +func (p PairedList) Len() int { return len(p) } +func (p PairedList) Less(i, j int) bool { return p[i].Value < p[j].Value } +func (p PairedList) Swap(i, j int) { p[i], p[j] = p[j], p[i] } diff --git a/exercise-001-corpus/corpus/test1.txt b/exercise-001-corpus/corpus/test1.txt new file mode 100644 index 0000000..4968768 --- /dev/null +++ b/exercise-001-corpus/corpus/test1.txt @@ -0,0 +1 @@ +test test test \ No newline at end of file diff --git a/exercise-001-corpus/word_count.go b/exercise-001-corpus/word_count.go new file mode 100644 index 0000000..dfcb03b --- /dev/null +++ b/exercise-001-corpus/word_count.go @@ -0,0 +1,19 @@ +package main + +import ( + "flag" + "fmt" + "saigo/exercise-001-corpus/corpus" +) + +func main() { + file := flag.String("file", "", "the file to parse") + flag.Parse() + + wordlist, err := corpus.WordCountFromFileSorted(*file, true) + if err == nil { + for _, pair := range wordlist { + fmt.Printf("%s %d\n", pair.Key, pair.Value) + } + } +}