This is a port of the previous post to golang. Golang is the closest I’ve gotten to writing production-code in a statically typed C-like language. Its nice, though I still can’t get over reading The Go Programming Language and seeing them refer to C and Go as high-level languages. Having started out in Lisp, it just feels weird.
Anyway, this is code that took about an afternoon to write. Since this is a statically-typed language, I fired up my RubyMine with the Go plugin and its great. I started writing the code using regular slices and maps, and then started switching to custom types since that is what the XML-encoder works with. It was so easy firing up new structs and using them instead of the pre-existing data-structures. I enjoyed doing the refactoring very much.
RubyMine’s (IntelliJ’s? JetBrains’?) plugin for Go is great to use. The debugger doesn’t seem to be working, which was a bit of a pain. When programming in Ruby its almost second nature for me to add a breakpoint and run amok in the runtime. I essentially instantiate my very own REPL in the correct context of the code. When doing Clojure, I always have a REPL open. It was a shift to debug only with PrintLns, but its a legitimate enough method. And with the compiler telling me what I’m doing wrong, I had to use the debugging statement less than I would have with Clojure or Ruby. Good stuff.
The code came down to 85 lines. 85 lines of code to write a file-format conversion isn’t bad at all. And this is something that would compile to a single executable file that can be handed over to anyone. Very nice.
Notes
- RubyMine and Go Plugin: Since this is a statically-typed language, it made sense to try the Go plugin. I’m very happy with it. It made refactorings so much easier, and this is making me a big fan of static-typing.
- Since the executable starts and runs so quickly, it made more sense for me to write the results to stdout instead of to a file, just as one would with any well-behaved UNIX utility.
package main import ( "os" "encoding/csv" "log" "strings" "encoding/xml" "time" ) type OrderLine struct { XMLName xml.Name `xml:"ORDER_LINE"` OrderId string `xml:"-"` LineId string `xml:"ID,attr"` Sku string `xml:"SKU,attr"` Quantity string `xml:"QUANTITY,attr"` Status string `xml:"STATUS,attr"` TrackingId string `xml:"TRACKING_ID,attr"` DispatchedDate string `xml:"DISPATCHED_DATE,attr"` } type Order struct { ID string `xml:"ID,attr"` OrderLines []OrderLine `xml:"ORDER"` } type DispatchDocket struct { XMLName xml.Name `xml:"DISPATCH_DOCKET` Orders []Order `xml:"ORDER"` } func recordToOrderLine(record string) OrderLine { split := strings.Split(record, ";") return OrderLine{ OrderId: split[0], LineId: split[1], Sku: split[2], Quantity: split[3], Status: split[4], DispatchedDate: time.Now().Format(time.UnixDate), TrackingId: split[5]} } func recordsToOrders(records [][]string) (out []Order) { final := make(map[string][]OrderLine) for _, v := range records { order := recordToOrderLine(v[0]) order_id := order.OrderId if order.Status == "shipped" { final[order_id] = append(final[order_id], order) } } for k, order_lines:= range final { out = append(out, Order{ID: k, OrderLines: order_lines}) } return } func main() { f, _ := os.Open("/home/ravi/myprogs/GETTINGSTARTEDTOS_DEMO_DATA/SampleDataFiles/Chapter3/order_status.csv") defer f.Close() r := csv.NewReader(f) records, err := r.ReadAll() if err != nil { log.Fatal(err) } if len(records) < 2 { log.Fatalf("We need more than 1 line of data.", err) } orders := DispatchDocket{Orders: recordsToOrders(records[1:])} output, err := xml.MarshalIndent(&orders, "", " ") if err != nil { log.Fatal(err) } os.Stdout.Write(output) }
Leave a Reply