Skip to content

tenfensw/toolatra

Repository files navigation

logo

Toolatra

The simplicity of Sinatra brought to Tcl.

package require Toolatra

get / {
	show "Good morning!"
}

run

Toolatra is a micro web framework that is very similar to Sinatra, but is ported to Tcl.

What does it have?

  • Sinatra-like syntax

  • A module that provides a fully-featured template engine

  • Built-in web server that integrates easily with Nginx or Apache

  • A module for generating and validating authorization tokens

Installation

On macOS

$ tclsh8.5 install-macos.tcl

On Ubuntu Linux

$ sudo sh
$ mkdir -v /usr/share/tcltk/toolatra
$ cp -r -v *.tcl /usr/share/tcltk/toolatra/
$ exit

Usage

Handling a GET request to a specific path:

package require Toolatra

get / {
	show {Hello there, stranger!}
}

run

If you save and run this file with tclsh and then go to http://127.0.0.1:5050, you should see Hello there, stranger!.

SPOILER! Specifying the port number on which the server should be ran to the run command will start Toolatra’s server on that port.

Throwing HTTP errors:

package require Toolatra

get /this-will-cause-an-error {
	error 404
}

run

By default, Toolatra’s ugly error handler will be used. To replace it with a custom one, just define a GET request handler with the path set to /<HTML error code here>. Example:

package require Toolatra

get /404 {
	show "Whoops, an error has occured."
}

get /this-will-cause-an-error {
	error 404
}

run

Serving additional headers:

package require Toolatra

get / {
	header Content-type text/plain
	show {Look, I'm plain text!}
}

run

Using templates:

package require Toolatra
package require ToolatraTemplates

get / {
	etcl index.html [dict create name Tim]
}

run

Example contents of index.html (it must be located in templates folder):

<h1>Hello there, @name@!</h1>
<p>Did you know that I can run Tcl code from here? Just look: 2 + 2 = @expr {2+2}@</p>

Speaking of templates, you don’t have to use Toolatra’s template engine - you can use Mustache templates if you want in a pretty similar manner (you’ll need ianka’s mustache.tcl library installed first, though);

package require Toolatra 19.12
package require ToolatraMustache 20.06 ;# needed for Mustache templates to work

get / {
	mustache greeter.html [dict create name Tim]
}

Example contents of greeter.html.mustache located inside the templates folder:

<h1>Hello again, {{name}}!</h1>

Serving dynamically-generated binary data:

package require Toolatra 19.12

get / {
	set binDtDesc [open a.out r]
	fconfigure $binDtDesc -translation binary -encoding binary
	set ctnt [read $binDtDesc]
	close $binDtDesc
	bshow $ctnt application/octet-stream ;# or brender
}

Accessing query string parameters:

package require Toolatra
package require ToolatraTemplates

get / {
	if {[dict exists $params name]} {
		show "Hello, [dict get $params name]!"
	} else {
		etcl form.html
	}
}

run

form.html template:

<form method=GET action=/>
<p>Your name: <input type="text" name=name /></p> <button type=submit>Greet me!</button>
</form>

This Tcl wiki page contains some useful examples on using templates and layouts: https://wiki.tcl-lang.org/page/Toolatra

Accessing header values:

package require Toolatra

get / {
	if {[dict exists $params User-Agent]} {
		show [dict get $params User-Agent]
	} else {
		show None
	}
}

run

Redirecting to other pages:

package require Toolatra

get / {
	redirect http://example.com
}


run

Handling POST requests with data:

package require Toolatra

post / {
	render "Data sent: $rawData"
}

get / {
	render "Params/headers sent: $params"
}

run

Handling cookies:

package require Toolatra 19.12

get / {
	if {[cookie token] != {}} {
		show "Cookie 'token' is set to [cookie token]"
	} else {
		redirect /settoken
	}
}

get /settoken {
	cookie token [expr {int(rand() * 9999)}]
}

Authorization example:

set toolatra_auth ",(!%" ;# this is a 4-digit string that will be used to later encode the tokens that ToolatraAuth produces

package require Toolatra 19.12
package require ToolatraTemplates 19.11
package require ToolatraAuth 19.12

get / {
	set cv [cookie authToken]
	if {! [tokenValid $cv]} {
		redirect /login
	} else {
		redirect /greet
	}
}

get /login {
	if {! [dict exists $params nm]} {
		etcl form.html
	} else {
		set name [dict get $params nm]
		set tkn [token $name] ;# the generated token will expire in 1 day, to specify the expiration date, specify the number of seconds as the second argument
		cookie authToken $tkn
		redirect /greet
	}
}

get /greet {
	set tkn [cookie authToken]
	if {! [tokenValid $tkn]} {
		redirect /login
	} else {
		set name [tokenValue $tkn]
		show "Greetings, $name!"
	}
}

run

where form.html is:

<form>
 <p>To continue, please enter your name.</p>
 <p>Name: <input type=text name=nm /></p>
 <button type=submit>Next</button>
</form>

License

As always, MIT License.