Using CODL to Generate Cookoo Routes

Sep 17 2014

Cookoo is a tool for rapidly build Go applications. A central part of any Cookoo app is its registry of routes. A route maps a particular request to a series of tasks that Cookoo will perform in response.

Cookoo routes are written in code and compiled into the application. While I love this feature of Cookoo, I do sometimes find the syntax to be a little clumsy. For example, a Cookoo route definition might look something like this:

reg.Route("default", "Default route").
    Does(fmt.Template, "out").
            Using("template").WithDefault("Hello World").
    Does(fmt.Println, "print").
            Using("content").From("cxt:out")

As routes get longer, this syntax feels tedious. I wanted something that felt, to be perfectly honest, more like SQL. I wanted something that did for Cookoo routes what SASS or LESS do for CSS. And so I wrote codl.

CODL is a simple domain-specific language (DSL) for defining Cookoo routes. A complete CODL file looks like this (app.codl):

IMPORT
    github.com/Masterminds/cookoo/fmt

ROUTE default "Default route"
  DOES fmt.Template out
    USING template "Hello World"
  DOES fmt.Println print
    USING content FROM cxt:out

This declares exactly the same route as the previous code example. But its syntax is simpler.

Like SASS or LESS, codl comes with a command-line tool that can transform a codl file into Go source. You can call it as codl build -d DIRECTORY, which will transform all codl files in DIRECTORY, or you can tell codl to continue to run and watch a directory for changes, compiling any time it finds any change.

$ codl watch -d routes/
[INFO] Watching routes for changes to .codl files.
[INFO] routes/app.codl has changed. Updating. ("routes/app.codl": CREATE)
[INFO] Translated routes/app.codl to routes/app.go

When codl transforms a codl file, the generated result looks something like this:

package routes

// This file is auto-generated by Codl.

import (
    "github.com/Masterminds/cookoo"
    "github.com/Masterminds/cookoo/fmt"
)

func AppRoutes(reg *cookoo.Registry) {
    reg.Route("default", "Default route").
    Does(fmt.Template, "out").
            Using("template").WithDefault("Hello World").
    Does(fmt.Println, "print").
            Using("content").From("cxt:out")
}

In my main app, I can include all of these generated routes by doing something like this:

func main() {
  reg, router, cxt := cookoo.Cookoo()

  routes.AppRoutes(reg)

  cli.New(reg, router, cxt).Run("default")
}

The routes.ApPRoute() function is automatically generated by codl based on the file name (app.codl).

Codl files are a nicety for small apps, but when writing large applications in Cookoo, having an easy to read (and easy to search) file format for your routes can make a huge diference in terms of maintainability.