BEER2RST - my first attempt with golang
I'm using Beersmith [1] a (non-free...) software to create my beer recipes. It's written in Java and runs well on Linux. One of the biggest benefits with using Beersmith is that my brewing house [2] is taking exported recipes as input and setup mash schemes automatically - really comfortable.
I brew beer several times a month and always takes notes of each brewing, both methods and results. The goal is to get reproducible results each time or to make small improvements. Instead of having all these notes in separate locations, it would be nice if I instead collected all of my brewing as blog posts.
With that said, this blog will probably evolve to contain non-technical posts as well in the near future.
In these posts, I also want to publish my recipes somehow. Beersmith is able to export recipes in a non complicated XML format and is quite straight forward to parse. All posts that I'm writing is in reStructuredText [3] format, so I need to create a tool that read the XML and export the recipes in reStructuredText format.
First glance at Golang
To be honest, I'm not really a fan of high-level programming languages as I can't take them seriously. But I guess it's time to learn something new. What I have tested with Go so far is rather impressive. For example, I cannot imagine a simpler XML parsing (I'm used to libxml2).
I also like the gofmt [4] tool to make the code properly formatted. Every language should have such a tool. It's also easy so cross compile the application to different architectures by specify $GOOS and $GOARCH. I have only tested with ARM and it just works.
Imports
Most languages has a way to tell what functionality it should import and be usable to your file. Go is using import and has the following syntax
import ( "encoding/xml" "flag" "fmt" "io/ioutil" "os" "strings" )
What makes it really interesting is when you do this
import ( "github.com/marcusfolkesson/tablewriter" )
I.e. point to a repository. You only need to download the repository to your $GOPATH location and then it's useable
go get github.com/marcusfolkesson/tablewriter
- Notes:
- I need to create tables for print the recipes properly. I found tablewriter [5] that is printing ASCII-tables. Unfortunately, it does not create tables in reStructuredText format so I had to fork the project and implement support for that. Hopefully the changes will make it back to the original project. There is a pending pull request for that.
Unmarshal XML
I really liked how easy it was to unmarshal XML. Consider the following snip from the exported recipe
<HOP> <NAME>Northern Brewer</NAME> <VERSION>1</VERSION> <ORIGIN>Germany</ORIGIN> <ALPHA>8.5000000</ALPHA> <AMOUNT>0.0038459</AMOUNT> <USE>Boil</USE> <TIME>60.0000000</TIME> <NOTES>Also called Hallertauer Northern Brewers Used for: Bittering and finishing both ales and lagers of all kinds Aroma: Fine, dry, clean bittering hop. Unique flavor. Substitutes: Hallertauer Mittelfrueh, Hallertauer Examples: Anchor Steam, Old Peculiar, </NOTES> <TYPE>Both</TYPE> <FORM>Pellet</FORM> <BETA>4.0000000</BETA> <HSI>35.0000000</HSI> <DISPLAY_AMOUNT>3.85 g</DISPLAY_AMOUNT> <INVENTORY>0.00 g</INVENTORY> <DISPLAY_TIME>60.0 min</DISPLAY_TIME> </HOP>
The first step is to create a structure that should hold the values
type Hop struct { Name string `xml:"NAME"` Origin string `xml:"ORIGIN"` Alpha float64 `xml:"ALPHA"` Amount float64 `xml:"AMOUNT"` Use string `xml:"USE"` Time float64 `xml:"TIME"` Notes string `xml:"NOTES"` }
There is no need to create variables for all tags, just the ones you are interesting in.
Later on, read the file and unmarshal the XML
content, err := ioutil.ReadFile("beer.xml") if err != nil { panic(err) } err = xml.Unmarshal(content, &hops) if err != nil { panic(err) }
The structure will now be populated with values from the XML file. Magical, isn't it?
Conclusion
I have only used tested Golang for a working day approximately, and I like it. I used to use Python for all kind of fast prototyping, but I think I will consider Golang the next time.
What I really like in comparison with Python are:
- The fact that it compiles to a single binary is really nice, especially when you cross compile to a different architecture.
- No need for interpreter
- Static types! Dynamic type languages makes my brain hurt
The result can be found on my GitHub [6] account.