Clio is a simple DSL for creating web applications in Go programming language with minimal effort. It includes routes for URL-matching, simple template system with support of application-wide and custom layouts and templates for each action, and bunch of useful utilities.
Clio comes with command line interface, which makes developing of Clio-based applications much easier. This is a bench of tools. You can find all needed information about them typing
$> clio help
in your terminal.
To create new Clio-based application skeleton, you need just to type few words in terminal:
$> clio create APPLICATION_NAME
It will create whole application's tree with some configuration assumptions, which you can change anyway. Also this will create basic structure of app folder, which you can modify during your work on application.
As an example of complex route usage here you can see the whole stack of REST-routes for curtain purposes (say, controller):
func ControllerRoutes () {
Get ("/books", Books )
Get ("/books/:id", Book )
Post ("/books", BooksCreate)
Put ("/books/:id", BookUpdate )
Delete ("/books", BooksRemove)
Delete ("/books/:id", BookRemove )
}
You can see that Clio supports next HTTP methods: GET, POST, PUT, DELETE. Every route method takes two arguments - route pattern and function to call if route matches. You can place closure as second argument, for example:
Get ("/", func () string {
return "Here's index"
})
One and only requirement for functions or closures that are called when pattern match - they should return string.
As you noted, route pattern can be given in a form of wild card:
Get ("/books/:id", Books)
So it can match, for example URLs like /books/12
. This irregular part of pattern is accessible as content of key-value map, returned by Splat()
function.
Pattern can also hold few irregular parts:
Get ("/books/:id/download/:format", BooksDownload)
func BooksDownload () string {
return "Book with id " + Splat()["id"] + "should be downloaded as: " + Splat()["format"]
}
Somethimes, splats is not enough. Also you want to remember your young years and code in PHP-like style :) It's not a problem with Query()
. For example, you want to handle all stuff after question mark in this URI: http://site.com/articles?number=20&offset=100
. To get values for number
and offset
you can type just:
func Articles () string {
return Query()["number"] + " " + Query()["offset"]
}
Clio has application-wide layout system, although you can define custom layout for specific routes. You can give away content without any layouts at all. Let's take a look at few examples:
To define view for specific action, you need to call Render()
method with path to a template as a first argument:
func Index () string {
return Render("index") // this will render `app/views/index.template` file
}
To send some data to template, you need to use second [optional] argument for Render()
method:
data := Author { Name: "Ernest", Surname: "Hemingway" }
view := Render ("author", data)
To specify custom layout for specific route you need to use third [optional] argument for Render()
method:
view := Render ("books/index", data, Settings { Layout: "hemingway" })
Clio reuses standard Go text/template package, but also defines partial
method, that can be pretty usable right in .template
files. Let's imagine that you want to include some template right in other template. To do so you need just to call render
method and use partial's file name as first function's argument:
{{ partial "specific_header" }}
Clio provides simple helper to represent your data as JSON without any exceed actions. To output JSON you just need to use Json ()
function in your controller:
func JsonBook () string {
return Json (Book { "Robert Graves", "Good-Bye to All That" })
}
it will automatically convert your data to JSON and set appropriate headers.
You can easily write websocket server-side with Clio. In order to use it, add a new route with Ws
function:
Ws ("/books/updates")
That's all. Now you have channel to connect and to perform all this JavaScript magic. Also you can send messages to channel right from backend anytime you want using:
WsSend("/books/updates", "51th Shade of Gray is born")
You can create as many separated channels as you want and use this feature, for example for role-based updates.
For example, you want to give away specific data not as html, but as plain text. To do so you need just to call SetHeader ()
method. Take a look at this example:
func BookPlain () string {
SetHeader("Content-Type", "text/plain")
return "(...)"
}
To see how real Clio-based application works, please take a look at https://github.com/grsmv/clio-example.
- Testing with gocheck
- Documentation
- More sophisticated example application