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