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.
-
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
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>