Thursday, February 4, 2016

ftmpl: a fast and typesafe golang (html) templating library

I always wondered why so many templating "languages" expect you learn a completely new language. For example, jstl, django templating, and golang templating. I know what a mess PHP can become if you mix too much PHP code, html and javascript code. But I, also, think that most developers today know that this is a no-no.

Another thing I dislike about templating libraries is that so many of them are type unsafe. Even when the original language is typesafe.

For example, in Golang:
type Inventory struct {
 Material string
 Count    uint
}
sweaters := Inventory{"wool", 17}
tmpl, err := template.New("test").Parse("{{.Count}} items are made of {{.Material}}")
if err != nil { panic(err) }
err = tmpl.Execute(os.Stdout, sweaters)
if err != nil { panic(err) }
But what if you call:
err = tmpl.Execute(os.Stdout, "jkljkl")
The template expects an argument that has Material and Count fields. But I executed it with a string! This will fail, but -- not in compile time! Keep in mind that in most cases templates are created in advance (not in runtime). This error should be caught in compile time, if you ask me. Especially if you (like me) moved to Golang from a scripting language -- because of its type safety.

Fast forward to my ftmpl... Ftmpl is a fast, typesafe templating for Golang I created for my last side project (more soon). Ftmpl generates Golang code from .tmpl files. Every template is "converted" to a Golang function, then you just call that function. If you declare your template with an argument Inventory then you can't call it with a string. The error will be caught on compile-time not in runtime!

See the README for more niceties (like extending templates, autoescaping, formatting output, ...).

My experience with golang

I rewrote a big part of Trackprofiler, my online GPS track editor. The web application is still mostly Python, the main difference is that all the GPS tracks manipulations (GPX files) are done in a microservice written in Golang. The main reason is that Python proved to be too slow and memory hungry for big GPX files.

I'll list a few things I learned in the process of rewriting it to Golang:

First, I want to emphasise that learning Golang has a lot less "WTF moments" than learning other languges.

My project has cca 9000 lines of code. I think that equivalent Python code is shorter, but if you write unit tests (you should), a lot of the Python ones will test things which in Golang aren't needed (because because they are caught by the compiler!).

The golang code tend to have more (but shorter) lines because in Golang you don't write things like:
something.DoSomething().DoSomethingElse().ThirdOperation()
Any of the .DoSomething(), DoSomethingElse(), ... calls can "throw" an exception. In many languages you just expect that somebody else will catch those exceptions. In Golang there is no "throw an exception". If there is an error you need to handle it immediately. The result is that you write every operation in a new line, and not cram them all in one.

But that's not necessarily bad, you have more lines, but they are all simple operations and easier to read and understand.

I thought that a big problem would be no DOM-based XML parser, but I love the builtin JSON and XML APIs!

Versioning dependencies sucks.

There are a lot of libraries*, but most are not mature enough. This will probably change with time.

And, yeah, it has no generics.

*If you are interested, here are my own open source libraries and utilities:

typescriptify golang structs

I'm a big fan of Go(lang).

I know, I know, Go don't have generics. I miss them, too. But on the other side -- it's simple, it's fast, vim go is great. Writing code is straightforward, it's readable. Golang is more like a better C (with a more consistent API and a garbage collection) with a feeling of a scripting language. It is not a "better Java".

Another language I (started to) like is Typescript. Not because I especially like the language or its syntax. It's because it is:
  • typesafe,
  • compiled to Javascript and the resulting code is similar (almost line-to-line) to the original Typescript code (making debugging very easy).
In my latest side project I decided to go the Go+Typescript way. There was only one thing missing. It is a web application with a lot of Javascript (i.e. Typescript code), but the data used in Typescript was not typesafe. On the server, the data is represented with Golang structs, but Typescript receives only their JSON representation. 

I solved the problem by writing a library which converts Golang structs to Typescript classes: github.com/tkrajina/typescriptify-golang-structs

It's simple, you just:
tscriptify -package=package/with/your/models -target=target_ts_file.ts Model1 Model2
....and then include target_ts_file.ts in your TypeScript scripts.

Make this step part of your bild process and... Now, if you change a field name in Golang -- the javascript/TypeScript compile will fail!

Wednesday, January 6, 2016

Golang GPX library

My golang port of gpxpy is finished, it's called (guess, guess...) gpxgo.

The first version of the port was done by Peter Vasil and I decided to continue from there. But my main aim was to implement a complete GPX 1.0 and GPX 1.1 specification. But, since his library was based on an older version of gpxpy, it was difficult to "reconcile" the different object models --so, I decided to change the library API. And this is how a new library is born.

Usage example:

import (
    ...
    "github.com/tkrajina/gpxgo/gpx"
    ...
)

gpxBytes := ...
gpxFile, err := gpx.ParseBytes(gpxBytes)
if err != nil {
    ...
}

// Analyize/manipulate your track data here...
for _, track := range gpxFile.Tracks {
    for _, segment := range track.Segments {
        for _, point := range segment.Points {
            fmt.Print(point)
        }
    }
}

// (Check the API for GPX manipulation and analyzing utility methods)

// When ready, you can write the resulting GPX file:
xmlBytes, err := gpxFile.ToXml(gpx.ToXmlParams{Version: "1.1", Indent: true})
...
Like gpxpy, gpxgo includes a command line utility to get some basic stats from .gpx files:

$ gpxinfo Mojstrovka.gpx
File: /Users/puzz/golang/src/github.com/tkrajina/gpxgo/test_files/Mojstrovka.gpx
GPX name:
GPX desctiption:
GPX version: 1.0
Author:
Email:


Global stats:
 Points: 184
 Length 2D: 2.6958067369682577
 Length 3D: 3.00439590990862
 Bounds: 46.430350, 46.435641, 13.738842, 13.748333
 Moving time: 0
 Stopped time: 0
 Max speed: 0.000000m/s = 0.000000km/h
 Total uphill: 446.4893280000001
 Total downhill: 417.65524800000026
 Started: 1901-12-13 20:45:52 +0000 UTC
 Ended: 1901-12-13 20:45:52 +0000 UTC


Track #1:
     Points: 184
     Length 2D: 2.6958067369682577
     Length 3D: 3.00439590990862
     Bounds: 46.430350, 46.435641, 13.738842, 13.748333
     Moving time: 0
     Stopped time: 0
     Max speed: 0.000000m/s = 0.000000km/h
     Total uphill: 446.4893280000001
...etc...

Sunday, January 3, 2016

Cartesius: Simple python coordinate system graphing library

Cartesius is a simple python library for writing coordinate system images. The main reason why I started it was that existing libraries depended on C/C++ modules which are not available on google appengine python SDK. Cartesius has only one dependency -- PIL.

Example usage:

import cartesius.main as cartesius
import cartesius.elements as elements
import cartesius.charts as charts

coordinate_system = cartesius.CoordinateSystem()

# list or tuple of two-element tuples (value, label):
piechart_data = (
    charts.data('abc', 1),
    charts.data('cde', 2),
    charts.data('efg', 4),
    charts.data('ijk', 1),
    charts.data('lmn', 5),
    charts.data('opq', 5),
    charts.data('xyz', 3),
)
piechart = charts.PieChart(data=piechart_data, color=(0, 0, 0))
coordinate_system.add(piechart)

# No need for axes:
coordinate_system.add(elements.Axis(horizontal=True, hide=True))
coordinate_system.add(elements.Axis(vertical=True, hide=True))
The result is:
More examples in the README.

Sunday, October 4, 2015

gpxpy 1.0

It's long overdue, but the 1.0 version of my python library for parsing/manipulation of GPX files (gpxpy) is out. It contains too many changes to list them here, but it can be summarised as: gpxpy now supports both GPX 1.0 and 1.1. formats.

The 0.* version started as a utility scripts for me, it supported only some fields GPX 1.0 and I was adding more as users requested. But with time, as GPX 1.1 started to be used more, it became difficult to follow what's exactly missing in the library. People requested featured from different versions of GPX but the library nominally supported only 1.0.

After a couple of months of complete refactoring of the code to parse/serialize GPX file the new version is out. There is only one thing missing -- support for GPX 1.1. extensions. This will (probably) come later.

Thursday, July 9, 2015

git plus

Just released: Git plus is my utility "plugin" for git. It consists of:
  • git multi a utility to execute a git command on multiple git repository
  • git relation to show the relation between two branches/commits
  • git old-branches to detect old/unused branches
  • git recent to list recently used or last-used branches
If you are using OSX and brew, it's available there, too.